Jack Jiang

我的最新工程MobileIMSDK:http://git.oschina.net/jackjiang/MobileIMSDK
posts - 467, comments - 13, trackbacks - 0, articles - 0

关于MobileIMSDK

MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,支持iOS、Android、H5、标准Java平台,服务端基于Netty编写。

工程开源地址是:

关于RainbowChat

► 详细产品介绍:http://www.52im.net/thread-19-1-1.html
► 版本更新记录:http://www.52im.net/thread-1217-1-1.html
► 全部运行截图:Android端iOS端
► 在线体验下载:专业版(TCP协议)专业版(UDP协议)      (关于 iOS 端,请:点此查看

 

RainbowChat是一套基于开源IM聊天框架 MobileIMSDK 的产品级移动端IM系统。RainbowChat源于真实运营的产品,解决了大量的屏幕适配、细节优化、机器兼容问题(可自行下载体验:专业版下载安装)。

* RainbowChat可能是市面上提供im即时通讯聊天源码的,唯一一款同时支持TCP、UDP两种通信协议的IM产品(通信层基于开源IM聊天框架  MobileIMSDK 实现)。

v8.3 版更新内容

此版更新内容更多历史更新日志):

(1)Android端主要更新内容bug修复及优化!】:

  • 1)[bug] 当首页“消息”列表所有的item都是置顶时,取消其中任一个置顶,都会错误地将其排在列表首位而不是列表末尾;
  • 2)[bug] 解决了从首页“消息”列表中遗留的陌生人聊天信息无法重置消息未读数的问题;
  • 3)[bug] 解决了聊天界面中底部面板和输入法软键盘切换时ui发生弹跳的问题;
  • 4)[优化] 重构了APP包名、应用名,防止被某些手机误报成恶意软件。
  • 5)[优化] 重构了搜索功能相关的代码,使之更易理解和维护;
  • 6)[优化] 优化了APP中各种文本输入框UI效果,以及其它UI细节;
  • 7)[优化] 解决了自定义长按菜单在某些机型上item文字会换行的问题;
  • 8)[优化] 大文件发送时,选择的图片、视频文件可以自动以图片消息和短视频消息的形式发送;
  • 9)[优化] 优化了APP处于后台时,收到实时语音/视频请求的通知形式(用高优先级的系统Notification方式提醒用户)。 

(2)服务端主要更新内容:

  • 1)[bug] 解决了uid登陆时的sql注入风险;
  • 2)[优化] 升级MobileIMSDK至v6.2正式版

此版主要功能运行截图更多截图点此查看): 

posted @ 2022-12-07 15:17 Jack Jiang 阅读(98) | 评论 (0)编辑 收藏

     摘要: 本文由腾讯PCG后台开发工程师的SG4YK分享,进行了修订和和少量改动。1、引言近日学习了 Protobuf 的编码实现技术原理,借此机会,正好总结一下并整理成文。接上篇《由浅入深,从根上理解Protobuf的编解码原理》,本篇将从Base64再到Base128编码,带你一起从底层来理解Protobuf的数据编码原理。本文结构总体与 Protobuf 官方文档相似,不少内容也来自官方文档,并在官方...  阅读全文

posted @ 2022-12-02 12:33 Jack Jiang 阅读(128) | 评论 (0)编辑 收藏

     摘要: 本文由码农的荒岛求生陆小风分享,为了提升阅读体验,进行了较多修订和排版。1、引言搞即时通讯IM方面开发的程序员,在谈到通讯层实现时,必然会提到网络编程。那么计算机网络编程中的一个非常基本的问题:到底该怎样组织Client与server之间交互的数据呢?本篇文章我们不讨论IM系统中的那些高端技术话题,我们回归到通讯的本质——也就是数据在网络中交互时的编解码原理,并由浅入深从底...  阅读全文

posted @ 2022-11-24 11:43 Jack Jiang 阅读(133) | 评论 (0)编辑 收藏

本文由vivo技术团队Li Guanyun分享,为了提升阅读体验,行了较多修订和重新排版。

1、引言

Protobuf 作为一种跨平台、语言无关、可扩展的序列化结构数据通讯协议,已广泛应用于网络数据交换的场景中(比如IM通信、分布式RPC调用等)。

随着互联网的发展,分布式系统的异构性会愈发突出,跨语言的需求会愈加明显,同时 gRPC 也大有取代Restful之势,而 Protobuf 作为gRPC 跨语言、高性能的法宝,我们技术人有必要深入理解 Protobuf 原理,为以后的技术更新和选型打下基础。

借此机会,我将个人的Protobuf学习过程以及实践经验,总结成文,与大家一起探讨学习。本篇主要从Protobuf的基础概念开始,包括技术背景、技术原理、使用方法和优缺点。

PS:本篇本跟上篇《Protobuf从入门到精通,一篇就够!》类似,都适合作为Protobuf的入门文章,但本篇力求简洁,尽量不涉及Protobuf的具体技术细节,目的是降低阅读的门槛、提升阅读效果,希望对你有用。

学习交流:

(本文已同步发布于:http://www.52im.net/thread-4081-1-1.html

2、系列文章

本文是系列文章中的第 2 篇,本系列总目录如下:

  • IM通讯协议专题学习(一):Protobuf从入门到精通,一篇就够!
  • IM通讯协议专题学习(二):快速理解Protobuf的背景、原理、使用、优缺点》(* 本文
  • 《IM通讯协议专题学习(三):由浅入深,从通信编解码原理上理解Protobuf》(稍后发布..)
  • 《IM通讯协议专题学习(四):从Base64到Protobuf,详解Protobuf的数据编码原理》(稍后发布..)
  • 《IM通讯协议专题学习(五):Protobuf到底比JSON快几倍?请看全方位实测!》(稍后发布..)
  • 《IM通讯协议专题学习(六):手把手教你如何在Android上从零使用Protobuf》(稍后发布..)
  • 《IM通讯协议专题学习(七):手把手教你如何在NodeJS中从零使用Protobuf》(稍后发布..)
  • 《IM通讯协议专题学习(八):金蝶随手记团队的Protobuf应用实践(原理篇)  》(稍后发布..)
  • 《IM通讯协议专题学习(九):金蝶随手记团队的Protobuf应用实践(实战篇) 》(稍后发布..)

3、什么是Protobuf?

Protobuf(全称是Protocol Buffers)是一种跨平台、语言无关、可扩展的序列化结构数据的方法,可用于网络通信数据交换及存储。

在序列化结构化数据的机制中,Protobuf是灵活、高效、自动化的,相对常见的XML、JSON,描述同样的信息,Protobuf序列化后数据量更小、序列化/反序列化速度更快、更简单。

一旦定义了要处理的数据的数据结构之后,就可以利用Protobuf的代码生成工具生成相关的代码。只需使用 Protobuf 对数据结构进行一次描述,即可利用各种不同语言(proto3支持C++, Java, Python, Go, Ruby, Objective-C, C#)或从各种不同流中对你的结构化数据轻松读写。

PS:类似的介绍,在上篇《Protobuf从入门到精通,一篇就够!》中也有涉及,有兴趣可以一并阅读之。

4、为什么是 Protobuf?

4.1 技术背景

大家可能会觉得 Google 发明 Protobuf 是为了解决序列化速度的,其实真实的原因并不是这样的。

Protobuf最先开始是 Google用来解决索引服务器 request/response 协议的。

在没有Protobuf之前,Google 已经存在了一种 request/response 格式,用于手动处理 request/response 的编解码。

这种sstk式也能支持多版本协议,不过代码不够优雅:

if(protocolVersion=1) {

    doSomething();

} elseif(protocolVersion=2) {

    doOtherThing();

} ...

如果是非常明确的格式化协议,会使新协议变得非常复杂。因为开发人员必须确保请求发起者与处理请求的实际服务器之间的所有服务器都能理解新协议,然后才能切换开关以开始使用新协议。

这也就是每个服务器开发人员都遇到过的低版本兼容、新旧协议兼容相关的问题。

为了解决这些问题,于是Protobuf就诞生了。

4.2 Protobuf 诞生了

Protobuf 最初被寄予以下 2 个期望:

  • 1)更容易引入新的字段,并且不需要检查数据的中间服务器可以简单地解析并传递数据(而无需了解所有字段);
  • 2)数据格式更加具有自我描述性,可以用各种语言来处理(比如C++, Java 等各种语言)。

但这个版本的 Protobuf 仍需要自己手写解析的代码。

随着Protobuf的发展、演进,它具有了更多的特性:

  • 1)自动生成的序列化和反序列化代码(避免了手动解析的需要。官方提供自动生成代码工具,各个语言平台的基本都有);
  • 2)除了用于数据交换之外,Protobuf也被用作某些持久化数据的便捷自描述格式。

Protocol Buffers 命名的由来:

Why the name "Protocol Buffers"?

The name originates from the early days of the format, before we had the protocol buffer compiler to generate classes for us. At the time, there was a class called ProtocolBuffer which actually acted as a buffer for an individual method. Users would add tag/value pairs to this buffer individually by calling methods like AddValue(tag, value). The raw bytes were stored in a buffer which could then be written out once the message had been constructed.

Since that time, the "buffers" part of the name has lost its meaning, but it is still the name we use. Today, people usually use the term "protocol message" to refer to a message in an abstract sense, "protocol buffer" to refer to a serialized copy of a message, and "protocol message object" to refer to an in-memory object representing the parsed message.

4.3 Protobuf 在谷歌业务中的地位

Protobuf 现在是 Google 用于数据交换和存储的通用语言。

谷歌代码树中定义了 48162 种不同的消息类型,包括 12183 个 .proto 文件。它们既用于 RPC 系统,也用于在各种存储系统中持久存储数据。

Protobuf 诞生之初是为了解决服务器端新旧协议(高低版本)兼容性问题,名字也很体贴——“协议缓冲区”,只不过后期慢慢发展成用于传输数据。

5、Protobuf 协议的工作原理

如下图所示:可以看到,对于序列化协议来说,使用方只需要关注业务对象本身,即 idl 定义,序列化和反序列化的代码只需要通过工具生成即可。

6、Protobuf 协议的消息定义

Protobuf 的消息是在idl文件(.proto)中描述的。

下面是本次样例中使用到的消息描述符 customer.proto

syntax = "proto3";

 

package domain;

 

option java_package = "com.Protobuf.generated.domain";

option java_outer_classname = "CustomerProtos";

 

message Customers {

    repeated Customer customer = 1;

}

 

message Customer {

    int32 id= 1;

    string firstName = 2;

    string lastName = 3;

 

    enum EmailType {

        PRIVATE = 0;

        PROFESSIONAL = 1;

    }

 

    message EmailAddress {

        string email = 1;

        EmailType type= 2;

    }

 

    repeated EmailAddress email = 5;

}

上面的消息比较简单,Customers包含多个Customer(Customer包含一个id字段、一个firstName字段、一个lastName字段以及一个email的集合)。

除了上述定义外,文件顶部还有三行可帮助代码生成器的申明:

  • 1)syntax = "proto3":用于idl语法版本,目前有两个版本proto2和proto3,两个版本语法不兼容,如果不指定,默认语法是proto2(由于proto3比proto2支持的语言更多,语法更简洁,本文使用的是proto3);
  • 2)package domain:此配置用于嵌套生成的类/对象;
  • 3)option java_package:生成器还使用此配置来嵌套生成的源(此处的区别在于这仅适用于Java,在使用Java创建代码和使用JavaScript创建代码时,使用了两种配置来使生成器的行为有所不同。也就是说,Java类是在包com.Protobuf.generated.domain下创建的,而JavaScript对象是在包domain下创建的)。

Protobuf 提供了更多选项和数据类型,本文不做详细介绍,感兴趣可以参考官方文档

7、Protobuf 的代码生成

首先安装 Protobuf 编译器 protoc(点这里有详细的安装教程)。

安装完成后,可以使用以下命令生成 Java 源代码:

1protoc --java_out=./src/main/java./src/main/idl/customer.proto

上述命令的意图是:从项目的根路径执行该命令,并添加了两个参数 java_out(即定义 ./src/main/java/ 为Java代码的输出目录;而 ./src/main/idl/customer.proto 是.proto文件所在目录)。

生成的代码非常复杂,但幸运的是它的用法却非常简单:

CustomerProtos.Customer.EmailAddress email = CustomerProtos.Customer.EmailAddress.newBuilder()

        .setType(CustomerProtos.Customer.EmailType.PROFESSIONAL)

        .setEmail("crichardson@email.com").build();

 

CustomerProtos.Customer customer = CustomerProtos.Customer.newBuilder()

        .setId(1)

        .setFirstName("Lee")

        .setLastName("Richardson")

        .addEmail(email)

        .build();

// 序列化

byte[] binaryInfo = customer.toByteArray();

System.out.println(bytes_String16(binaryInfo));

System.out.println(customer.toByteArray().length);

// 反序列化

CustomerProtos.Customer anotherCustomer = CustomerProtos.Customer.parseFrom(binaryInfo);

System.out.println(anotherCustomer.toString());

8、Protobuf 的性能数据

我们简单地以上述Customers为模型,分别构造、选取小对象、普通对象、大对象进行性能对比。

序列化耗时以及序列化后数据大小对比:

反序列化耗时:

更多性能数据可以参考官方的测试Benchmark

9、Protobuf 的优点

9.1效率高

从序列化后的数据体积角度,与XML、JSON这类文本协议相比,Protobuf通过 T-(L)-V(TAG-LENGTH-VALUE)方式编码,不需要", {, }, :等分隔符来结构化信息。同时在编码层面使用varint压缩。

所以描述同样的信息,Protobuf序列化后的体积要小很多,在网络中传输消耗的网络流量更少,进而对于网络资源紧张、性能要求非常高的场景。比如在移动网络下的IM即时通讯应用中,Protobuf协议就是非常不错的选择(PS:这也是我为什么着手分享Protobuf系列文章的原因啦)。

我们来简单做个对比。

要描述如下JSON数据:

1{"id":1,"firstName":"Chris","lastName":"Richardson","email":[{"type":"PROFESSIONAL","email":"crichardson@email.com"}]}

使用JSON序列化后的数据大小为118byte:

7b226964223a312c2266697273744e616d65223a224368726973222c226c6173744e616d65223a2252696368617264736f6e222c22656d61696c223a5b7b2274797065223a2250524f46455353494f4e414c222c22656d61696c223a226372696368617264736f6e40656d61696c2e636f6d227d5d7d

而使用Protobuf序列化后的数据大小为48byte:

0801120543687269731a0a52696368617264736f6e2a190a156372696368617264736f6e40656d61696c2e636f6d1001

从序列化/反序列化速度角度,与XML、JSON相比,Protobuf序列化/反序列化的速度更快,比XML要快20-100倍。

9.2支持跨平台、多语言

Protobuf是平台无关的,无论是Android、iOS、PC,还是C#与Java,都可以利用Protobuf进行无障碍通讯。

proto3支持C++、Java、Python、Go、Ruby、Objective-C、C#(详见《Protobuf从入门到精通,一篇就够》)。

9.3扩展性、兼容性好

Protobuf具有向后兼容的特性:更新数据结构以后,老版本依旧可以兼容,这也是Protobuf诞生之初被寄予解决的问题,因为编译器对不识别的新增字段会跳过不处理。

9.4使用简单

Protobuf 提供了一套编译工具,可以自动生成序列化、反序列化的样板代码,这样开发者只要关注业务数据idl,简化了编码解码工作以及多语言交互的复杂度。

10、Protobuf 的缺点

Protobuf的优点很突出,但缺点也很明显。

Protobuf的缺点主要是:

  • 1)不具备自描述能力:跟XML、JSON相比,这两者是自描述的,而ProtoBuf则不是;
  • 2)数据可读性非常差:ProtoBuf是二进制协议,如果没有idl文件,就无法理解二进制数据流,对调试非常不友好。

不过:Charles已经支持Protobuf协议,导入数据的描述文件即可,详情可参考 Charles Protocol Buffers

然而:由于没有idl文件无法解析二进制数据流,ProtoBuf在一定程度上可以保护数据,提升核心数据被破解的门槛,降低核心数据被盗爬的风险(也算是缺点变优点的典型范例)。

11、参考资料

[1] Protobuf官方网站

[2] Protobuf从入门到精通,一篇就够!

[3] 如何选择即时通讯应用的数据传输格式

[4] 强列建议将Protobuf作为你的即时通讯应用数据传输格式

[5] APP与后台通信数据格式的演进:从文本协议到二进制协议

[6] 面试必考,史上最通俗大小端字节序详解

[7] 移动端IM开发需要面对的技术问题(含通信协议选择)

[8] 简述移动端IM开发的那些坑:架构设计、通信协议和客户端

[9] 理论联系实际:一套典型的IM通信协议设计详解

[10] 58到家实时消息系统的协议设计等技术实践分享

(本文已同步发布于:http://www.52im.net/thread-4081-1-1.html

posted @ 2022-11-17 10:52 Jack Jiang 阅读(89) | 评论 (0)编辑 收藏

为了更好地分类阅读52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第5 期。

* 评语:本系列文章尽量使用最浅显易懂的文字、图片来组织内容,力求通信技术零基础的人群也能看懂。但个人建议,至少稍微了解过网络通信方面的知识后再看,会更有收获。特别推荐即时通讯开发者来阅读,因为针对移动弱网的问题,确实可以找到很多有价值的答案。


[- 1 -] IM开发者的零基础通信技术入门(一):通信交换技术的百年发展史(上)

[链接]http://www.52im.net/thread-2354-1-1.html

[摘要] 本文(上下两篇)将带你了解当今通信交换技术最初的模样以及发展过程。学习技术更要了解技术的前世今生,技术本无聊,故事很有趣。


[- 2 -]IM开发者的零基础通信技术入门(二):通信交换技术的百年发展史(下)

[链接]http://www.52im.net/thread-2356-1-1.html

[摘要] 接上篇,本篇里我们需要暂停一下,回过头来看看我们国家的交换机发展情况。


[- 3 -] IM开发者的零基础通信技术入门(三):国人通信方式的百年变迁

[链接] http://www.52im.net/thread-2360-1-1.html

[摘要] 本文通过大量珍贵历史图片,从中国第一条电报线路,到如今触手可及的5G网络,回顾过去、展望未来,一起来看国人通信方式的百年历史变迁。


[- 4 -]IM开发者的零基础通信技术入门(四):手机的演进,史上最全移动终端发展史

[链接] http://www.52im.net/thread-2369-1-1.html

[摘要] 本文将通过大量历史图片,讲述手机这种移动终端的演化过程,为您呈现如今已深度融入人类生活的智能手机本来的样子。了解过去,才能更好地展望未来。


[- 5 -] IM开发者的零基础通信技术入门(五):1G到5G,30年移动通信技术演进史

[链接] http://www.52im.net/thread-2373-1-1.html

[摘要] 今天的5G,3.5GHz+大规模MIMO+波束赋形,还有固定无线应用,不禁让人看到了当年3G时代WiMax的影子,但WiMax为何输给了LTE,难道命运也喜欢对技术开玩笑吗?一部跨越三十年惊心动魄的移动通信史,为你揭晓答案。


[- 6 -] IM开发者的零基础通信技术入门(六):移动终端的接头人——“基站”技术

[链接] http://www.52im.net/thread-2375-1-1.html

[摘要]自上个世纪70年代末移动通信网络诞生以来,移动通信基站已经陪伴人类40年了,为人类社会带来了空前的变革,但你知道它的故事吗?


[- 7 -] IM开发者的零基础通信技术入门(七):移动终端的千里马——“电磁波”

[链接] http://www.52im.net/thread-2382-1-1.html

[摘要] 本文将回归到无线通信的技术之魂——“电磁波”,尽量用通俗易懂的文字讲述这个稍显枯燥的通信技术基础知识。


[- 8 -] IM开发者的零基础通信技术入门(八):零基础,史上最强“天线”原理扫盲

[链接] http://www.52im.net/thread-2385-1-1.html

[摘要] 实际生活中,无线通信中的天线都长什么样?有哪些用途?更重要的是,天线的技术原理是怎样的?本文将通过大量的图片,为你讲述这些内容。本文力求通俗易懂,面向零基础读者,希望继续给即时通讯网的开发者带来更多通信技术方面的收获。


[- 9 -] IM开发者的零基础通信技术入门(九):无线通信网络的中枢——“核心网”

[链接]http://www.52im.net/thread-2391-1-1.html

[摘要] 对于通信专业的人来说,几乎每个人都认为核心网难(不只是难,而且是非常难),很难有人能通俗易懂地讲明白它是什么东西。所以本文想借此机会,为零基础的IM开发者或其他移动端应用层程序员们,讲清楚这个话题。


[- 10 -] IM开发者的零基础通信技术入门(十):零基础,史上最强5G技术扫盲

[链接] http://www.52im.net/thread-2394-1-1.html

[摘要] 作为IM开发者,或者移动端开发者来说,提前了解5G技术显然是很有必要的。那么什么是5G技术?技术原理是怎么样的?5G技术将带来哪些技术革新?本文将以零基础的应用程序开发者为阅读对象,帮你找到这些问题的答案。


[- 11 -] IM开发者的零基础通信技术入门(十一):为什么WiFi信号差?一文即懂!

[链接] http://www.52im.net/thread-2402-1-1.html

[摘要] 为什么WiFi信号会受影响?什么情况下该使用何种方式组网?如何改善WiFi信号差的问题?等等,本文将通俗易懂地为你找到这些问题的答案。


[- 12 -] IM开发者的零基础通信技术入门(十二):上网卡顿?网络掉线?一文即懂!

[链接]http://www.52im.net/thread-2406-1-1.html

[摘要] 本文将详细介绍生活中遇到的常见网络问题,及可能的解决方法,虽说是一篇技术文章,但内容将一如既往地通俗易懂,简单实用。


[- 13 -] IM开发者的零基础通信技术入门(十三):为什么手机信号差?一文即懂!

[链接] http://www.52im.net/thread-2415-1-1.html

[摘要] 关于手机信号的问题真的不是大家想象得那么简单。本文正好收集整理了这一块的通信技术知识,一如既往的力求通俗易懂,希望对你有用。


[- 14 -] IM开发者的零基础通信技术入门(十四):高铁上无线上网有多难?一文即懂!

[链接] http://www.52im.net/thread-2419-1-1.html

[摘要] 为什么在高铁上手机信号会这么差?这个无线通信难题真的无法解决吗?今天,作为通信老司机的笔者,就详细和大家聊聊这个问题。


[- 15 -] IM开发者的零基础通信技术入门(十五):理解定位技术,一篇就够

[链接]http://www.52im.net/thread-2428-1-1.html

[摘要] 定位技术到底是怎么实现的?技术原理怎样?有哪些局限性?貌似我们平时也没有做更多了解,既然这样,那就跟着本文来一窥究竟吧。


👉52im社区本周新文:《IM通讯协议专题学习(一):Protobuf从入门到精通,一篇就够! http://www.52im.net/thread-4080-1-1.html》,欢迎阅读!👈

我是Jack Jiang,我为自已带盐!https://github.com/JackJiang2011/MobileIMSDK/

posted @ 2022-11-11 11:33 Jack Jiang 阅读(215) | 评论 (0)编辑 收藏

     摘要: 本文由IBM开发者社区分享,有较多修订和改动。1、引言在当今移动网络时代,手机流量和电量是宝贵的资源,对于移动端最常见的即时通讯IM应用,由于实时通信基于Socket长连接,它对于流量和电量的需求较一般应用来说更高(详见《移动端IM实践:WhatsApp、Line、微信的心跳策略分析》)。在IM应用中,优化数据流量消耗过多的基本方法就是使用高度压缩的通讯协议,而数据压缩后流量减小带来的自然结果也就...  阅读全文

posted @ 2022-11-10 11:49 Jack Jiang 阅读(112) | 评论 (0)编辑 收藏

关于MobileIMSDK

MobileIMSDK 是一套专门为移动端开发的开源IM即时通讯框架,超轻量级、高度提炼,一套API优雅支持UDP 、TCP 、WebSocket 三种协议,支持iOS、Android、H5、标准Java平台,服务端基于Netty编写。

工程开源地址是:

关于RainbowChat

► 详细产品介绍:http://www.52im.net/thread-19-1-1.html
► iOS端更新记录:http://www.52im.net/thread-2735-1-1.html
► 全部运行截图:iOS端全部运行截图 (另:Android端运行截图 点此查看
► 在线体验下载:App Store安装地址 (另:Android端下载体验 点此查看

 

RainbowChat是一套基于开源IM聊天框架 MobileIMSDK 的产品级移动端IM系统。RainbowChat源于真实运营的产品,解决了大量的屏幕适配、细节优化、机器兼容问题(可自行下载体验:专业版下载安装)。

RainbowChat可能是市面上提供im即时通讯聊天源码的,唯一一款同时支持TCP、UDP两种通信协议的IM产品(通信层基于开源IM聊天框架 MobileIMSDK 实现)。

v6.1 版更新内容

此版更新内容更多历史更新日志):

  • 1)[bug] 在聊天信息界面中查找消息时,点击查看指定消息,在聊天界面中不能自动滚动到这条消息;
  • 2)[bug] 点击首页“消息”列表中遗留的陌生人聊天信息时,无法重置消息未读数的问题;
  • 3)[bug] 在聊天界面中进入别的界面再回来时,底部面板没有自动关闭/收起;
  • 4)[优化] 优化了标题栏弹出菜单的圆角效果(使之更符合最新iOS美感设计);
  • 5)[优化] 优化了APP中各种文本输入框UI效果,以及其它UI细节;
  • 6)[优化] 优化了短视频录制界面在iOS16“灵动岛”手机上的ui适配。

此版主要功能运行截图更多截图点此查看):

 

posted @ 2022-11-05 17:42 Jack Jiang 阅读(86) | 评论 (0)编辑 收藏

1、引言

在《IM消息ID技术专题》系列文章的前几篇中,我们已经深切体会到消息ID在分布式IM聊天系统中的重要性以及技术实现难度,各种消息ID生成算法及实现虽然各有优势,但受制于具体的应用场景,也并不能一招吃遍天下,所以真正在IM系统中该如何落地消息ID算法和实现逻辑,还是要因地致宜,根据自已系统的设计逻辑和产品定义取其精华,综合应用之。

本文将基于网易严选的订单ID使用现状,分享我们是如何结合业内常用的分布式ID解决方案,从而在此基础之上进行ID特性丰富,并不断提升系统可用性和稳定性保障。同时,也对ID生成算法的落地实践过程中遇到坑进行了深入剖析。

本篇中的订单ID虽然不同于IM系统中的消息ID,但其技术实践仍然相通,希望能给你的IM系统消息ID技术选型也来更多的启发。

学习交流:

本文已同步发布于:http://www.52im.net/thread-4069-1-1.html

2、关于作者

西狂:服务端研发工程师, 早期参与严选采购、严选财务、严选合伙人以及报警平台等系统后端建设,目前主要致力于严选交易域技术演进以及业务研发工作。

3、系列文章

本文是系列文章中的第7篇,本系列总目录如下:

4、为什么需要分布式ID?

4.1 业务背景

如上图所示,对于网易严选的主站、分销和tob都会生成各自的订单ID,在同步订单数据到订单中心的时候,订单中心会生成一个订单中心内部的一个订单号,只是推送给到下游仓配时使用的订单ID略有不同。

4.2 带来的问题

 因为订单ID使用的混乱,导致了一系列问题的产生,例如: 沟通壁垒 、管控困难以及代码腐化等等。

4.3 技术目标

 我们希望通过分布式ID来帮助生成订单ID,在业务规则上必须全局唯一、安全性高,在性能上要高可用、低延迟。

5、我们的分布式ID架构原理

5.1 技术选型

下表是业内常见的分布式ID解决方案:

综合考虑是否支持水平扩展以及能够显示指定ID长度,最终选择的是Leaf的Segment模式(详见《深度解密美团的分布式ID生成算法》)。

5.2 架构简介

Leaf采用了预分发的方式来生成ID(如下图所示),在DB之上搭载若干个Server,每个Server在启动的时候,都会去DB中拿固定长度的ID列表,存放于内存中,因为ID是基于内存分发的,所以可以做到很高效。

在数据持久化方面,每次去DB拿固定长度的ID列表,只是把最大的ID持久化。

整体架构实现比较简单,主要是为了尽快解决业务层DB压力的问题,但是在生产环境中也暴露出一些问题。

比如:

  • 1)TP999数据波动大,当号段使用完之后还是会hang在更新数据库的I/O上,tp999数据会出现偶尔的尖刺;
  • 2)当更新DB号段的时候,如果DB宕机或者发生主从切换,会导致一段时间的服务不可用。

5.3 可用性优化

为了解决上面提到这个两个问题,引入双Buffer机制和异步更新策略,当一个Buffer消耗到某个临界点时,就会异步的触发任务,把下一个号段加载到内存中。

保证无论何时DB出现问题,都能有一个Buffer的号段可以正常对外提供服务,只要DB在一个Buffer的下发的周期内恢复,就不会影响整个Leaf的可用性。

5.4 步长动态调整

号段长度在固定不变的前提下,流量的突增和锐减都会使得正常流量下维持原有号段正常工作的时间缩短和提升。

可以尝试使用以下关系表达式来描述:

Q * T = L

(Q:服务qps  L:号段长度  T:号段更新周期)

但是Leaf的本质是希望T固定,如果Q和L可以正相关,T就可以趋于一个定值。

所以在Leaf每次更新号段的时候,会根据上一次号段更新的周期T和号段长度step,来决定下一次号段长度nextStep。

如下所示:

T < 15min,nextStep = step * 2

15min < T < 30min,nextStep = step

T > 30min,nextStep = step / 2

(初始指定step <= nextStep <= 最大值(自定义:100W))

6、我们做了什么改进?

6.1 特性丰富

 

通过结合严选的实际业务场景,进行了特性化支持,例如支持批量ID获取、大促提前扩容以及提前跳段处理。

6.2 可用性保障

1)针对DB:

 DB(MySql)采用主从模式(读写分离、降低主库压力),一主两从的配置方式,Master和Slave之间采用的是半同步复制(数据一致性要求,后期可考虑使用MySql Group Replication)。同时还添加了双1配置,保证不丢数据。

2)引入SDK:

通过引入SDK可以降低各个业务方的接入成本、降低Leaf服务端压力以及在Leaf服务不可用时,客户端起到短暂降级的效果。

SDK的实现原理和Leaf类似,在项目启动之初会加载业务关心参数配置信息,在应用构建本地缓存,同样采用了双Buffer存储模式。

6.3 稳定性保障

1)运维方面:

主要分为3个方面:

  • 1)日志监控:可以帮助发现预期之外的异常情况;
  • 2)流量监控:流有助于号段长度的评估范围,预防号段被快速消费的极端场景;
  • 3)线上巡检:可以时刻对服务进行存活校验。

2)SLA高可用方面:

除了运维之外还做了SLA的接入,通常用SLA来衡量系统的稳定性,除此之外我们还按照接口维度设定了SLO目标规则,目前的指标项比较单一只有请求延迟和错误率这两项。

 


 

7、我们遇到的坑

7.1 问题发现

如下图所示,我们发现每次服务启动上线接口的rt(响应时间)都要比平时高的多,但是过了一段时间之后却又恢复成正常水平。

7.2 问题探究

在分析之前,我们可以先简单的回顾下java虚拟机是如何运行Java字节码的。

虚拟机视角下Java字节码如何被虚拟机运行:

Java虚拟机将class文件加载到虚拟机中,然后将字节码翻译成机器码给底层硬件执行,而这里的翻译有两种形式,解释执行和编译执行。前者的优势在于无需等待编译,后者的优势在于实际运行速度更快。HotSpot默认采用混合模式,它会先解释执行字节码,然后将其中反复执行的热点代码,以方法为单位进行即时编译,JVM是依据方法的调用次数以及循环回边的执行次数来触发JIT编译的。

在Java7之前我们可以根据程序的特性选择对应的即时编译器。Java7开始引入分层编译机制(-XX:+TieredCompilation):综合了C1的启动性能优势和C2的峰值性能优势。

分层编译将JVM的执行状态分为了5个层次:

  • L0:解释执行(也会profiling);
  • L1:执行不带profiling的C1代码;
  • L2:执行仅带方法调用次数和循环回边执行次数profiling的C1代码;
  • L3:执行带所有profiling的C1代码;
  • L4:执行C2代码。

对于C1编译的三个层次,按执行效率从高至低:L1 > L2 > L3, 这是因为profiling越多,额外的性能开销越大。通常情况下,C2代码的执行效率比C1代码高出30%以上。(这里需要注意的是Java8默认开启了分层编译)

这张图列出了常见的分层编译的编译路径:

  • 1)通常情况下,热点方法会被第三层的C1编译器编译,再被C2编译器编译(0-> 3-> 4);
  • 2)如果方法的字节数目比较少并且第三层的profilling没有可收集的数据,jvm会判定该方法对于C1和C2的执行效率相同,在经过3层的C1编译过后,直接回到1层的C1(0-> 3-> 1);
  • 3)在C1忙碌的情况下,JVM在解释执行过程中对程序进行profiling,而后直接由4层的C2编译(0-> 4);
  • 4)在C2忙碌的情况下,方法会被2层的C1编译,然后再被3层的C1编译,以减少方法在3层的执行时间(0-> 2-> 3-> 4)。

上图是项目启动时的分层编译日志以及整个过程接口响应RT。

从图中可以看到先是执行了C1编译,再执行C2编译(日志文件中的3和4分别打标L3和L4),满足 0->3->4 编译顺序。

发现从C1编译到C2编译耗时过程比较长,这符合我们一开始提出的疑问,为什么项目启动需要经过一段时间接口RT才能趋于稳定。

7.3 解决方案

为了能在项目启动之初,快速达到接口RT峰值,因此只要尽最大程度缩短解释执行这个中间过程即可。

相应的解决方案:

  • 方案 1:关闭分层编译,降低编译阈值;
  • 方案 2:Mock接口数据, 快速触发JIT编译以及C2编译;
  • 方案 3:Java9 AOT提前编译。

针对方案3:Java9中支持新特性AOT提前编译,相比较于JIT即时编译而言,AOT在运行前就已经编译好了,避免 JIT 编译器的运行时性能消耗,同时避免解释程序的早期性能开销,可以极大提高java代码性能。

8、落地使用概况

Leaf已经在线上环境投入使用,各个业务方(包括主站、渠道、tob)也相应接入进行统一整改,自此严选订单ID生成得到统一收拢。

在整个严选的落地情况,按照业务维度,目前累计接入3个业务,分别是订单ID、订单快照ID、订单商品快照ID,都经受住了双十一和双十二考验。

9、参考资料

[1] 微信的海量IM聊天消息序列号生成实践(算法原理篇)

[2] 解密融云IM产品的聊天消息ID生成策略

[3] 深度解密美团的分布式ID生成算法

[4] 深度解密滴滴的高性能ID生成器(Tinyid)

本文已同步发布于:http://www.52im.net/thread-4069-1-1.html

posted @ 2022-11-03 11:45 Jack Jiang 阅读(109) | 评论 (0)编辑 收藏

为了更好地分类阅读52im.net 总计1000多篇精编文章,我将在每周三推送新的一期技术文集,本次是第4 期。

[- 1 -] 不为人知的网络编程(一):浅析TCP协议中的疑难杂症(上篇)

[链接] http://www.52im.net/thread-1003-1-1.html

[摘要] 可能大家都知道TCP是三次交互完成连接的建立,四次交互来断开一个连接,那为什么是三次握手和四次挥手呢?反过来不行吗?


[- 2 -] 不为人知的网络编程(二):浅析TCP协议中的疑难杂症(下篇)

[链接] http://www.52im.net/thread-1004-1-1.html

[摘要] 接上篇《不为人知的网络编程(一):浅析TCP协议中的疑难杂症(上篇)》,我们提到第6个疑问:TCP的头号疼症TIME_WAIT状态,下面我们继续这个问题的解答。


[-3 -] 不为人知的网络编程(三):关闭TCP连接时为什么会TIME_WAIT、CLOSE_WAIT

[链接] http://www.52im.net/thread-1007-1-1.html

[摘要] 这次就和大家分享一下我们的netframework服务总会抛出一个“connet reset by peer”的原因吧。


[-4 -] 不为人知的网络编程(四):深入研究分析TCP的异常关闭

[链接] http://www.52im.net/thread-1014-1-1.html

[摘要] 大家都明白是“网络被对端重置了”,但究竟什么情况下会导致这种情况呢?本文就对TCP的各种关闭情况做了进一步的测试研究。


[- 5 -] 不为人知的网络编程(五):UDP的连接性和负载均衡

[链接] http://www.52im.net/thread-1018-1-1.html

[摘要] 本文将从实践出发,讨论UDP在实际应用中的连接性和负载均衡问题。


[- 6 -] 不为人知的网络编程(六):深入地理解UDP协议并用好它

[链接] http://www.52im.net/thread-1024-1-1.html

[摘要]本文接上篇《不为人知的网络编程(五):UDP的连接性和负载均衡》,将从实践出发,讨论如何深入地理解UDP协议并在实践中用好它。


[- 7 -] 不为人知的网络编程(七):如何让不可靠的UDP变的可靠?

[链接] http://www.52im.net/thread-1293-1-1.html

[摘要] 在 UDP 之上做一层可靠,很多朋友认为这是很不靠谱的事情,也有朋友认为这是一个大杀器,可以解决实时领域里大部分问题。涉及到实时传输我们都会先考虑 RUDP,RUDP 应用在我们APP核心传输体系的各个方面,但不同的系统场景我们设计了不同的 RUDP 方式,所以基于那些激烈的讨论和我们使用的经验,我决定扒一扒 RUDP,来给大家分享如何让UDP变的可靠的实践经验。


[- 8 -] 不为人知的网络编程(八):从数据传输层深度解密HTTP

[链接] http://www.52im.net/thread-2456-1-1.html

[摘要] 市面上讲HTTP协议的文章很多,但深入到传输层从2进制的角度来解析,则相当少见。保证全篇读完之后,你对HTTP的理解会上升一个台阶!


[- 9 -] 不为人知的网络编程(九):理论联系实际,全方位深入理解DNS

[链接] http://www.52im.net/thread-2740-1-1.html

[摘要] 当我们发现可以上QQ但不能浏览网页时,我们会想到可能是域名服务器挂掉了;当我们用别人提供的hosts文件浏览到一个“不存在”的网页时,我们会了解到域名解析系统的脆弱。然而关于DNS还有一大堆故事值得我们去倾听,去思考。


[- 10 -] 不为人知的网络编程(十):深入操作系统,从内核理解网络包的接收过程(Linux篇)

[链接] http://www.52im.net/thread-3247-1-1.html

[摘要] 这篇文章将用图解的方式,从操作系统这一层来深度理解一下网络包的接收过程。


[- 11 -] 不为人知的网络编程(十一):从底层入手,深度分析TCP连接耗时的秘密

[链接] http://www.52im.net/thread-3265-1-1.html

[摘要] TCP的开销到底有多大,能否进行量化。一条TCP连接的建立需要耗时延迟多少,是多少毫秒,还是多少微秒?能不能有一个哪怕是粗略的量化估计?我今天只分享我在工作实践中遇到的比较高发的各种情况。


[- 12 -] 不为人知的网络编程(十二):彻底搞懂TCP协议层的KeepAlive保活机制

[链接] http://www.52im.net/thread-3506-1-1.html

[摘要] 次借本文想把TCP协议的KeepAlive保活机制给详细的整理出来,以便大家能深入其中一窥究竟。


[- 13 -] 不为人知的网络编程(十三):深入操作系统,彻底搞懂127.0.0.1本机网络通信

[链接] http://www.52im.net/thread-3590-1-1.html

[摘要] 今天咱们就把 127.0.0.1 本机网络通信相关问题搞搞清楚!


[- 14 -] 不为人知的网络编程(十四):拔掉网线再插上,TCP连接还在吗?一文即懂!

[链接] http://www.52im.net/thread-3846-1-1.html

[摘要] 本篇文章,我们就从系统层面深入地探讨一个有趣的TCP技术问题:拔掉网线后,再插上,原本的这条TCP连接还在吗?或者说它还“好”吗?

我是Jack Jiang,我为自已带盐!

https://github.com/JackJiang2011/MobileIMSDK/

posted @ 2022-11-01 12:14 Jack Jiang 阅读(99) | 评论 (0)编辑 收藏

本文作者网易云信高级前端开发工程师李宁,本文有修订。

1、引言

在IM客户端的使用场景中,基于本地数据的全文检索功能扮演着重要的角色,最常用的比如:查找聊天记录、联系人等。

类似于IM中的聊天记录查找、联系人搜索这类功能,有了全文检索能力后,确实能大大提高内容查找的效率,不然,让用户手动翻找,确实降低了用户体验。

本文将要分享的是,网易云信基于Electron的PC端是如何实现IM客户端全文检索能力的。

学习交流:

(本文已同步发布于:http://www.52im.net/thread-4065-1-1.html

2、关于作者

李宁:网易云信高级前端开发工程师,负责音视频 IM SDK 的应用开发、组件化开发及解决方案开发,对 React、PaaS 组件化设计、多平台的开发与编译有丰富的实战经验。

3、系列文章

本文是系列文章中的第6篇,本系列总目录如下:

4、什么是全文检索

所谓全文检索,就是要在大量内容中找到包含某个单词出现位置的技术。

在传统的关系型数据库中,只能通过LIKE条件查询来实现,这样有几个弊端:

  • 1)无法使用数据库索引,需要遍历全表,性能较差;
  • 2)搜索效果差,只能首尾位模糊匹配,无法实现复杂的搜索需求;
  • 3)无法得到内容与搜索条件的相关性。

我们在 IM 的 iOS、安卓以及桌面端中都实现了基于 SQLite 等库的本地数据全文检索功能,但是在 Web 端和 基于Electron的PC端上缺少了这部分功能。

因为在 Web 端,由于浏览器环境限制,能使用的本地存储数据库只有 IndexDB,暂不在讨论的范围内。但在基于Electron的PC端上,虽然也是内置了 Chromium 的内核,但是因为可以使用 Node.js 的能力,于是乎选择的范围就多了一些。本文内容我们具体以基于Electron的IM客户端为例,来讨论全文检索技术实现。

PS:如果你不了解什么是Electron技术,读一下这篇《快速了解Electron:新一代基于Web的跨平台桌面技术》。

我们先来具体看下该如何实现全文检索。

要实现全文检索,离不开以下两个知识点:

  • 1)倒排索引;
  • 2)分词。

这两个技术是实现全文检索的技术以及难点,其实现的过程相对比较复杂,在聊全文索引的实现前,我们具体学习一下这两个技术的原理。

5、什么是倒排索引

先简单介绍下倒排索引,倒排索引的概念区别于正排索引:

  • 1)正排索引:是以文档对象的唯一 ID 作为索引,以文档内容作为记录的结构;
  • 2)倒排索引:是以文档内容中的单词作为索引,将包含该词的文档 ID 作为记录的结构。

以倒排索引库 search-index 举个实际的例子:

在我们的 IM 中,每条消息对象都有 idClient 作为唯一 ID,接下来我们输入「今天天气真好」,将其每个中文单独分词(分词的概念我们在下文会详细分享),于是输入变成了「今」、「天」、「天」、「气」、「真」、「好」。再通过 search-index 的 PUT 方法将其写入库中。

最后看下上述例子存储内容的结构:

如是图所示:可以看到倒排索引的结构,key 是分词后的单个中文、value 是包含该中文消息对象的 idClient 组成的数组。

当然:search-index 除了以上这些内容,还有一些其他内容,例如 Weight、Count 以及正排的数据等,这些是为了排序、分页、按字段搜索等功能而存在的,本文就不再细细展开了。

6、什么是分词

6.1基本概念

分词就是将原先一条消息的内容,根据语义切分成多个单字或词句,考虑到中文分词的效果以及需要在 Node 上运行,我们选择了Nodejieba作为基础分词库。

以下是 jieba 分词的流程图:

以“去北京大学玩”为例,我们选择其中最为重要的几个模块分析一下。

6.2加载词典

jieba 分词会在初始化时先加载词典,大致内容如下:

6.3构建前缀词典

接下来会根据该词典构建前缀词典,结构如下:

其中:“北京大”作为“北京大学”的前缀,它的词频是0,这是为了便于后续构建 DAG 图。

6.4构建 DAG 图

DAG 图是 Directed Acyclic Graph 的缩写,即有向无环图。

基于前缀词典,对输入的内容进行切分。

其中:

  • 1)“去”没有前缀,因此只有一种切分方式;
  • 2)对于“北”,则有“北”、“北京”、“北京大学”三种切分方式;
  • 3)对于“京”,也只有一种切分方式;
  • 4)对于“大”,有“大”、“大学”两种切分方式;
  • 5)对于“学”和“玩”,依然只有一种切分方式。

如此,可以得到每个字作为前缀词的切分方式。

其 DAG 图如下图所示:

6.5最大概率路径计算

以上 DAG 图的所有路径如下:

  • 去/北/京/大/学/玩
  • 去/北京/大/学/玩
  • 去/北京/大学/玩
  • 去/北京大学/玩

因为每个节点都是有权重(Weight)的,对于在前缀词典里的词语,它的权重就是它的词频。因此我们的问题就是想要求得一条最大路径,使得整个句子的权重最高。

这是一个典型的动态规划问题,首先我们确认下动态规划的两个条件。

1)重复子问题:

对于节点 i 和其可能存在的多个后继节点 j 和 k:

  • 1)任意通过i到达j的路径的权重 = 该路径通过i的路径权重 + j的权重,即 R(i -> j) = R(i) + W(j);
  • 2)任意通过i到达k的路径的权重 = 该路径通过i的路径权重 + k的权重,即 R(i -> k) = R(i) + W(k)。

即对于拥有公共前驱节点 i 的 j 和 k,需要重复计算到达 i 路径的权重。

2)最优子结构:

设整个句子的最优路径为 Rmax,末端节点为 x,多个可能存在的前驱节点为 i、j、k。

得到公式如下:

Rmax = max(Rmaxi, Rmaxj, Rmaxk) + W(x)

于是问题变成了求解 Rmaxi、Rmaxj 以及 Rmaxk,子结构里的最优解即是全局最优解的一部分。

如上,最后计算得出最优路径为“去/北京大学/玩”。

6.6HMM 隐式马尔科夫模型

对于未登陆词,jieba 分词采用 HMM(Hidden Markov Model 的缩写)模型进行分词。

它将分词问题视为一个序列标注问题,句子为观测序列,分词结果为状态序列。

jieba 分词作者在 issue 中提到,HMM 模型的参数基于网上能下载到的 1998 人民日报的切分语料,一个 MSR 语料以及自己收集的 TXT 小说、用 ICTCLAS 切分,最后用 Python 脚本统计词频而成。

该模型由一个五元组组成,并有两个基本假设。

五元组:

  • 1)状态值集合;
  • 2)观察值集合;
  • 3)状态初始概率;
  • 4)状态转移概率;
  • 5)状态发射概率。

基本假设:

  • 1)齐次性假设:即假设隐藏的马尔科夫链在任意时刻 t 的状态只依赖于其前一时刻 t-1 的状态,与其它时刻的状态及观测无关,也与时刻 t 无关;
  • 2)观察值独立性假设:即假设任意时刻的观察值只与该时刻的马尔科夫链的状态有关,与其它观测和状态无关。

状态值集合即{ B: begin, E: end, M: middle, S: single },表示每个字所处在句子中的位置,B 为开始位置,E 为结束位置,M 为中间位置,S 是单字成词。

观察值集合就是我们输入句子中每个字组成的集合。

状态初始概率表明句子中的第一个字属于 B、M、E、S 四种状态的概率,其中 E 和 M 的概率都是0,因为第一个字只可能 B 或者 S,这与实际相符。

状态转移概率表明从状态 1 转移到状态 2 的概率,满足齐次性假设,结构可以用一个嵌套的对象表示:

P = {

    B: {E: -0.510825623765990, M: -0.916290731874155},

    E: {B: -0.5897149736854513, S: -0.8085250474669937},

    M: {E: -0.33344856811948514, M: -1.2603623820268226},

    S: {B: -0.7211965654669841, S: -0.6658631448798212},

}

P['B']['E'] 表示从状态 B 转移到状态 E 的概率(结构中为概率的对数,方便计算)为 0.6,同理,P['B']['M'] 表示下一个状态是 M 的概率为 0.4,说明当一个字处于开头时,下一个字处于结尾的概率高于下一个字处于中间的概率,符合直觉,因为二个字的词比多个字的词要更常见。

状态发射概率表明当前状态,满足观察值独立性假设,结构同上,也可以用一个嵌套的对象表示:

P = {

    B: {'突': -2.70366861046, '肃': -10.2782270947, '适': -5.57547658034},

    M: {'要': -4.26625051239, '合': -2.1517176509, '成': -5.11354837278},

    S: {……},

    E: {……},

}

P['B']['突'] 的含义就是状态处于 B,观测的字是“突”的概率的对数值等于 -2.70366861046。

最后,通过Viterbi算法,输入观察值集合,将状态初始概率、状态转移概率、状态发射概率作为参数,输出状态值集合(即最大概率的分词结果)。关于Viterbi算法,本文不再详细展开,有兴趣的读者可以自行查阅。

7、技术实现

上节中介绍的全文检索这两块技术,是我们架构的技术核心。基于此,我们对IM 的 Electron 端技术架构做了改进。以下将详细介绍之。

7.1架构图详解

考虑到全文检索只是 IM 中的一个功能,为了不影响其他 IM 的功能,并且能更快的迭代需求,所以采用了如下的架构方案。

架构图如下:

如上图所示,右边是之前的技术架构,底层存储库使用了 indexDB,上层有读写两个模块。

读写模块的具体作用是:

  • 1)当用户主动发送消息、主动同步消息、主动删除消息以及收到消息的时候,会将消息对象同步到 indexDB;
  • 2)当用户需要查询关键字的时候,会去 indexDB 中遍历所有的消息对象,再使用 indexOf 判断每一条消息对象是否包含所查询的关键字(类似 LIKE)。

那么,当数据量大的时候,查询的速度是非常缓慢的。

左边是加入了分词以及倒排索引数据库的新的架构方案,这个方案不会对之前的方案有任何影响,只是在之前的方案之前加了一层。

现在,读写模块的工作逻辑:

  • 1)当用户主动发送消息、主动同步消息、主动删除消息以及收到消息的时候,会将每一条消息对象中的消息经过分词后同步到倒排索引数据库;
  • 2)当用户需要查询关键字的时候,会先去倒排索引数据库中找出对应消息的 idClient,再根据 idClient 去 indexDB 中找出对应的消息对象返回给用户。

7.2架构优点

该方案有以下4个优点:

  • 1)速度快:通过 search-index 实现倒排索引,从而提升了搜索速度;
  • 2)跨平台:因为 search-index 与 indexDB 都是基于 levelDB,因此 search-index 也支持浏览器环境,这样就为 Web 端实现全文检索提供了可能性;
  • 3)独立性:倒排索引库与 IM 主业务库 indexDB 分离;
  • 4)灵活性:全文检索以插件的形式接入。

针对上述第“3)”点:当 indexDB 写入数据时,会自动通知到倒排索引库的写模块,将消息内容分词后,插入到存储队列当中,最后依次插入到倒排索引数据库中。当需要全文检索时,通过倒排索引库的读模块,能快速找到对应关键字的消息对象的 idClient,根据 idClient 再去 indexDB 中找到消息对象并返回。

针对上述第“4)”点:它暴露出一个高阶函数,包裹 IM 并返回新的经过继承扩展的 IM,因为 JS 面向原型的机制,在新的 IM 中不存在的方法,会自动去原型链(即老的 IM)当中查找,因此,使得插件可以聚焦于自身方法的实现上,并且不需要关心 IM 的具体版本,并且插件支持自定义分词函数,满足不同用户不同分词需求的场景

7.3使用效果

使用了如上架构后,经过我们的测试,在数据量 20W 的级别上,搜索时间从最开始的十几秒降到一秒内,搜索速度快了 20 倍左右。

8、本文小结

本文中,我们便基于Nodejiebasearch-index在 Electron 上实现了IM聊天消息的全文检索,加快了聊天记录的搜索速度。

当然,后续我们还会针对以下方面做更多的优化,比如以下两点:

1)写入性能 :在实际的使用中,发现当数据量大了以后,search-index 依赖的底层数据库 levelDB 会存在写入性能瓶颈,并且 CPU 和内存的消耗较大。经过调研,SQLite 的写入性能相对要好很多,从观测来看,写入速度只与数据量成正比,CPU 和内存也相对稳定,因此,后续可能会考虑用将 SQLite 编译成 Node 原生模块来替换 search-index。

2)可扩展性 :目前对于业务逻辑的解耦还不够彻底。倒排索引库当中存储了某些业务字段。后续可以考虑倒排索引库只根据关键字查找消息对象的 idClient,将带业务属性的搜索放到 indexDB 中,将倒排索引库与主业务库彻底解耦。

以上,就是本文的全部分享,希望我的分享能对大家有所帮助。

9、参考资料

[1]微信移动端的全文检索优化之路

[2]微信移动端的全文检索多音字问题解决方案

[3]微信iOS端的最新全文检索技术优化实践

[4]蘑菇街基于Electron开发IM客户端的技术实践

[5]融云基于Electron的IM跨平台SDK改造实践总结

[6]闲鱼IM基于Flutter的移动端跨端改造实践

(本文已同步发布于:http://www.52im.net/thread-4065-1-1.html

posted @ 2022-10-27 11:18 Jack Jiang 阅读(90) | 评论 (0)编辑 收藏

仅列出标题
共47页: First 上一页 12 13 14 15 16 17 18 19 20 下一页 Last 
Jack Jiang的 Mail: jb2011@163.com, 联系QQ: 413980957, 微信: hellojackjiang