4

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结

 1 year ago
source link: http://www.52im.net/thread-4202-1-1.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client
IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结
微信扫一扫关注!

本文由巩鹏军分享,原题“IM兼容性基建”,即时通讯网有修订。

一个成熟的IM成品,在运营过程中随着时间的推移,会发布不同的版本,但为了用户体验并不能强制要求用户必须升级到最新版本,而服务端此时已经是最新版本了,所以为了让这些不同客户端版本的用户都能正常使用(尤其IM这种产品,不同版本可能通信协议都会有变动,这就更要命了),则必须要针对不同客户端版本的兼容处理。

本文将基于笔者的IM产品开发和运营实践,为你分享如何实现不同APP客户端版本与服务端通信的兼容性处理方案。

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_cover-opti.png

2、关于作者

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_x.jpg

巩鹏军:专注移动开发十多年,热爱即时通讯技术。个人微信公众号:“巩鹏军”。

作者在即时通讯网分享的另一篇《知识科普:IM聊天应用是如何将消息发送给对方的?(非技术篇)》,感兴趣的读者也可以看看。

3、一个App时怎么办?

提示:“一个App”指的是同一个IM服务端,只服务于一个特定的IM产品。

首先想到的就是直接使用App版本号判断新老版本并进行兼容处理。

如下图所示:
IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_1.png



一般来说,不同的IM客户端(如iOS、Android、Windows、Mac)都是同步迭代,多端发版时间一致,App版本号也一样。

所以用跨多端的App版本号可以很容易地让服务端只用写一遍判断和兼容逻辑。

示例:假设从V2.1.0开始应用红包消息,那么判断客户端是否支持红包的逻辑就很简单。

伪代码如下:

boolean isSupportRedEnvelop(String appVersion) {
return gte(appVersion, "2.1.0");
}
附:版本号比对逻辑(未充分考虑异常情况):
List<Integer> toNums(String version) {
Matcher matcher = Pattern
.compile("/[0-9]+\\.[0-9]+\\.[0-9]+")
.matcher(version);
String versionString = matcher.find()
? matcher.group(0).substring(1)
: "1.0.0";
List<Integer> verNums = Arrays
.stream(versionString.split("\\."))
.map(Integer::valueOf)
.collect(Collectors.toList());
return verNums;
}
boolean gte(String version, String target) {
List<Integer> appVerNums = toNums(version);
Integer appMajor = appVerNums.get(0);
Integer appMinor = appVerNums.get(1);
Integer appPatch = appVerNums.get(2);
List<Integer> targetNums = toNums(target);
Integer targetMajor = targetNums.get(0);
Integer targetMinor = targetNums.get(1);
Integer targetPatch = targetNums.get(2);
return (appMajor >= targetMajor) ||
(appMinor >= targetMinor) ||
(appPatch >= targetPatch);
}

4、多个App时怎么办?

4.1概述

提示:“多个App”指的是同一个IM服务端,可能作为通用服务,作为多个不同APP产品中的聊天模块使用的场景。

只有一个App时肯定是比较简单的。但现实情况是一套IM系统通常会用于多个业务场景,这是很普遍的现象。业界的知名IM产品,比如钉钉、飞书、企业微信、美团大象等都是这样。

底层逻辑大概是:IM系统比较复杂,功能繁多而且难以实现、更难以稳定,所以一个IM团队维护一套IM系统,然后应用在多个业务场景就是最具性价比的选择了。

4.2使用App版本号

每个业务场景都会有自己的客户端App,每个App都有自己的版本号,那么根据App版本号判断新老版本的逻辑就不适用了(如下图所示)。

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_2.png



一个App时可以这样做兼容性判断:

boolean isSupportRedEnvelop(String appVersion) {
return gte(appVersion, "2.1.0");
}
多个App时的兼容性判断:
boolean isSupportRedEnvelop(String version) {
return
(app.equals("App1")&>e(version,"2.1.0"))||
(app.equals("App2")&>e(version,"2.2.3"))||
(app.equals("App3")&>e(version,"6.1"));
}

4.3使用App版本号的麻烦

随着App的增多,需要的判断也越多,这会很麻烦,也很容易出错。

每个App推出新版本后,用户不可能瞬间就升级到最新版本,根据经验,每个App往往都会同时存在十个以上的不同版本。

这就会形成如下图所示的局面:
IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_3.png

5、多个App时,可将IM能力提炼为一套公用代码

多个App时的问题总结起来就是:一套服务端代码如何适应集成了不同IM能力的不同App客户端?

我们来具体举例分析一下,假设一个IM团队维护的IM相关的客户端模块有IM Client SDK、联系人、长连接、朋友圈等四个模块(如下图所示)。

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_4.png



如上图所示:

  • 1)App 1:集成了全部四个模块;
  • 2)App 2:只集成了三个模块;
  • 3)App 3:只集成了三个模块。
因为三个App面向的客户群不同,发版节奏不同,所以各自集成的IM的能力也不同。

比如下面这样:
  • 1)App 1:面向内部员工办公沟通使用的App 1需要功能丰富,对于稳定性和Bug有一定的包容性,也容易沟通和修复再发版;
  • 2)App 2:面向客服场景,用于企业的客服专员和企业的C端用户沟通解决客诉问题,对于稳定性要求高,C端用户升级率不好控制,发版节奏慢,最快只能和主业务App一致;
  • 3)App 3:面向企业和B端供应商,比如美团和美团上的商户,京东和京东平台上的第三方商家,对于稳定性要求也比较高,B端商家的升级率好控制一点,发版节奏也可以快一些。
从上图可以看出,因为IM核心能力是同一个团队维护,所以Core包含的多个模块的代码必然是只有一套源代码。不同App只是Core集成打包出来的产物,或者说不同App只是Core外面套了不同的壳而已,只要Core一样,则App的IM能力就一样(这就是本节标题所述的“多个App时,可将IM能力提炼为一套公用的代码”这个意思)。

6、给每个App中使用的公用代码(Core)一个版本号

如上节所述,我们将IM能力提炼为一套公用代码(以下内容简称“Core”)。

那么,我们能不能给Core一个版本标识呢?

答案是肯定的:
IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_5.png



站在App的角度,每个App相当于打上了Core版本标签:

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_6.png

7、如何正确地解读Core版呢?

7.1抛开App看Core版本

如果不看App版本,只看Core版本标签:
IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_7.png

7.2从一套服务端代码看Core版本

同一个IM团队,其IM Servers必然也是同一套代码集,不考虑部署的区别。

那么上图逻辑上等价于下图:
IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_8.png

7.3使用Core版本的兼容性判断

站在Core的视角,多个App就像单个App类似,只是使用的版本标识不同。

具体如下:
  • 1)单个App时,IM服务端要区分不同App版本;
  • 2)多个App时,IM服务端要区分不同Core版本。
还拿是否支持红包的判断举例。

一个App时:
boolean isSupportRedEnvelop(String appVersion){
return gte(appVersion, "2.1.0");
}
多个App时:
boolean isSupportRedEnvelop(Integer coreVersion){
return coreVersion >= 2;
}
通过Core版本号,我们可以把兼容逻辑判断简化到和单个App一样的简单。

8、关于Core版本的命名和取值

关于Core版本号的取值,有下列可能的选项:
  • 选项一:语义版本号 1.2.0;
  • 选项二:整数 自然数 1 2 3;
  • 选项三:整数 迭代日期 20220819 或 220819。
因为Core版本号不用给最终用户看的,无需遵循常见的语义版本号规范。而且Core版本号只用于版本对比,所以整数会是一个比较好的选择,方便比较,准确可靠。

用自然数 1、 2、 3作为Core版本号是可以的,每个迭代发布新的Core版本时递增一下就可以了。

但是考虑到有多个终端平台iOS、Android、Windows、Mac,如果某个平台的Core发布后发现小Bug需要HotFix,那么要递增版本号,就会挤占其它端的下一个自然数。究其原因,在于自然数是连续的,没办法在两个常规的版本间插入一个HotFix版本。

IM开发干货分享:IM客户端不同版本兼容运行的技术思路和实践总结_9.png



选项三就可以解决这个问题:因为Core的迭代发布日期是稀疏的,若干天后才会发布一个Core版本,那么当某个端需要一个HotFix版本时,选择HotFix当天的日期作为版本号即可。

总体上:多个端的主要版本号都是约定的统一的发布日期,多端一致,同时允许某个端临时HotFix插入一个新的版本号,保留弹性。

参考 Google 对Android SDK API版本的实践,我们可以把Core版本号命名为core_level,取值为Core的发布日期的整数表示。

9、多个App情况下的其它版本标识

1)platform:

一套Core,不同端在实际开发中,可能存在差异,为了针对具体端进行特定的兼容,需要知道当前是哪个端,可以约定platform字段表示端。取值可以是:ios、android、win、mac、linux等。

2)App版本号:

在IM相关逻辑的兼容性判断中,只需使用跨App的多端一致的core_level了。但是为了和最终用户、产品经理等沟通方便,保留App版本号app_version用于人和人之间沟通交流。core_level主要用于研发工程师之间,还有工程师和程序之间的沟通。两者各取所长。

10、版本标识的传输方式

每个API和每条长连接数据包都携带Core版本,这样服务端可以无状态得处理每一个请求。如果需要在服务端主动推送时区分目标端的版本,可以在App登录时将其携带的Core版本落库存储,然后推送时查询使用。

10.1短连接(HTTP)

HTTP短连接通过新增Header字段方式传输:
curl "https://{domain}/api/v1/xxx" \
-H "platform: ios" \
-H "app_version: 8.0.25" \
-H "core_level: 220819"

10.2长连接(Socket)

长连接SDK通过类似HTTP Header的方式传输:
{
"platform":"ios",
"app_version":"8.0.25",
"core_level":"220819"
}

10.3短转长

短转长时HTTP Header会转换为长连接数据body里的header通过长链传递。

这样就同时存在长连接header和长连接body.header两套字段,最终以长连接body.header为准即可。

10.4其它

IM系统里的浏览器和小程序,如果可以新增HTTP Header则新增Header传输,实在没有办法可以通过User-Agent传输该信息,服务端优先解析Header,没有找到时再解析User-Agent。

服务端解析UA的正则表达式:
/ platform\/(ios|android|mac|win|linux) app_version\/([0-9]\.[0-9]+\.[0-9]+) core_level\/([1-9][0-9]+)( |$)/
以上正则表达式在线运行效果:点此查看

11、本文小结

至此,我们找到了一个适用于多个App、多个子模块、多个功能点、临时BugFix的版本标识:Core版本号,这样就可以很好地解决多App的IM能力兼容性问题。

以下是版本兼容性判断伪码:
boolean isSupportRedEnvelop(Integer coreLevel) {
return coreLevel >= 220819;
}

12、参考资料

[1] Browser vs Engine Version
[2] Node.js ABI version number
[3] Android SDK API Level
[4] 零基础IM开发入门(一):什么是IM系统?
[5] 一套海量在线用户的移动端IM架构设计实践分享(含详细图文)
[6] 一套原创分布式即时通讯(IM)系统理论架构方案
[7] 从零到卓越:京东客服即时通讯系统的技术架构演进历程
[8] 一套亿级用户的IM架构技术干货(上篇):整体架构、服务拆分等
[9] 基于实践:一套百万消息量小规模IM系统技术要点总结
[10] 一套十万级TPS的IM综合消息系统的架构实践与思考
[11] 从新手到专家:如何设计一套亿级消息量的分布式IM系统
[12] 闲鱼亿级IM消息系统的架构演进之路
[13] 深度解密钉钉即时消息服务DTIM的技术设计
[14] 一套高可用、易伸缩、高并发的IM群聊、单聊架构方案设计实践
[15] 企业微信的IM架构设计揭秘:消息模型、万人群、已读回执、消息撤回等

来源:即时通讯网 - 即时通讯开发者社区!

上一篇:网络编程懒人入门(十五):外行也能读懂的网络硬件设备功能原理速成

本帖已收录至以下技术专辑


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK