后臺架構(gòu)剖析1.聊天APP后臺架構(gòu)1).網(wǎng)絡(luò)特性及解決方案a.弱網(wǎng)絡(luò)性 弱網(wǎng)絡(luò)性指手機(jī)不斷移動(例如地鐵,、汽車)的特性,處于快速移動中,出現(xiàn)信號不穩(wěn),、響應(yīng)時間過長,、出現(xiàn)丟包等情況。 因此針對弱網(wǎng)絡(luò)環(huán)境,開發(fā)者設(shè)計協(xié)議的時候必須考慮盡量減少數(shù)據(jù)往返次數(shù),,降低耗時,。 由于弱網(wǎng)絡(luò)性,App以長連接的形式連接服務(wù)器,,可能出現(xiàn)App和服務(wù)器連接忽然中斷的情況,,而且這種情況無法通過連接端口的異常判斷。例如當(dāng)?shù)罔F高速行駛的時候網(wǎng)絡(luò)中斷,,App無法向服務(wù)器的端口發(fā)送斷開的信息,,服務(wù)器還以為App一直保持連接,這種現(xiàn)象稱為TCP half-open TCP half-open的危害: - 占用服務(wù)器資源(服務(wù)器維持連接耗費(fèi)資源)
- 發(fā)送消息的異常
有效防止TCP half-open的方法是使用應(yīng)用層心跳包:在App和服務(wù)器保持連接過程中,,App在規(guī)定時間間隔內(nèi)向服務(wù)器發(fā)送一個數(shù)據(jù)(為節(jié)省流量,,數(shù)據(jù)可以是單字節(jié)字符,因?yàn)閿?shù)據(jù)是隔一定時間發(fā)送一次,,這種數(shù)據(jù)被形象的稱為"心跳數(shù)據(jù)"),。對于未收到心跳數(shù)據(jù)的連接,服務(wù)器主動斷開連接,。 服務(wù)器檢查App的連接有三種方式: 服務(wù)器記錄每個連接收到心跳包的最后時間,,每隔一段時間檢查所有連接,,若某個連接最后收到心跳包時間減去當(dāng)前時間大于超時時間,斷開連接,。這種方式主要缺點(diǎn)是需要同時檢查所有連接,,如果連接數(shù)量過大,檢查連接是對系統(tǒng)的性能影響大,。 服務(wù)器為每一個連接建立一個定時器,,到了規(guī)定的超時時間沒有收到心跳包定時器就觸發(fā),把當(dāng)前連接斷開,,如果收到心跳包,,就重置定時器,不斷重復(fù)過程,。這種方法主要缺點(diǎn),,為每一個連接設(shè)置獨(dú)立定時器,如果連接數(shù)量過大,,會建立大量定時器,耗費(fèi)系統(tǒng)資源,。 這種方式是方式1和方式2的折中,,稱為時間輪片。按照超時時間的長短,,每秒設(shè)置一個桶,,如果超時時間為60秒就設(shè)置60個桶,60個桶組成一個循環(huán)隊(duì)列,,第一個桶放一秒后將要超時的連接,,第二個桶放置兩秒后將要超時的連接,每一個連接一收到心跳包就把自己放到第60個桶,,然后每秒的定時器把第一個桶中的連接斷開,,把這個空桶放到隊(duì)尾。這種服務(wù)器檢查App的連接方式,,好處是不需要檢查所有的連接,,節(jié)省大量的系統(tǒng)資源。
b.對流量敏感性 非Wi-Fi環(huán)境下用戶使用手機(jī)流量上網(wǎng),,對流量敏感,。 2).協(xié)議a.協(xié)議要求- 簡化系統(tǒng)設(shè)計
- 提供可靠高效的消息傳輸
- 易于擴(kuò)展需求
b.常用協(xié)議- XMPP:基于XML的消息協(xié)議,曾被廣泛應(yīng)用于Gtalk,、Facebook的聊天服務(wù)
優(yōu)點(diǎn):XAMP協(xié)議有大量網(wǎng)絡(luò)資料和成熟開源模塊,,易于移動端集成。 缺點(diǎn):耗費(fèi)流量 舉例子:XMPP添加好友
<iq id="rostersetl" type="set">
<query XMLns="jabber:iq:roster">
<item jid="[email protected]" name="user"/>
</query>
</iq>
<presence from="[email protected]" to[email protected] type="subscribe"> - MQTT:IBM開發(fā)的聊天協(xié)議,,一個簡單的消息協(xié)議
- 類ActivitySync:微信實(shí)現(xiàn)的協(xié)議,,省流量,,性能高,私有協(xié)議,,IM所有功能自己實(shí)現(xiàn),。
c.常用開源服務(wù)器- Ejobberd:Erlang語言開發(fā),成熟穩(wěn)定,,支持集群,,支持多進(jìn)程、并發(fā),。
- Openfire:Java語言開發(fā),,成熟穩(wěn)定,插件多,,但對內(nèi)存要求高,,并發(fā)低,集群性能弱,。
d.tcp協(xié)議粘包問題及解決方案- 問題的由來:App的通信協(xié)議一般使用TCP協(xié)議,,接收端存在緩沖區(qū)。
- 問題案例:假設(shè)用戶緩存區(qū)大小為15字節(jié),,當(dāng)?shù)谝粋€數(shù)據(jù)包(10字節(jié))和第二個數(shù)據(jù)包(10個字節(jié))到達(dá)接收端,,根據(jù)緩沖區(qū)大小取數(shù)據(jù)15字節(jié),這時候取出數(shù)據(jù)包含第一個數(shù)據(jù)包和第二個數(shù)據(jù)包,,產(chǎn)生粘包問題,。(例如第一個數(shù)據(jù)包為"1234567890",第二個數(shù)據(jù)包為"0123456789"),取出數(shù)據(jù)為"123456789001234"
- 解決方案:制定合理的協(xié)議格式,,在每個數(shù)據(jù)包中標(biāo)明包的長度,。
one. MySQL協(xié)議格式 MySQL協(xié)議中一個數(shù)據(jù)包的前3個字節(jié)是數(shù)據(jù)包長度,第4個字節(jié)是數(shù)據(jù)包的序列號,,剩下字節(jié)是數(shù)據(jù),。
two. Redis 協(xié)議格式(Redis協(xié)議是以CR LF結(jié)尾的) Redis協(xié)議格式如下: *參數(shù)個數(shù) CR LF
$第一個參數(shù)的字符串占用字節(jié)數(shù) CR LF
參數(shù)數(shù)據(jù) CR LF
......
$第N個參數(shù)的字符串占用字節(jié)數(shù) CR LF
參數(shù)數(shù)據(jù) CR LF
舉例子(Redis命令:"set name jeff"): *3\r\n$3\r\n\set\r\n\$4\r\n\name\$4\r\n\jeff\r\n
e.丟包情況處理1.問題的由來:由于移動物聯(lián)網(wǎng)的弱網(wǎng)絡(luò)性,經(jīng)常出現(xiàn)丟包的情況,。 需要處理的問題: - 怎么確定客戶端是否接收消息?
- 怎么確定需要重發(fā)哪些消息?
2.解決方案: - 基于隊(duì)列的消息協(xié)議(二次握手)
傳統(tǒng)的IM協(xié)議是基于隊(duì)列的消息發(fā)送和反饋機(jī)制,。 服務(wù)器按照順序把隊(duì)列中的消息依次發(fā)給客戶端,當(dāng)客戶端收到消息后給服務(wù)端發(fā)送"確認(rèn)收到"的應(yīng)答,如果過了一段時間還沒有收到客戶端的應(yīng)答,就重發(fā)該消息。 這樣就存在兩個問題: one.如果客戶端已收到消息且發(fā)送了"確認(rèn)收到"的應(yīng)答,,網(wǎng)絡(luò)中斷,,造成服務(wù)器收不到應(yīng)答,服務(wù)器重發(fā),,導(dǎo)致客戶端收到重復(fù)的消息,。 two.每條消息都需要應(yīng)答極其費(fèi)時間,服務(wù)器要維護(hù)每個消息的狀態(tài)也容易出錯。 - 基于版本號的消息協(xié)議
f.非文本文件傳送- 發(fā)送圖片,、聲音等文件,,方法是先上傳到服務(wù)器,然后把URL采用純文本的方式發(fā)送數(shù)據(jù),。
- 實(shí)例說明:
3).后臺整體架構(gòu)設(shè)計a.連接層- 作用:保持App與服務(wù)層之間的連接,,把消息通過隊(duì)列轉(zhuǎn)發(fā)到邏輯層處理。
- 注意要點(diǎn):連接層服務(wù)器要很少重啟,,一旦重啟造成大量App斷線,,短時間內(nèi)重連服務(wù)器。服務(wù)器一瞬間大量連接請求引發(fā)類似DDOS攻擊的現(xiàn)象,。防止這類現(xiàn)象出現(xiàn)的機(jī)制:當(dāng)連接層服務(wù)器準(zhǔn)備重啟的時候,,發(fā)送一條消息到連接著這臺服務(wù)器的App,讓其重連到別的服務(wù)器,。
- 動態(tài)分配接入點(diǎn)方案:App訪問接入點(diǎn)服務(wù)器,,接入點(diǎn)服務(wù)器根據(jù)各個連接服務(wù)器的負(fù)載等因素,綜合計算,,返回一個連接服務(wù)器的IP給App,,App通過IP連接服務(wù)器。如果App因?yàn)榫W(wǎng)絡(luò)原因無法連接接入點(diǎn)服務(wù)器,,可以預(yù)埋IP,,讓App連接上默認(rèn)的連接服務(wù)器。
b.業(yè)務(wù)層- 驗(yàn)證模塊:驗(yàn)證用戶信息
- 路由模塊:連接服務(wù)器的集群,包含了數(shù)量眾多的服務(wù)器,,例如當(dāng)用戶A向用戶B發(fā)送消息,兩個用戶的連接不一定是一個服務(wù)器,,因此需要通過路由模塊判斷用戶所在的服務(wù)器,。群聊功能要在這個模塊實(shí)現(xiàn)"訂閱/發(fā)布"關(guān)系。
- 統(tǒng)計模塊:統(tǒng)計各種信息,,例如總連接數(shù),、每秒發(fā)送消息數(shù)、總用戶數(shù),、Android客戶端連接數(shù),、iOS客戶端連接數(shù)等等。
- 數(shù)據(jù)存儲模塊:存儲消息,、統(tǒng)計信息,、用戶身份信息等
- 連接層通過隊(duì)列向業(yè)務(wù)層進(jìn)行消息傳遞,業(yè)務(wù)層不斷從隊(duì)列中取出消息進(jìn)行相關(guān)的處理,,使用隊(duì)列后,,業(yè)務(wù)層重啟程序時,對用戶影響最小,因?yàn)樗幚淼南⒋鎯υ陉?duì)列中,,重啟不會造成消息丟失,。
c.持久層 該層提供數(shù)據(jù)存取服務(wù),數(shù)據(jù)包含用戶的身份信息,、消息,、統(tǒng)計信息等。 根據(jù)不同數(shù)據(jù)對存取速度的不同要求,,可使用不同的軟件存儲不同的業(yè)務(wù)數(shù)據(jù),。例如用戶身份信息這種高頻讀寫的數(shù)據(jù),可存儲在Redis,、Memcached等內(nèi)存數(shù)據(jù)庫中,。 d.工作流程(以老公向老婆發(fā)送"我愛你"為例)- 老公向連接服務(wù)器1發(fā)送消息(里面包含老婆的唯一標(biāo)識和內(nèi)容"我愛你")
- 連接服務(wù)器1接收該消息后向老公返回一個應(yīng)答,同時隊(duì)列把消息傳遞到業(yè)務(wù)層
- 業(yè)務(wù)層的驗(yàn)證模塊驗(yàn)證老公的身份信息,,驗(yàn)證完成后通過路由模塊把消息傳遞給老婆所在的服務(wù)器為連接服務(wù)器2,,把消息傳遞到連接服務(wù)器2從而發(fā)送到老婆的手機(jī)上
- 業(yè)務(wù)層把消息傳遞給老婆時,對相關(guān)的消息進(jìn)行統(tǒng)計,,同時數(shù)據(jù)存儲模塊把數(shù)據(jù)持久化
2.社交APP后臺架構(gòu)1).基本表結(jié)構(gòu)2).推拉模式 a.推模式b.拉模式c.推拉模式總結(jié)d.微博中的應(yīng)用3).數(shù)據(jù)庫架構(gòu)a.自增idb.分表分庫策略4).緩存架構(gòu)a.分布式緩存b.主從緩存c.防止緩存失效措施3.LBS APP后臺架構(gòu)1).地理坐標(biāo)處理及坐標(biāo)系認(rèn)識2).查找附件的人功能a.MySql空間數(shù)據(jù)庫b.geohash編碼c.MongoDB地理位置查詢功能3).基于MongoDB的LBS后臺架構(gòu)a.副本集架構(gòu)b.分片架構(gòu)4.推送服務(wù)器后臺架構(gòu)1). Android推送 (類似Gopush-Cluster架構(gòu))a.App連接推送服務(wù)器的流程b.后臺推送消息到App的流程c.App獲取離線消息的流程2).iOS推送(基于APNS)a.APNS原理b.APNS推送協(xié)議分析c.iOS推送服務(wù)器架構(gòu)未完待續(xù)
|