IM開發(fā)干貨分享:IM客戶端不同版本兼容運(yùn)行的技術(shù)思路和實(shí)踐總結(jié)
Posted on 2023-04-28 10:41 Jack Jiang 閱讀(82) 評(píng)論(0) 編輯 收藏本文由鞏鵬軍分享,原題“IM兼容性基建”,本文有修訂。
1、引言
一個(gè)成熟的IM成品,在運(yùn)營(yíng)過程中隨著時(shí)間的推移,會(huì)發(fā)布不同的版本,但為了用戶體驗(yàn)并不能強(qiáng)制要求用戶必須升級(jí)到最新版本,而服務(wù)端此時(shí)已經(jīng)是最新版本了,所以為了讓這些不同客戶端版本的用戶都能正常使用(尤其IM這種產(chǎn)品,不同版本可能通信協(xié)議都會(huì)有變動(dòng),這就更要命了),則必須要針對(duì)不同客戶端版本的兼容處理。
本文將基于筆者的IM產(chǎn)品開發(fā)和運(yùn)營(yíng)實(shí)踐,為你分享如何實(shí)現(xiàn)不同APP客戶端版本與服務(wù)端通信的兼容性處理方案。
學(xué)習(xí)交流:
- 移動(dòng)端IM開發(fā)入門文章:《新手入門一篇就夠:從零開發(fā)移動(dòng)端IM》
- 開源IM框架源碼:https://github.com/JackJiang2011/MobileIMSDK(備用地址點(diǎn)此)
(本文已同步發(fā)布于:http://www.52im.net/thread-4202-1-1.html)
2、關(guān)于作者

鞏鵬軍:專注移動(dòng)開發(fā)十多年,熱愛即時(shí)通訊技術(shù)。個(gè)人微信公眾號(hào):“鞏鵬軍”。
作者在即時(shí)通訊網(wǎng)分享的另一篇《知識(shí)科普:IM聊天應(yīng)用是如何將消息發(fā)送給對(duì)方的?(非技術(shù)篇)》,感興趣的讀者也可以看看。
3、一個(gè)App時(shí)怎么辦?
提示:“一個(gè)App”指的是同一個(gè)IM服務(wù)端,只服務(wù)于一個(gè)特定的IM產(chǎn)品。
首先想到的就是直接使用App版本號(hào)判斷新老版本并進(jìn)行兼容處理。
如下圖所示:

一般來說,不同的IM客戶端(如iOS、Android、Windows、Mac)都是同步迭代,多端發(fā)版時(shí)間一致,App版本號(hào)也一樣。
所以用跨多端的App版本號(hào)可以很容易地讓服務(wù)端只用寫一遍判斷和兼容邏輯。
示例:假設(shè)從V2.1.0開始應(yīng)用紅包消息,那么判斷客戶端是否支持紅包的邏輯就很簡(jiǎn)單。
偽代碼如下:
booleanisSupportRedEnvelop(String appVersion) {
returngte(appVersion, "2.1.0");
}
附:版本號(hào)比對(duì)邏輯(未充分考慮異常情況):
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());
returnverNums;
}
booleangte(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、多個(gè)App時(shí)怎么辦?
4.1概述
提示:“多個(gè)App”指的是同一個(gè)IM服務(wù)端,可能作為通用服務(wù),作為多個(gè)不同APP產(chǎn)品中的聊天模塊使用的場(chǎng)景。
只有一個(gè)App時(shí)肯定是比較簡(jiǎn)單的。但現(xiàn)實(shí)情況是一套IM系統(tǒng)通常會(huì)用于多個(gè)業(yè)務(wù)場(chǎng)景,這是很普遍的現(xiàn)象。業(yè)界的知名IM產(chǎn)品,比如釘釘、飛書、企業(yè)微信、美團(tuán)大象等都是這樣。
底層邏輯大概是:IM系統(tǒng)比較復(fù)雜,功能繁多而且難以實(shí)現(xiàn)、更難以穩(wěn)定,所以一個(gè)IM團(tuán)隊(duì)維護(hù)一套IM系統(tǒng),然后應(yīng)用在多個(gè)業(yè)務(wù)場(chǎng)景就是最具性價(jià)比的選擇了。
4.2使用App版本
每個(gè)業(yè)務(wù)場(chǎng)景都會(huì)有自己的客戶端App,每個(gè)App都有自己的版本號(hào),那么根據(jù)App版本號(hào)判斷新老版本的邏輯就不適用了(如下圖所示)。

一個(gè)App時(shí)可以這樣做兼容性判斷:
booleanisSupportRedEnvelop(String appVersion) {
returngte(appVersion, "2.1.0");
}
多個(gè)App時(shí)的兼容性判斷:
booleanisSupportRedEnvelop(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版本號(hào)的麻煩
隨著App的增多,需要的判斷也越多,這會(huì)很麻煩,也很容易出錯(cuò)。
每個(gè)App推出新版本后,用戶不可能瞬間就升級(jí)到最新版本,根據(jù)經(jīng)驗(yàn),每個(gè)App往往都會(huì)同時(shí)存在十個(gè)以上的不同版本。
這就會(huì)形成如下圖所示的局面:

5、多個(gè)App時(shí),可將IM能力提煉為一套公用代碼
多個(gè)App時(shí)的問題總結(jié)起來就是:一套服務(wù)端代碼如何適應(yīng)集成了不同IM能力的不同App客戶端?
我們來具體舉例分析一下,假設(shè)一個(gè)IM團(tuán)隊(duì)維護(hù)的IM相關(guān)的客戶端模塊有IM Client SDK、聯(lián)系人、長(zhǎng)連接、朋友圈等四個(gè)模塊(如下圖所示)。

如上圖所示:
- 1)App 1:集成了全部四個(gè)模塊;
- 2)App 2:只集成了三個(gè)模塊;
- 3)App 3:只集成了三個(gè)模塊。
因?yàn)槿齻€(gè)App面向的客戶群不同,發(fā)版節(jié)奏不同,所以各自集成的IM的能力也不同。
比如下面這樣:
- 1)App 1:面向內(nèi)部員工辦公溝通使用的App 1需要功能豐富,對(duì)于穩(wěn)定性和Bug有一定的包容性,也容易溝通和修復(fù)再發(fā)版;
- 2)App 2:面向客服場(chǎng)景,用于企業(yè)的客服專員和企業(yè)的C端用戶溝通解決客訴問題,對(duì)于穩(wěn)定性要求高,C端用戶升級(jí)率不好控制,發(fā)版節(jié)奏慢,最快只能和主業(yè)務(wù)App一致;
- 3)App 3:面向企業(yè)和B端供應(yīng)商,比如美團(tuán)和美團(tuán)上的商戶,京東和京東平臺(tái)上的第三方商家,對(duì)于穩(wěn)定性要求也比較高,B端商家的升級(jí)率好控制一點(diǎn),發(fā)版節(jié)奏也可以快一些。
從上圖可以看出,因?yàn)镮M核心能力是同一個(gè)團(tuán)隊(duì)維護(hù),所以Core包含的多個(gè)模塊的代碼必然是只有一套源代碼。不同App只是Core集成打包出來的產(chǎn)物,或者說不同App只是Core外面套了不同的殼而已,只要Core一樣,則App的IM能力就一樣(這就是本節(jié)標(biāo)題所述的“多個(gè)App時(shí),可將IM能力提煉為一套公用的代碼”這個(gè)意思)。
6、給每個(gè)App中使用的公用代碼(Core)一個(gè)版本號(hào)
如上節(jié)所述,我們將IM能力提煉為一套公用代碼(以下內(nèi)容簡(jiǎn)稱“Core”)。
那么,我們能不能給Core一個(gè)版本標(biāo)識(shí)呢?
答案是肯定的:

站在App的角度,每個(gè)App相當(dāng)于打上了Core版本標(biāo)簽:

7、如何正確地解讀Core版呢?
7.1拋開App看Core版本
如果不看App版本,只看Core版本標(biāo)簽:

7.2從一套服務(wù)端代碼看Core版本
同一個(gè)IM團(tuán)隊(duì),其IM Servers必然也是同一套代碼集,不考慮部署的區(qū)別。
那么上圖邏輯上等價(jià)于下圖:

7.3使用Core版本的兼容性判斷
站在Core的視角,多個(gè)App就像單個(gè)App類似,只是使用的版本標(biāo)識(shí)不同。
具體如下:
- 1)單個(gè)App時(shí),IM服務(wù)端要區(qū)分不同App版本;
- 2)多個(gè)App時(shí),IM服務(wù)端要區(qū)分不同Core版本。
還拿是否支持紅包的判斷舉例。
一個(gè)App時(shí):
booleanisSupportRedEnvelop(String appVersion){
returngte(appVersion, "2.1.0");
}
多個(gè)App時(shí):
booleanisSupportRedEnvelop(Integer coreVersion){
returncoreVersion >= 2;
}
通過Core版本號(hào),我們可以把兼容邏輯判斷簡(jiǎn)化到和單個(gè)App一樣的簡(jiǎn)單。
8、關(guān)于Core版本的命名和取值
關(guān)于Core版本號(hào)的取值,有下列可能的選項(xiàng):
- 選項(xiàng)一:語義版本號(hào) 1.2.0;
- 選項(xiàng)二:整數(shù) 自然數(shù) 1 2 3;
- 選項(xiàng)三:整數(shù) 迭代日期 20220819 或 220819。
因?yàn)镃ore版本號(hào)不用給最終用戶看的,無需遵循常見的語義版本號(hào)規(guī)范。而且Core版本號(hào)只用于版本對(duì)比,所以整數(shù)會(huì)是一個(gè)比較好的選擇,方便比較,準(zhǔn)確可靠。
用自然數(shù) 1、 2、 3作為Core版本號(hào)是可以的,每個(gè)迭代發(fā)布新的Core版本時(shí)遞增一下就可以了。
但是考慮到有多個(gè)終端平臺(tái)iOS、Android、Windows、Mac,如果某個(gè)平臺(tái)的Core發(fā)布后發(fā)現(xiàn)小Bug需要HotFix,那么要遞增版本號(hào),就會(huì)擠占其它端的下一個(gè)自然數(shù)。究其原因,在于自然數(shù)是連續(xù)的,沒辦法在兩個(gè)常規(guī)的版本間插入一個(gè)HotFix版本。

選項(xiàng)三就可以解決這個(gè)問題:因?yàn)镃ore的迭代發(fā)布日期是稀疏的,若干天后才會(huì)發(fā)布一個(gè)Core版本,那么當(dāng)某個(gè)端需要一個(gè)HotFix版本時(shí),選擇HotFix當(dāng)天的日期作為版本號(hào)即可。
總體上:多個(gè)端的主要版本號(hào)都是約定的統(tǒng)一的發(fā)布日期,多端一致,同時(shí)允許某個(gè)端臨時(shí)HotFix插入一個(gè)新的版本號(hào),保留彈性。
參考 Google 對(duì)Android SDK API版本的實(shí)踐,我們可以把Core版本號(hào)命名為core_level,取值為Core的發(fā)布日期的整數(shù)表示。
9、多個(gè)App情況下的其它版本標(biāo)識(shí)
1)platform:
一套Core,不同端在實(shí)際開發(fā)中,可能存在差異,為了針對(duì)具體端進(jìn)行特定的兼容,需要知道當(dāng)前是哪個(gè)端,可以約定platform字段表示端。取值可以是:ios、android、win、mac、linux等。
2)App版本號(hào):
在IM相關(guān)邏輯的兼容性判斷中,只需使用跨App的多端一致的core_level了。但是為了和最終用戶、產(chǎn)品經(jīng)理等溝通方便,保留App版本號(hào)app_version用于人和人之間溝通交流。core_level主要用于研發(fā)工程師之間,還有工程師和程序之間的溝通。兩者各取所長(zhǎng)。
10、版本標(biāo)識(shí)的傳輸方式
每個(gè)API和每條長(zhǎng)連接數(shù)據(jù)包都攜帶Core版本,這樣服務(wù)端可以無狀態(tài)得處理每一個(gè)請(qǐng)求。如果需要在服務(wù)端主動(dòng)推送時(shí)區(qū)分目標(biāo)端的版本,可以在App登錄時(shí)將其攜帶的Core版本落庫(kù)存儲(chǔ),然后推送時(shí)查詢使用。
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長(zhǎng)連接(Socket)
長(zhǎng)連接SDK通過類似HTTP Header的方式傳輸:
{
"platform":"ios",
"app_version":"8.0.25",
"core_level":"220819"
}
10.3短轉(zhuǎn)長(zhǎng)
短轉(zhuǎn)長(zhǎng)時(shí)HTTP Header會(huì)轉(zhuǎn)換為長(zhǎng)連接數(shù)據(jù)body里的header通過長(zhǎng)鏈傳遞。
這樣就同時(shí)存在長(zhǎng)連接header和長(zhǎng)連接body.header兩套字段,最終以長(zhǎng)連接body.header為準(zhǔn)即可。
10.4其它
IM系統(tǒng)里的瀏覽器和小程序,如果可以新增HTTP Header則新增Header傳輸,實(shí)在沒有辦法可以通過User-Agent傳輸該信息,服務(wù)端優(yōu)先解析Header,沒有找到時(shí)再解析User-Agent。
服務(wù)端解析UA的正則表達(dá)式:
/ platform\/(ios|android|mac|win|linux) app_version\/([0-9]\.[0-9]+\.[0-9]+) core_level\/([1-9][0-9]+)( |$)/
以上正則表達(dá)式在線運(yùn)行效果:點(diǎn)此查看。
11、本文小結(jié)
至此,我們找到了一個(gè)適用于多個(gè)App、多個(gè)子模塊、多個(gè)功能點(diǎn)、臨時(shí)BugFix的版本標(biāo)識(shí):Core版本號(hào),這樣就可以很好地解決多App的IM能力兼容性問題。
以下是版本兼容性判斷偽碼:
booleanisSupportRedEnvelop(Integer coreLevel) {
returncoreLevel >= 220819;
}
12、參考資料
[2] Node.js ABI version number
[4] 零基礎(chǔ)IM開發(fā)入門(一):什么是IM系統(tǒng)?
[5] 一套海量在線用戶的移動(dòng)端IM架構(gòu)設(shè)計(jì)實(shí)踐分享(含詳細(xì)圖文)
[6] 一套原創(chuàng)分布式即時(shí)通訊(IM)系統(tǒng)理論架構(gòu)方案
[7] 從零到卓越:京東客服即時(shí)通訊系統(tǒng)的技術(shù)架構(gòu)演進(jìn)歷程
[8] 一套億級(jí)用戶的IM架構(gòu)技術(shù)干貨(上篇):整體架構(gòu)、服務(wù)拆分等
[9] 基于實(shí)踐:一套百萬消息量小規(guī)模IM系統(tǒng)技術(shù)要點(diǎn)總結(jié)
[10] 一套十萬級(jí)TPS的IM綜合消息系統(tǒng)的架構(gòu)實(shí)踐與思考
[11] 從新手到專家:如何設(shè)計(jì)一套億級(jí)消息量的分布式IM系統(tǒng)
[12] 閑魚億級(jí)IM消息系統(tǒng)的架構(gòu)演進(jìn)之路
[13] 深度解密釘釘即時(shí)消息服務(wù)DTIM的技術(shù)設(shè)計(jì)
[14] 一套高可用、易伸縮、高并發(fā)的IM群聊、單聊架構(gòu)方案設(shè)計(jì)實(shí)踐
[15] 企業(yè)微信的IM架構(gòu)設(shè)計(jì)揭秘:消息模型、萬人群、已讀回執(zhí)、消息撤回等
(本文已同步發(fā)布于:http://www.52im.net/thread-4202-1-1.html)
作者:Jack Jiang (點(diǎn)擊作者姓名進(jìn)入Github)
出處:http://www.52im.net/space-uid-1.html
交流:歡迎加入即時(shí)通訊開發(fā)交流群 215891622
討論:http://www.52im.net/
Jack Jiang同時(shí)是【原創(chuàng)Java
Swing外觀工程BeautyEye】和【輕量級(jí)移動(dòng)端即時(shí)通訊框架MobileIMSDK】的作者,可前往下載交流。
本博文
歡迎轉(zhuǎn)載,轉(zhuǎn)載請(qǐng)注明出處(也可前往 我的52im.net 找到我)。