分布式系統(tǒng)設(shè)計(jì)的求生之路

作者:Simon,騰訊后臺(tái)開發(fā)高級(jí)工程師

WeTest導(dǎo)讀

分布式系統(tǒng)理念漸漸成為了后臺(tái)架構(gòu)技術(shù)的重要選擇,本文介紹了作者在手游領(lǐng)域?qū)Ψ植际较到y(tǒng)進(jìn)行的種種嘗試,并在嘗試中制定了對(duì)服務(wù)的定義、整體框架的構(gòu)建以及服務(wù)內(nèi)部拆分的流程。

前言

業(yè)務(wù)規(guī)模不斷擴(kuò)大,對(duì)穩(wěn)定性、擴(kuò)展性的要求不斷提高,推動(dòng)了后臺(tái)架構(gòu)技術(shù)的不斷革新。面對(duì)日益復(fù)雜的需求,分布式系統(tǒng)的理念也逐漸深入到后臺(tái)開發(fā)者的骨髓。2013年,借著手游熱潮我對(duì)分布式系統(tǒng)開始嘗試。在近三年的摸爬滾打中,踩過(guò)不少坑,也從業(yè)界技術(shù)發(fā)展中吸取一些經(jīng)驗(yàn),逐漸形成了目前的設(shè)計(jì)思路。這里和大家分享點(diǎn)心得,不敢奢談?dòng)卸啻髤⒖純r(jià)值,權(quán)當(dāng)拋磚引玉吧。

1.?失敗的首次嘗試

最初考慮使用分布式的出發(fā)點(diǎn)很簡(jiǎn)單:解決端游開發(fā)時(shí)單點(diǎn)結(jié)構(gòu)導(dǎo)致容災(zāi)、擴(kuò)容困難的問(wèn)題。一種樸素的想法就是將相同功能的進(jìn)程作為一個(gè)整體對(duì)外提供服務(wù)。這里簡(jiǎn)要描述下基本框架:

這種架構(gòu)提供了三個(gè)基本組件:

Client?API,?服務(wù)請(qǐng)求者API:

從?Cluster?Center?Server?獲取服務(wù)提供者地址

向Server集群內(nèi)所有實(shí)例注冊(cè),注冊(cè)成功則認(rèn)為可用

通過(guò)負(fù)載均衡算法,選擇一個(gè)Server實(shí)例通信

檢測(cè)Server集群內(nèi)各實(shí)例的運(yùn)行狀態(tài)

Server?API,?服務(wù)提供者API:

向?Cluster?Center?Server?上報(bào)自己的狀態(tài)、訪問(wèn)地址等

接收?Client?API?的注冊(cè),并提供服務(wù)

向已經(jīng)注冊(cè)成功的Client定時(shí)匯報(bào)狀態(tài)

Cluster?Center?Server,?集群中心進(jìn)程:

接收?Server?Cluster?上報(bào),確定服務(wù)集群的結(jié)構(gòu),以及各實(shí)例的狀態(tài)

接收?Client?Cluster?的請(qǐng)求,返回可用服務(wù)集群列表

這種架構(gòu)具備了集群的基本雛形,可以滿足容災(zāi)擴(kuò)容的基本需求,大家應(yīng)該也發(fā)現(xiàn)不少問(wèn)題,我這里總結(jié)幾點(diǎn):

1.?服務(wù)發(fā)現(xiàn)的蹩腳實(shí)現(xiàn)

Cluster?Center?Server?的實(shí)現(xiàn)是單點(diǎn),出現(xiàn)故障時(shí)Client請(qǐng)求會(huì)異常;沒(méi)有提供監(jiān)控機(jī)制,Client只能通過(guò)定時(shí)請(qǐng)求來(lái)獲取服務(wù)的最新狀況。

2.?CS采用Request/Response的通信方式不靈活

現(xiàn)實(shí)應(yīng)用中,服務(wù)往往存在相互請(qǐng)求,一應(yīng)一答遠(yuǎn)遠(yuǎn)不夠,全雙工?是必須要支持的。

3.?有瑕疵的?;顧C(jī)制

Server對(duì)Client定期單邊心跳,有兩個(gè)問(wèn)題:不同Client對(duì)?;钜罂赡懿煌?,有些5s,有些可能1s,如果心跳發(fā)起全部在Server,無(wú)法滿足差異化要求;服務(wù)端作為被動(dòng)方,承擔(dān)監(jiān)控請(qǐng)求者存活的責(zé)任不明智。

4.?架構(gòu)設(shè)計(jì)的層次不清晰

對(duì)架構(gòu)的層次、模塊劃分沒(méi)有作出很好的規(guī)劃,比如通信底層、服務(wù)發(fā)現(xiàn)、集群探測(cè)與?;畹鹊葲](méi)有清晰定義接口,導(dǎo)致相互耦合,替換、維護(hù)較為困難。

2.?看看外面的世界

上述問(wèn)題,歸根結(jié)底還是眼界狹窄,自己悶頭造輪子沒(méi)跟上業(yè)界技術(shù)發(fā)展的步伐。近幾年微服務(wù)架構(gòu)發(fā)展迅速,相比傳統(tǒng)面向服務(wù)架構(gòu)不再過(guò)分強(qiáng)調(diào)企業(yè)服務(wù)總線,而是深入到單個(gè)業(yè)務(wù)系統(tǒng)內(nèi)部的組件化。這里我介紹下自己的調(diào)研結(jié)果。

2.1?服務(wù)協(xié)同

服務(wù)協(xié)同是分布式系統(tǒng)一個(gè)核心組成部分,概述為:多個(gè)進(jìn)程節(jié)點(diǎn)作為整體對(duì)外提供服務(wù),服務(wù)可以相互發(fā)現(xiàn),服務(wù)關(guān)注者可以及時(shí)獲取被關(guān)注者的變化以完成協(xié)作。具體運(yùn)行過(guò)程包括:服務(wù)注冊(cè)?和?服務(wù)發(fā)現(xiàn)。在實(shí)現(xiàn)上涉及以下方面:

統(tǒng)一命名?對(duì)服務(wù)以及其中的節(jié)點(diǎn),進(jìn)行集中式、統(tǒng)一命名,便于相互區(qū)分和訪問(wèn)。

監(jiān)控?確定服務(wù)的可用性和狀態(tài),當(dāng)服務(wù)狀態(tài)變化時(shí),關(guān)注者要有途徑獲知。

訪問(wèn)策略?服務(wù)通常包含多個(gè)節(jié)點(diǎn),以集群形式存在,Client在每次請(qǐng)求時(shí)需要策略確定通信節(jié)點(diǎn),策略目標(biāo)可能是多樣的,比如?負(fù)載均衡?,穩(wěn)定映射?等等。

可用性?容災(zāi)處理,動(dòng)態(tài)擴(kuò)容。

業(yè)界中較為成熟的實(shí)現(xiàn)如下表所示:

2.2?消息中間件

亦稱消息隊(duì)列,在分布式系統(tǒng)廣泛使用,在需要進(jìn)行網(wǎng)絡(luò)通信的節(jié)點(diǎn)間建立通道,高效可靠地進(jìn)行平臺(tái)無(wú)關(guān)的數(shù)據(jù)交流。架構(gòu)上主要分為兩種:Broker-Based(代理),和?Brokerless(無(wú)代理)。前者需要部署一個(gè)消息轉(zhuǎn)發(fā)的中間層,提供二次處理和可靠性保證。后者輕量級(jí),直接在內(nèi)嵌在通信節(jié)點(diǎn)上。業(yè)界較為成熟的實(shí)現(xiàn)如下表所示:

2.3?通信協(xié)議數(shù)據(jù)格式

服務(wù)間通信,需要將數(shù)據(jù)結(jié)構(gòu)/對(duì)象和傳輸過(guò)程中的二進(jìn)制流做相互轉(zhuǎn)化,一般稱為?序列化/反序列化?。不同編程語(yǔ)言或應(yīng)用場(chǎng)景,對(duì)數(shù)據(jù)結(jié)構(gòu)/對(duì)象的定義和實(shí)現(xiàn)是不同的。在選擇時(shí)需要考慮以下方面:

通用性?是否支持跨平臺(tái)、跨語(yǔ)言;業(yè)界是否廣泛流行或者支持

可讀性?文本流有天然優(yōu)勢(shì),純粹二進(jìn)制流如果沒(méi)有便捷可視化工具,調(diào)試將會(huì)異常痛苦

性能?空間開銷——存儲(chǔ)空間的占用;時(shí)間開銷——序列化/反序列化的快慢

可擴(kuò)展性?業(yè)務(wù)的不變之道就是——一直在變,必須具有處理新舊數(shù)據(jù)之間的兼容性的能力

實(shí)現(xiàn)?序列化/反序列化?的組件一般包含:IDL(Interface?Description?Language),?IDL?Compiler,?Stub/Skeleton。業(yè)界目前比較流行的序列化協(xié)議有:XML,?JSON,?ProtoBuf,?Thrift,?Avro等。關(guān)于這幾種協(xié)議的實(shí)現(xiàn)以及比較,可以參考文章?《序列化和反序列化》。這里將原文中的選型結(jié)論摘錄給大家:

允許高延遲比如100ms以上,內(nèi)容變更頻繁,且復(fù)雜的業(yè)務(wù),可以考慮基于XML的SOAP協(xié)議。

基于Web?browser的Ajax,以及Mobile?app與服務(wù)端之間的通訊;對(duì)于性能要求不太高,或者以動(dòng)態(tài)類型語(yǔ)言為主的場(chǎng)景,JSON可以考慮。

對(duì)性能和簡(jiǎn)潔性有極高要求的場(chǎng)景,Protobuf,Thrift,Avro都差不多。

對(duì)于Terabyte級(jí)別數(shù)據(jù)持久化應(yīng)用場(chǎng)景,Protobuf和Avro是首要選擇。持久化后的數(shù)據(jù)若存儲(chǔ)在Hadoop子項(xiàng)目里,或以動(dòng)態(tài)類型語(yǔ)言為主,Avro會(huì)是更好的選擇;非Hadoop項(xiàng)目,以靜態(tài)類型語(yǔ)言為主,首選Protobuf。

不想造?RPC?的輪子,Thrift可以考慮。

如果序列化之后需要支持不同的傳輸層協(xié)議,或者需要跨防火墻訪問(wèn)的高性能場(chǎng)景,Protobuf可以優(yōu)先考慮。

3.?重整旗鼓

調(diào)研周邊后,2015年開搞第二款手游,吸取之前的教訓(xùn),這次設(shè)計(jì)的基本原則是:

系統(tǒng)拆分、解耦,清晰定義系統(tǒng)間接口,隱藏系統(tǒng)內(nèi)部實(shí)現(xiàn)

大框架盡可能通用,子系統(tǒng)可在不同場(chǎng)景替換

下面首先對(duì)服務(wù)定義,然后介紹整體框架和服務(wù)內(nèi)部拆分。

3.1?服務(wù)定義

舉個(gè)手游的例子,看圖說(shuō)話:

Service?Cluster?服務(wù)集群,由功能相同的實(shí)例組成,作為整體對(duì)外服務(wù),是一個(gè)集合。比如?Lobby?提供大廳服務(wù),Battle?提供戰(zhàn)斗服務(wù),Club?提供工會(huì)服務(wù),Trade?提供交易服務(wù)。

Service?Instance?服務(wù)實(shí)例,提供某種服務(wù)功能的最細(xì)粒度,以進(jìn)程形式存在。比如Club?集群中有兩個(gè)實(shí)例?3.2.6.1?和?3.2.6.2?,功能一致。

Service?Node?服務(wù)節(jié)點(diǎn),是服務(wù)發(fā)現(xiàn)組件管理的基本單元,可以是集群、實(shí)例、層次關(guān)系或者業(yè)務(wù)關(guān)心的含義。

Service?Key?服務(wù)節(jié)點(diǎn)的Key,全局唯一的身份標(biāo)記。key的設(shè)計(jì)需要能夠體現(xiàn)出層級(jí)關(guān)系,至少要能夠體現(xiàn)出?Cluster?和?Instance?的包含關(guān)系。etcd和zookeeper均支持key層次化的組織關(guān)系,類似文件系統(tǒng)的樹形結(jié)構(gòu)。etcd有mkdir直接建立目錄,zookeeper則通過(guò)路徑描述父子關(guān)系。但不管怎么都可以在概念層次使用路徑結(jié)構(gòu)。

上圖中,Service?Instance?完整路徑可描述為:/AppID/Area/Platform/WorldID/GroupID/ClusterName/InstanceName。有以下特點(diǎn):

集群路徑一定是其中各個(gè)實(shí)例的父路徑

從功能完整性而言,集群是服務(wù)的基本粒度

相同功能的集群在不同前綴路徑下含義不同,服務(wù)目標(biāo)也可以不同,比如:

/Example/wechat/android/w_1/g_1/Lobby?和/Example/wechat/android/w_3/g_2/Lobby?功能上均表示大廳服務(wù),但一個(gè)為大區(qū)1分組1服務(wù),一個(gè)為大區(qū)3分組2服務(wù)

3.2?服務(wù)發(fā)現(xiàn)基本流程

先抽象幾個(gè)基本操作,不同服務(wù)發(fā)現(xiàn)組件的API可能略有差異,但應(yīng)該有對(duì)應(yīng)功能:

Create?在服務(wù)發(fā)現(xiàn)組件中創(chuàng)建?Key?對(duì)應(yīng)的?Service?Node,指定全局唯一的標(biāo)記。

Delete?在服務(wù)發(fā)現(xiàn)組件中刪除?Key?對(duì)應(yīng)的節(jié)點(diǎn)。

Set?設(shè)置?Key?對(duì)應(yīng)的?Value,?安全訪問(wèn)策略或者節(jié)點(diǎn)基礎(chǔ)屬性等。

Get?根據(jù)?Key?獲取對(duì)應(yīng)節(jié)點(diǎn)的數(shù)據(jù),如果是父節(jié)點(diǎn)可以獲取其子節(jié)點(diǎn)列表。

Watch?對(duì)節(jié)點(diǎn)設(shè)置監(jiān)視器,當(dāng)該節(jié)點(diǎn)自身,以及嵌套子節(jié)點(diǎn)數(shù)據(jù)發(fā)生變更時(shí),服務(wù)發(fā)現(xiàn)組件將變更事件主動(dòng)通知給監(jiān)視者。

Service?Instance每次在啟動(dòng)時(shí),按照下面的流程處理:

生成自己的?Service?Path,注意這是服務(wù)實(shí)例的路徑。

以?Service?Path?為key,通過(guò)?Create?方法生成節(jié)點(diǎn),Set?數(shù)據(jù):對(duì)外開放的地址、安全訪問(wèn)策略等。

生成需要訪問(wèn)的服務(wù)集群的?Service?Path,通過(guò)?Get?方法獲取集群數(shù)據(jù),如果找不到說(shuō)明該服務(wù)不存在;如果可以找到分兩種情況:

該路徑下沒(méi)有子節(jié)點(diǎn)。說(shuō)明當(dāng)前不存在可用的服務(wù)實(shí)例,對(duì)集群路徑設(shè)置watcher,等待新的可用實(shí)例。

該路徑下有子節(jié)點(diǎn)。那么?Get?所有子節(jié)點(diǎn)列表,并進(jìn)一步?Get?子節(jié)點(diǎn)訪問(wèn)方式和其它數(shù)據(jù)。同時(shí)設(shè)置?watcher?到集群路徑,檢測(cè)集群是否存在變化,比如新增或減少實(shí)例等。

Service?Instance在關(guān)閉時(shí),按照下面的流程處理:

通過(guò)?Delete?方法刪除自己對(duì)應(yīng)的節(jié)點(diǎn)。有些服務(wù)發(fā)現(xiàn)組件可以在實(shí)例生命周期結(jié)束時(shí)自行刪除,比如zookeeper的臨時(shí)節(jié)點(diǎn)。對(duì)于etcd的目錄,或者zookeeper的父路徑,如果非空,是無(wú)法刪除的。

根據(jù)上面的抽象可以定義?服務(wù)發(fā)現(xiàn)?的基本接口,接口的具體實(shí)現(xiàn)可以針對(duì)不同的組件開發(fā)不同的wrapper,但可以和業(yè)務(wù)解耦。

3.3?服務(wù)架構(gòu)

所有的架構(gòu)歸根結(jié)底還是需要具體到進(jìn)程層次實(shí)現(xiàn)的。目前我們項(xiàng)目開發(fā)的分布式架構(gòu)組件稱之為?DMS(Distributed?Messaging?System),以?DMS?Library?的形式提供,集成該庫(kù)即可實(shí)現(xiàn)面向服務(wù)的分布式通信。下面是?DMS?設(shè)計(jì)的總體結(jié)構(gòu):

關(guān)于Serialize/DeSerialize,?APP業(yè)務(wù)的選擇自由度較高,下面介紹其它Layer的具體實(shí)現(xiàn):

3.3.1?Message?Middleware

消息中間件前面介紹有很多選擇。DMS?使用的是?ZeroMQ,出發(fā)點(diǎn)是:輕量級(jí)、性能強(qiáng)大、偏底層所以靈活而且可控性較高。由此帶來(lái)的成本是,高級(jí)應(yīng)用場(chǎng)景需要做不少二次開發(fā),而且長(zhǎng)達(dá)80多頁(yè)的資料也需要不少時(shí)間。介紹ZeroMQ的文章太多,這里不打算科普,所以直接給出設(shè)計(jì)方案。

通信模式的選擇

ZeroMQ的Socket有多種類型,不同組合可以形成不同的通信模式,列舉幾種常見的:

REQ/REP?一應(yīng)一答,有請(qǐng)求必須等待回應(yīng)

PUB/SUB?發(fā)布訂閱

PUSH/PULL?流水線式處理,上游推數(shù)據(jù),下游拉數(shù)據(jù)

DEALER/ROUTER?全雙工異步通信

看到這里,大家可能會(huì)覺(jué)得選擇PUB/SUBDEALER/ROUTER應(yīng)該可以滿足絕大部分應(yīng)用場(chǎng)景吧。實(shí)際上DMS只使用了一種socket類型,那就是ROUTER,通信模式只有一種ROUTER/ROUTER。一種socket,一種通信模式,聽起來(lái)很簡(jiǎn)單,但真可以滿足要求嗎?

DEALER/ROUTER?是傳統(tǒng)異步模式,一方connect,一方bind。前端如果要連接多個(gè)后端就得建立多個(gè)socket。在前面描述的集群服務(wù)模式下,一個(gè)節(jié)點(diǎn)既會(huì)作為Client也會(huì)作為Server,會(huì)有多條入邊(被動(dòng)接收連接)和出邊(主動(dòng)發(fā)起連接)。這正好就是路由的概念,一個(gè)ROUTER?socket可以建立多條通路,并對(duì)每條通路發(fā)送或者接收消息。

PUB/SUB?注重的是擴(kuò)展性和規(guī)模,按照Z(yǔ)eroMQ作者的意思當(dāng)每秒鐘需要向上千的節(jié)點(diǎn)廣播百萬(wàn)條消息時(shí),你應(yīng)該考慮使用?PUB/SUB?。好吧,可預(yù)見的將來(lái)業(yè)務(wù)規(guī)??峙逻€到達(dá)不到這種程度,現(xiàn)在先把簡(jiǎn)單放在第一位吧。

3.3.2?DMS?Protocol

消息結(jié)構(gòu)

DMS的協(xié)議實(shí)現(xiàn)集群管理,消息轉(zhuǎn)發(fā)等基本功能。ZeroMQ的消息可以由?Frame?組成,一個(gè)Frame可以為空也可以是一段字節(jié)流,一個(gè)完整的消息可以包含多個(gè)Frame,稱為Multipart?Message。基于這種特點(diǎn),在DMS定義協(xié)議,可以將內(nèi)容拆分為不同的基本單元,每個(gè)單元用一個(gè)Frame描述,通過(guò)單元組合表示不同的含義。這與傳統(tǒng)方式:一條協(xié)議就是一個(gè)結(jié)構(gòu)體,不同單元組合需要定義為一個(gè)結(jié)構(gòu)體的方式相比更加靈活。

下面來(lái)看看DMS?Protocol的基本組成。首幀一定是對(duì)端ID。對(duì)端接收后也一定會(huì)獲取信息發(fā)送端的ID。第二幀包含DMS控制信息。第三、第四幀等全部是業(yè)務(wù)自定義的傳輸信息,僅對(duì)REQ-REP有效:

PIDF有兩層含義:所在服務(wù)集群的標(biāo)記,自身的實(shí)例標(biāo)記。這些標(biāo)記與Service?Discovery關(guān)于節(jié)點(diǎn)key的定義保持一致,有兩種形式?字符串?與?整型,前者可讀方便理解,后者是前者的Hash,提高傳輸效率。使用偽代碼來(lái)描述PIDF,大概是下面的樣子:

PIDF中的?ClusterID?和?InstanceID?各種取值,會(huì)有不同的通信行為:

在連接首次建立時(shí),還需要將可讀的服務(wù)路徑傳輸給對(duì)端:

協(xié)議命令字

DMS協(xié)議全部在每個(gè)消息的第二幀即Control?Frame中實(shí)現(xiàn)。命令字定義為:

通信流程——建立連接

通過(guò)Service?Discovery找到server后不要立即連接,而是發(fā)送探測(cè)包。原因有以下幾點(diǎn):

服務(wù)發(fā)現(xiàn)雖然可以反映節(jié)點(diǎn)是否存活,但一般有延遲,所以從服務(wù)發(fā)現(xiàn)獲取的節(jié)點(diǎn)僅僅是候選節(jié)點(diǎn)。

網(wǎng)絡(luò)底層機(jī)制差異較大,有些基于連接,比如raw?socket,有些沒(méi)有連接,比如shared?memory。最好在高層協(xié)議中解決連接是否成功。這就好比聲納,投石問(wèn)路,有回應(yīng)說(shuō)明可以連接,沒(méi)有回應(yīng)說(shuō)明目前連接不可用。

通信流程——業(yè)務(wù)消息發(fā)送

普通消息?若?PIDF?表示對(duì)端實(shí)例和當(dāng)前進(jìn)程直接連接,那么發(fā)送消息

路由消息?若?PIDF?表示對(duì)端實(shí)例和當(dāng)前進(jìn)程沒(méi)有直接連接,那么可以通過(guò)直連的實(shí)例轉(zhuǎn)發(fā)。路由機(jī)制?后文會(huì)介紹

廣播消息?若?PIDF?InstanceID為負(fù)數(shù),則向指定集群內(nèi)所有實(shí)例廣播

路由?和?廣播?是可以混合使用的。上述過(guò)程?DMS?自動(dòng)完成,業(yè)務(wù)不必參與,但可以截獲干預(yù)。

通信流程——?;顧C(jī)制

建立連接后,請(qǐng)求者會(huì)持續(xù)按照自己的間隔向服務(wù)者發(fā)送探測(cè)包。如果請(qǐng)求者連續(xù)若干次沒(méi)有收到服務(wù)者的PONG回包,則請(qǐng)求者認(rèn)為與服務(wù)者的連接已經(jīng)斷開。

如果服務(wù)者收到請(qǐng)求者的任何數(shù)據(jù)包,認(rèn)為請(qǐng)求者存活,如果超出一定時(shí)間沒(méi)有收到(含PING),則認(rèn)為請(qǐng)求者掉線。這個(gè)超時(shí)時(shí)間包含在READY協(xié)議中,由請(qǐng)求者告知服務(wù)者。

通信流程——連接斷開

任何一方收到?DISCONNECT?后,即認(rèn)為對(duì)方主動(dòng)斷開連接,不要再主動(dòng)向?qū)Ψ竭M(jìn)行任何形式的通信。

3.3.3?DMS?Kernel

下面介紹?DMS?Kernel?如何根據(jù)?DMS?Protocol?實(shí)現(xiàn)相關(guān)邏輯,并如何與業(yè)務(wù)交互。

SERVICE MANAGER

self?確定自身?服務(wù)路徑,實(shí)現(xiàn)服務(wù)注冊(cè),以及與目標(biāo)通信鏈路的注冊(cè),供路由表使用

targets?獲取并監(jiān)控目標(biāo)服務(wù)的數(shù)據(jù)以及運(yùn)行狀態(tài)

ACL?訪問(wèn)控制管理

對(duì)服務(wù)發(fā)現(xiàn)層接口進(jìn)行封裝,不同的?SERVICE?DISCOVERY?功能可能有所不同

ROUTER?MANAGER

每個(gè)服務(wù)實(shí)例在主動(dòng)成功連接對(duì)端服務(wù)后,通過(guò)?SERVICE?MANAGER?將連接以邊的形式寫入到?SERVICE?DISCOVERY?中,這樣就會(huì)以?鄰接邊?的形式生成一張完整的圖結(jié)構(gòu),也就是routing?table。比如:?Service?1?和?Service?2,Service?3,Service?4?均有連接,那么將邊(1,2),(1,3),(1,4)?記錄下來(lái)。SERVICE?DISCOVERY?關(guān)于路由鄰接鏈表的記錄可以使用公共的key,比如:?/AppID/Area/Platform/routing_table?。然后所有的服務(wù)實(shí)例都可以更新、訪問(wèn)該路徑以便獲得一致的路由表?;A(chǔ)功能有兩個(gè):

Updater?用于向路由表中添加邊,刪除邊,設(shè)置邊的屬性(比如權(quán)重),并對(duì)邊的變化進(jìn)行監(jiān)控

Calculator?根據(jù)鄰接邊形成的?圖結(jié)構(gòu)?計(jì)算路由,出發(fā)點(diǎn)是當(dāng)前實(shí)例,給定目標(biāo)點(diǎn)判斷目標(biāo)是否可達(dá),如果可達(dá)確定路徑并傳輸給下一個(gè)節(jié)點(diǎn)轉(zhuǎn)發(fā)。默認(rèn)選擇?Dijkstra?算法,業(yè)務(wù)可以定制。

CONNECTION?MANAGER

管理?Frontends?即前端請(qǐng)求進(jìn)入的連接,和?Backends?即向后端主動(dòng)發(fā)起的連接。Backends的目標(biāo)來(lái)源于?Service?Manager。

Sentinel?對(duì)前端發(fā)起的連接,通過(guò)?READY?協(xié)議,可以獲取該連接的失活標(biāo)準(zhǔn),并通過(guò)前端主動(dòng)包來(lái)判斷進(jìn)入連接是否存活。如果失活,將該連接置為斷開狀態(tài),不再向?qū)?yīng)前端主動(dòng)發(fā)包。

Prober?對(duì)后端服務(wù)進(jìn)行連接建立和連接保活。

Dispatcher?消息發(fā)送時(shí)用于確定通信對(duì)端實(shí)例。連接是基于實(shí)例的,但是業(yè)務(wù)一般都是面向服務(wù)集群的,所以Dispathcer?需要實(shí)現(xiàn)一定的分配機(jī)制,將消息轉(zhuǎn)發(fā)給?服務(wù)集群中的某個(gè)?具體實(shí)例?。注意這里僅只存在直接連接的單播。分配時(shí)應(yīng)考慮?負(fù)載均衡?默認(rèn)使用一致性哈希算法,業(yè)務(wù)完全可以根據(jù)具體應(yīng)用場(chǎng)景自定義。

3.3.4?DMS?Interface

DMS?API?是DMS對(duì)業(yè)務(wù)提供的服務(wù)接口,可以管理服務(wù)、通信等基本功能;

DMS?APP?Interface?是DMS要求業(yè)務(wù)必須實(shí)現(xiàn)的接口比如:Dispatcher?的負(fù)載均衡策略,對(duì)端服務(wù)狀態(tài)變化通知,以及業(yè)務(wù)自定義?路由算法?等等。

3.4?應(yīng)用場(chǎng)景

下面羅列DMS三大類典型應(yīng)用場(chǎng)景,其它場(chǎng)景應(yīng)該可以通過(guò)這三個(gè)例子組合實(shí)現(xiàn):

無(wú)Broker通信

最基礎(chǔ)的通信方式——兩個(gè)集群之間的?Instance?全連接,適合服務(wù)數(shù)量不多、邏輯不復(fù)雜的簡(jiǎn)單業(yè)務(wù)。

Broker通信

對(duì)于一個(gè)內(nèi)部聚合的子系統(tǒng),可能包含N個(gè)服務(wù),這些服務(wù)之間相互存在較強(qiáng)的交互行為。如果使用無(wú)Broker模式可能有兩個(gè)問(wèn)題:鏈路過(guò)多:通信層的內(nèi)存占用較大;運(yùn)維維護(hù)困難;服務(wù)沒(méi)有解耦,直接依賴于對(duì)端的存在;

這時(shí)Broker集群可以承擔(dān)消息中轉(zhuǎn)的作用,而且可以完成一些集中式邏輯處理。注意這里Broker只是一個(gè)名字,通過(guò)?DMS?Library?可以直接實(shí)現(xiàn)。

Broker級(jí)聯(lián)通信

多個(gè)子系統(tǒng)相互通信,估計(jì)沒(méi)有設(shè)計(jì)者愿意把內(nèi)部細(xì)節(jié)完全暴露給對(duì)方,這時(shí)兩個(gè)Broker集群就相當(dāng)于門戶:首先可以實(shí)現(xiàn)內(nèi)部子系統(tǒng)相互通信,以及集中邏輯;其次,可以作為所處子系統(tǒng)的對(duì)外接口,屏蔽細(xì)節(jié)。這樣不同子系統(tǒng)只需通過(guò)各自的Broker集群對(duì)外提供服務(wù)即可。

總結(jié)

本文主要介紹了?DMS?的幾個(gè)基礎(chǔ)結(jié)構(gòu):服務(wù)發(fā)現(xiàn)、消息中間件以及通信架構(gòu)?;舅枷胧牵嚎蚣芊謱?、層級(jí)之間接口清晰定義,以便在不同場(chǎng)景下使用不同的具體實(shí)現(xiàn)進(jìn)行替換。其中?zookeeper,ZeroMQ?只是舉例說(shuō)明當(dāng)前的一種實(shí)現(xiàn)方式,在不同場(chǎng)景下可以選擇不同組件,只要滿足接口即可。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時(shí)請(qǐng)結(jié)合常識(shí)與多方信息審慎甄別。
平臺(tái)聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡(jiǎn)書系信息發(fā)布平臺(tái),僅提供信息存儲(chǔ)服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

友情鏈接更多精彩內(nèi)容