babel: yet another rpc, but far beyond rpc(中)

2 架構描述

簡單架構

從之前的描述,已經可以看出我們會採用RPC over MQ的方式做底層實現,類似方法調用的通信語義會在client和server兩端的庫中作封裝。

從後端實現來說,我們用三套後端來滿足不同的場景:

1 對大中型分散式系統環境,rabbitmq是非常非常好的支撐。本來以為需要自己做很多工作,但深入了解rabbitmq,尤其是其支持的amqp協議,發現其實前人在很多思路方面已經栽好樹了,比如一致性hash和跨機房等功能,都有相應的插件支撐。

所以,rabbitmq成為babel的第一選擇,可以實現我們規劃功能的全集,我們的SAAS平台都是使用的rabbitmq。

2 對少量機器而言,redis提供了非常輕量級的隊列支持,可以提供有限但必要的功能。

redis沒有類似amqp這樣的協議,需要手動作些封裝。我們在單機環境使用redis,儘可能減少部署和運維的開銷。

3 對性能有苛刻要求的可以用zeromq後端去做tcp直連。前兩種mq的方式畢竟會多幾跳中轉,但在路由的靈活性和通訊語義的提供更豐富的選擇,而且在大數據量的處理上,吞吐量和平均延時並不會比直連差很多。

但為了滿足特殊環境的需要,我們預留了zeromq的實現選擇,最近由於新的需求,正在準備完成這塊拼圖。zeromq的缺點在於需要中央配置系統來幫忙完成路由功能。

每種後台實現對使用者透明,可以通過配置進行透明切換,但是有些高級通信語義redis和zeromq不支持。

如果對應到web service 三要素:

UDDI:傳統的rpc或者SOA都是去註冊中心發現遠端對象,然後客戶端主動推送數據到服務端。mq的方式幫我們省卻了自註冊(訂閱實現)和服務發現(mq自己路由)的問題。

WSDL:目前我們通過json的方式來描述rpc的service端,包括機房所在地,持久化,超時等等。

SOAP:目前使用json的方式,我們定義了一個統一的Event對象來封裝一些固定屬性,其他都在一個map中。由業務代碼自己去打包拆包。當然這種方式在大團隊中不適合。

通訊語義封裝

大量的工作可以利用mq來實現,我們的工作主要體現在通訊語義的封裝。

? client端訪問模式語義

  • queue語義(消息有去無回):傳統的數據輸送。
  • 簡單rpc(消息一去一回):傳統的rpc和soa都適用於此場景。
  • 輪詢rpc(消息一去多回):一個request出去,多個response回來,適合於輪詢下游節點的場景。
  • 分散式存儲rpc(一個request消息,只要有最小條件的response消息就返回):適合於分散式場景下的讀寫。例如三個拷貝,需要至少兩份讀成功或者至少兩份寫成功,等等。目前此方式我們還沒有用到。

? 消息分發語義(實際上這裡的行為參考了storm的部分功能)

  • Shuffle:一個消息,會有多個接收者,這些接收者根據自己的資源情況去搶佔同一來源的消息,達到load balance的目的。實際上我們通過shuffle來做集群功能,省掉了LB的引入。而且性能強的拉多點,性能弱的拉少點,變相的實現了根據消費者的性能來做分發。
  • Sharding:與shuffle類似,也是多個consumer來分享消息,不過根據消息的key,保證在拓撲環境不發生改變的情況下,同一個key始終指向同一個消費者,為後續分散式系統的搭建打下基礎
  • topic語義:所有消費者都會得到消息的一個拷貝。常見的mq語義
  • topic+shffule:一組消費者作為一個整體來訂閱topic,得到所有的消息,每個訂閱團體內部通過shuffle的形式去分攤。這種非常適合用大數據環境下,有不同類型的數據消費者,每一個類型的消費者有各自的實例數。
  • topic+sharding:一組消費者作為一個整體來訂閱topic,得到所有的消息,每個訂閱團體內部通過sharding的形式去分攤。類似於topic shuffle,只是換用了sharding這種更嚴格的語義。

? 數據的封裝語義。用於指定babel上承載數據的特徵,例如:

  • batch operation:用於指定是否進行批處理傳遞。
  • Security:暫無使用。
  • Compressing:指定payload壓縮方法,目前只做了gzip。
  • 機房:指定了機房所在地,框架會根據生產者和消費者的不同自動做跨機房的處理。
  • 持久化:指定在無消費者的情況下,是否需要持久化存儲,以及最大大小。
  • 超時:指定消息的最大有效時間,超過的消息將會被丟棄。
  • 其他

對於以上的通訊語義,首先需要去底層的mq基礎裡面找到相對應的設施來做封裝,比如對於queue語義作個簡單舉例:

而對於像rpc,輪詢,以及其他功能,則需要相應的代碼來支撐,比如:

· response的返回可以通過client監聽queue來實現

· response和request的串聯可以通過自定義的requestid來實現

· 輪詢可以通過client 端等待多個消息返回,可以用condition來做同步

· ……

這裡有不少細節,暫不在本文中進行展開了。

跨語言

由於幾種mq都有python和java的客戶端,所以我們工作會輕鬆很多,只是同樣的邏輯需要寫兩份,好處還是很明顯的,使得我們的系統語言無關,方便根據當前人員的技能情況來分配開發任務。

不過這裡不得不吐槽python的並發,雖然有心理準備,沒想到是如此之差。當使用多線程的時候,性能下降的厲害,比java要差兩個數量級,所以我們python版做了同步(多線程),非同步(協程)兩個版本。非同步版本的性能尚可接受;我們已經準備在build自己的非同步python框架,來覆蓋我們的應用程序。

跨機房通信

Babel的一大特色是跨機房通信,來幫助我們解決不同數據中心的通信問題,使得業務開發人員只用關心其所負責的業務即可。跨機房的通信和本機房的通信有所不同:

本地機房的通信講究高吞吐量,rpc類訪問會要求低延時。

跨機房通信必須應對複雜的網路情況,要求數據不丟,rpc類通信可以接受相對較高的延時。

實現上,我們利用了federation插件,當rpc框架發現存在跨機房訪問時,會自動啟用相應的路由,下圖是同事畫的兩種情況下的路由,綠線是本地調用,紅線是跨機房調用。

對於業務應用而言,使用上是基本透明的,藉助於mq的中轉,在多機房環境下它也可以玩轉除數據推送外的RPC類訪問語義。

3實戰舉例

實例

1分散式數據計算平台

首先是我們的私有化大數據平台warden。warden集數據採集、轉換、分發、實時分析和展示等功能於一體,希望從客戶的原始網路流量中找出異常點和風險事件。

此圖是一個warden分散式版本的草圖:

1. 採集的數據通過topic sharding類分配給不同類型的消費者,比如ES writer,Mysql writer,實時分析引擎;每種消費者可以有不同的實例數。

2. 實時計算引擎通過sharding來分攤流量,達到scale out的效果。

3. rule引擎需要數據的時候同樣通過簡單的sharding的rpc就可以獲得相應的數據了。

4. 規則引擎的結果可以通過topic來進行再分發。

5. 目前只有實時引擎是java的,因為性能要求苛刻,其他模塊採用python開發。

上圖只是個例子,來簡要說明babel是如何支撐一個分散式數據計算系統的。實際的系統使用了更多的語義,也更加的複雜。不過藉助於Babel的協助,整個系統在實現和運維上已經很大程度上減輕了複雜程度。

2水平擴展的web系統

第二個例子是我們曾經做過的SAAS平台私有化案例,是我們早期SAAS平台的極簡版本。

圖畫的略凌亂了些:

1. 系統主架構是用haproxy做負載均衡,發到我們的兩台主機上。

2. 兩台主機內部完全相同,右邊主機內部組件沒有畫全。

3. 每台主機有內部的nginx,load balance到本機器內部的諸多python web server 上。

4. python web server直接讀取本地的nosql資料庫。

5. 寫數據時,由於寫請求sharding到兩台機器上,所以我們有個topic的service來處理nosql數據寫入,保證每個寫入操作都寫到兩台機器上,每台機器的nosql始終存有全量的最新數據。

6. 由於客戶要求落地關係型資料庫,所以通過shuffle再將寫請求分散開,統一寫入mysql中。

在這個系統中,我們成功的利用babel建立了自己的一致性框架,從而避免了去使用db做數據一致性;同時由於對等的伺服器架構,在部署維護上省掉了很多事情。

框架運維

整個框架,我們都準備了統一的metrics體系去做監控和報警(實際上metrics系統本身的跨機房屬性反而是通過babel來實現),詳盡的監視了RPC的某個環節,之前有過我們監控的文章,這裡就不重複了。


推薦閱讀:

為什麼手機收不到4G信號的時候總是可以收到3G2G信號?
指環手機,開啟隱形通訊時代!
Y2T39 為何高溫靈敏度會劣化,論靈敏度與接收機雜訊
通信領域的科學大廈是否已經蓋好?上面還有令人不安的烏雲嗎?
為什麼不用wifi取代3G?

TAG:RPC框架 | 通讯技术 |