支撐微博千億調用的輕量級RPC框架:Motan

81365341 8年前發布 | 144K 次閱讀 RPC WEB服務/RPC/SOA

來自: http://h2ex.com/820

張雷,新浪微博技術專家,MotanRPC 框架技術負責人。2013 年加入新浪微博,作為核心技術成員參與微博 RPC 服務化、混合云等多個重點項目,當前負責 MotanRPC 框架的維護與架構改進。專注于高可用架構及服務中間件開發方向。

“2013 年微博 RPC 框架 Motan 在前輩大師們(福林、fishermen、小麥、王喆等)的精心設計和辛勤工作中誕生,向各位大師們致敬,也得到了微博各個技術團隊的鼎力支持及不斷完善,如今 Motan 在微博平臺中已經廣泛應用,每天為數百個服務完成近千億次的調用。” —— 張雷

隨著微博容器化部署以及混合云平臺的高速發展,RPC 在微服務化的進程中越來越重要,對 RPC 的需求也產生了一些變化。今天主要介紹一下微博 RPC 框架 Motan,以及為了更好的適應混合云部署所做的一些改進。

RPC 框架的發展與現狀

RPC(Remote Procedure Call)是一種遠程調用協議,簡單地說就是能使應用像調用本地方法一樣的調用遠程的過程或服務,可以應用在分布式服務、分布式計算、遠程服務調用等許多場景。說起 RPC 大家并不陌生,業界有很多開源的優秀 RPC 框架,例如 Dubbo、Thrift、gRPC、Hprose 等等。下面先簡單介紹一下 RPC 與常用遠程調用方式的特點,以及一些優秀的開源 RPC 框架。

RPC 與其它遠程調用方式比較

RPC 與 HTTP、RMI、Web Service 都能完成遠程調用,但是實現方式和側重點各有不同。

HTTP

HTTP(HyperText Transfer Protocol)是應用層通信協議,使用標準語義訪問指定資源(圖片、接口等),網絡中的中轉服務器能識別協議內容。HTTP 協議是一種資源訪問協議,通過 HTTP 協議可以完成遠程請求并返回請求結果。

HTTP 的 優點是簡單、易用、可理解性強且語言無關 ,在遠程服務調用中包括微博有著廣泛應用。HTTP 的缺點是協議頭較重,一般請求到具體服務器的鏈路較長,可能會有 DNS 解析、Nginx 代理等。

RPC 是一種協議規范,可以把 HTTP 看作是一種 RPC 的實現,也可以把 HTTP 作為 RPC 的傳輸協議來應用。RPC 服務的自動化程度比較高,能夠實現強大的服務治理功能,和語言結合更友好,性能也十分優秀。與 HTTP 相比,RPC 的缺點就是相對復雜,學習成本稍高。

RMI

RMI(Remote Method Invocation)是指 Java 語言中的遠程方法調用,RMI 中的每個方法都具有方法簽名,RMI 客戶端和服務器端通過方法簽名進行遠程方法調用。RMI 只能在 Java 語言中使用, 可以把 RMI 看作面向對象的 Java RPC

Web Service

Web Service 是一種基于 Web 進行服務發布、查詢、調用的架構方式,重點在于服務的管理與使用。Web Service 一般通過 WSDL 描述服務,使用 SOAP通過 HTTP 調用服務。

RPC 是一種遠程訪問協議,而 Web Service 是一種體系結構,Web Service 也可以通過 RPC 來進行服務調用,因此 Web Service 更適合同一個 RPC 框架進行比較。當 RPC 框架提供了服務的發現與管理,并使用 HTTP 作為傳輸協議時,其實就是 Web Service。

相對 Web Service,RPC 框架可以對服務進行更細粒度的治理,包括流量控制、SLA 管理等,在微服務化、分布式計算方面有更大的優勢。

RPC 框架介紹

RPC 協議只規定了 Client 與 Server 之間的點對點調用流程,包括 stub、通信協議、RPC 消息解析等部分,在實際應用中,還需要考慮服務的高可用、負載均衡等問題,所以這里的 RPC 框架指的是能夠完成 RPC 調用的解決方案,除了點對點的 RPC 協議的具體實現外,還可以包括服務的發現與注銷、提供服務的多臺 Server 的負載均衡、服務的高可用等更多的功能。 目前的 RPC 框架大致有兩種不同的側重方向,一種偏重于服務治理,另一種偏重于跨語言調用 。

服務治理型 RPC 框架

服務治理型的 RPC 框架有 Dubbo、DubboX 等,Dubbo 是阿里開源的分布式服務框架,能夠實現高性能 RPC 調用,并且提供了豐富的管理功能,是十分優秀的 RPC 框架。DubboX 是基于 Dubbo 框架開發的 RPC 框架,支持 REST 風格遠程調用,并增加了一些新的 feature。

這類的 RPC 框架的特點是功能豐富,提供高性能的遠程調用以及服務發現及治理功能, 適用于大型服務的微服務化拆分以及管理 ,對于特定語言(Java)的項目可以十分友好的透明化接入。但缺點是語言耦合度較高,跨語言支持難度較大。

跨語言調用型

跨語言調用型的 RPC 框架有 Thrift、gRPC、Hessian、Hprose 等,這一類的 RPC 框架重點關注于服務的跨語言調用,能夠支持大部分的語言進行語言無關的調用,非常適合于為不同語言提供通用遠程服務的場景。 但這類框架沒有服務發現相關機制 ,實際使用時一般需要代理層進行請求轉發和負載均衡策略控制。

微博的 RPC 框架 Motan 屬于服務治理類型,是一個基于 Java 開發的高性能的輕量級 RPC 框架,Motan 提供了實用的服務治理功能和優秀的 RPC 協議擴展能力。

Motan 提供的主要功能包括:

  • 服務發現 :服務發布、訂閱、通知
  • 高可用策略 :失敗重試(Failover)、快速失敗(Failfast)、異常隔離(Server 連續失敗超過指定次數置為不可用,然后定期進行心跳探測)
  • 負載均衡 :支持低并發優先、一致性 Hash、隨機請求、輪詢等
  • 擴展性 :支持 SPI 擴展(service provider interface)
  • 其他 :調用統計、訪問日志等

Motan 可以支持不同的 RPC 協議、傳輸協議。Motan 能夠無縫支持 Spring 配置方式使用 RPC 服務,通過簡單、靈活的配置就可以提供或使用 RPC 服務。通過使用 Motan 框架,可以十分方便的進行服務拆分、分布式服務部署。

與同類型的 Dubbo 相比,Motan 在功能方面并沒有那么全面,也沒有實現特別多的擴展,但 Motan 是一個小而精的 RPC 框架,它的特點是簡單、易用,是一個不斷向著實用、易用方向發展的 RPC 服務框架。

Motan RPC 框架介紹

Motan 的交互流程

Motan 中有服務提供方 RPC Server,服務調用方 RPC Client 和服務注冊中心 Registry 三個角色。

  • Server 向 Registry 注冊服務,并向注冊中心發送心跳匯報狀態。
  • Client 需要向注冊中心訂閱 RPC 服務,Client 根據 Registry 返回的服務列表,對具體的 Sever 進行 RPC 調用。
  • 當 Server 發生變更時,Registry 會同步變更,Client 感知后會對本地的服務列表作相應調整。

交互關系如下圖:

Motan 可以支持不同的注冊中心,如 ZK、Consul,目前使用的注冊中心是平臺開發的 Vintage,Vintage 是一個基于 Redis 的輕量級 KV 存儲系統,能夠提供命名空間服務、服務注冊、服務訂閱等功能。

Motan 框架

Motan 中主要有 register、transport、serialize、protocol 幾個功能模塊,各個功能模塊都支持通過 SPI 進行擴展,各模塊的交互如下圖所示:

Register:用來和注冊中心進行交互,包括注冊服務、訂閱服務、服務變更通知、服務心跳發送等功能。Server 端會在系統初始化時通過 register 模塊注冊服務,Client 端在系統初始化時會通過 register 模塊訂閱到具體提供服務的 Server 列表,當 Server 列表發生變更時也由 register 模塊通知 Client。

Protocol:用來進行 RPC 服務的描述和 RPC 服務的配置管理,這一層還可以添加不同功能的 filter 用來完成統計、并發限制等功能。

Serialize:將 RPC 請求中的參數、結果等對象進行序列化與反序列化,即進行對象與字節流的互相轉換;默認使用對 Java 更友好的 hessian2 進行序列化。

Transport:用來進行遠程通信,默認使用 Netty nio 的 TCP 長鏈接方式。

Cluster: Client 端使用的模塊,cluster 是一組可用的 Server 在邏輯上的封裝,包含若干可以提供 RPC 服務的 Server,實際請求時會根據不同的高可用與負載均衡策略選擇一個可用的 Server 發起遠程調用。

在進行 RPC 請求時,Client 通過代理機制調用 cluster 模塊,cluster 根據配置的 HA 和 LoadBalance 選出一個可用的 Server,通過 serialize 模塊把 RPC 請求轉換為字節流,然后通過 transport 模塊發送到 Server 端。

Server 端的 transport 模塊收到數據后,通過 serialize 模塊還原成 RPC 請求,并通過 protocol 層配置的參數找到具體提供服務的實現類,通過反射進行調用。調用后的結果在通過類似的方式返回到 Client 端,完成一次 RPC 請求。

Motan RPC 在混合云中新的需求和挑戰

在混合云平臺建設過程中,RPC 服務需要適應云端部署,在實際部署當中對 RPC 提出了一些新的要求。混合云平臺包括私有云與公有云兩部分,其中私有云在本地機房,為了保證云與云之間通信的穩定,使用了專線的方式鏈接私有云與公有云。RPC 服務在云端擴容大致有如下三種情況:

  • 擴容 RPC Client

  • 擴容 RPC Server

  • 按比例同時擴容 Client 和 Server

在實際擴容中會我們會盡量使用第三種方式進行就近訪問,但是有些情況下不可避免會產生第一二種情況,例如需要單獨擴容 RPC 服務,或者某些 RPC 服務依賴的資源暫時無法在公有云部署等。 在第一、二種情況下,會產生跨專線調用(圖中藍色虛線),為了能夠靈活控制 RPC 調用,需要 RPC 支持跨機房調用的能力,并且能夠控制跨機房調用的比例。

另外,以前 RPC 都是通過 group 配置來實現同機房調用,實際使用當中并不需要特別關注帶寬占用情況,但是在混合云環境中,跨機房調用會占用專線帶寬,因此,RPC 占用的帶寬也需要盡量節約。

我們做了如下改進:

流量壓縮

在微博中有兩種典型的 RPC 使用場景:一種是請求的參數和返回值都非常小,但是請求量大,QPS 高。例如微博的未讀數 unread 服務。另一種場景是 QPS 不高,但每次返回值較大。

針第一種場景,我們對協議自身信息進行了壓縮。Motan 的請求信息大致包括 4 個部分 essay-header 信息、Service 及請求方法描述、參數值、附加信息。

  • essay-header 信息包括 RPC 版本、消息類型、消息長度等固定內容,長度 16 字節。
  • Service 和方法描述包括請求服務的接口類全稱、方法名、方法參數描述。
  • 參數值是通過 hessian2 序列化后的參數對象。
  • 附加信息包括調用方信息(application)、接口版本號、group、requestId 等。

一般情況下 Service 和方法描述、附加信息等就有 200 多個字節,這樣在發送一個只有 long 型參數等請求時,協議的有效載荷就比較低。壓縮的思路是使?方法簽名代替方法描述信息,并緩存傳輸中固定的附加信息。

RPC Server 提供的接口和方法是有限的,只要能標示出想要調用的方法及版本,就不需要攜帶完整的方法信息。Server 端和 Client 端都把接口名、方法名、參數描述、版本信息生成 16 字節的簽名并把對應關系進行緩存,這樣在每次請求時只需要攜帶 16 字節的簽名就能找到具體的調用方法。

方法簽名需要在一個 Server 內唯一,所以不需要考慮全局碰撞,只要對應 Server 中的方法簽名不產生碰撞沖突就可以。按單個 Server 中包含 10W 個方法估算,16 字節簽名產生沖突的概率?約為 1 / 2 * 10W  * (10W – 1)  * 1 / (2 ^ 128) 約為 1 / 2 ^ 99,碰撞的概率可以忽略不計。當 Server 端新增方法出現碰撞時,可以通過修改?法名避免沖突。

請求中的附加信息用來統計調用情況,每個 Client 在運行中附加信息大部分是不會變動的,因此如果 Server 端能夠緩存這些信息也就不需要每次請求都攜帶了。

在 Client 首次調用服務時,會把固定的附加信息和對應的附加信息簽名一起發送給 Server 端,Server 端收到請求后會以 IP 為維度緩存附加信息和簽名,然后返回值中會回傳對應簽名作為應答。Client 收到應答信息后再次請求時就只需傳遞簽名了。當 Server 端因為重啟等原因丟失簽名對應信息后,如果收到未知的簽名信息會要求 Client 下次請求攜帶完整附加信息,這樣就能夠再次緩存對應信息。

對協議信息進行壓縮后,一次只有一個 long 參數的 RPC 請求從 280 字節減少到了 94 個字節。在實測場景下平均每個請求壓縮了 60%,在 QPS 較高的場景中上行帶寬明顯減少。

針對另一種返回值較大的場景,我們嘗試了不同的序列化協議,如使用 Protocol Buffers 代替 hessian2,實際測試約減小 10%~15%,效果不算理想,當使用 QuickLZ 或 GZIP 進行壓縮后,根據原始大小不同壓縮后大約可以減少 30%~70%,缺點就是 CPU 負載會有所增加。

最后我們增加了壓縮功能,業務方可以根據業務特點配置是否啟用壓縮以及啟用壓縮的最小閾值。為了盡量減少第三方包依賴,默認使用 GZIP 進行壓縮。實際測試在返回值 2KB~20KB 時壓縮會有比較好的效果,大于 50KB 壓縮平均耗時就會比較高。

壓縮統計數據如下:

動態流量調整

Motan 中的 Service(即接口類)以 group 為單位劃分,group 一般由機房+業務線組成,一個 group 下可以有多個 Service。例如 group 為 yf-user 表示永豐機房的 user RPC 服務,yf-user 的 Client 只會訂閱 yf-user 的 Server,通過 group 實現同機房就近訪問。

要想控制 Client 的跨機房調用,實際上就是允許 Client 以一定比例進行跨 group 調用。因此從注冊中心或 Client 端自身都可以實現這個功能,為了 Motan 框架在使用不同注冊中心時都能擁有流量控制的功能,我們選擇了在 Client 端實現。

我們設計了一套指令系統,這樣方便后續擴展更多的管理功能,指令使用 JSON 格式保存在注冊中心,當 Client 在訂閱 RPC 服務時同時也訂閱對應的指令。指令以 group 為單位存儲,即相同 group 的 Client 會接收到相同的指令。當從管理后臺發布一條指令后,對應的 Client 會收到指令,并且解析執行指令。

單條指令的樣例如下:

{

” index ” : 1,

” version ” :  “1.0”,

” dc ” :  ” yf “,

” pattern ” :  ” * “,

” mergeGroups ” : [

” openapi-tc-test-RPC : 12 “,

” openapi-yf-test-RPC : 1 ”

] ,

” routeRules ” :  [],

” remark ” : ” 切換 50% 流量到另外一個機房 ”

}

其中 mergeGroups 屬性用來實現跨機房調用設置,可以設置多個機房以及各機房調用的權重。跨機房調用過程如下圖所示:

group1 和 group2 分別屬于兩個不同的機房,提供相同的 Service,Client1 和 Server1 屬于 group1,Client2 和 Server2 屬于 group2。 正常情況下 Client 對Server 的調用是同 group 調用,如圖中藍色箭頭所示。

當 group1 的 Server 壓力突增時,通過管理后臺 manager 在 registry 設置 group1 的 mergeGroup 指令為

” mergeGroups ” :  [

” group1 : 5 “,

” group2 : 1 ”

]

這時 group1 的 Client1 會收到指令并解析,根據指令 Client1 會同時訂閱 group1 和 group2,并按 5 : 1 的比例同時訪問 group1 的 Server1 和 group2 的 Server2,如圖中紅色虛線表示。

Client2 由于不屬于 group1,所以并不會接收到這條指令,仍然只訪問 Server2。

指令控制的粒度可以到接口類級別,通過 pattern 字段來進行設置。通過設置 group 的權重來控制流量切換的比例,最小比例可以到 1%。

指令中的 routeRules 字段可以實現路由功能,來精確控制調用,或者用來實現預覽等功能。如:

{

” index ” : 3,

” version ” :  ” 1.0 “,

” dc ” : ” yf “,

” pattern “: ” com.weibo.xxxx.Preview “,

” mergeGroups ” :  [],

” routeRules ” : [

” * to !10.75.0.1 ”

],

” remark ” : ” 預覽某臺機器,關閉其線上流量 ”

}

routeRules 中的每條 rule 表示 from to 的關系, ” * to !10.75.0.1 ” 這條 rule 表示禁止訪問預覽機 10.75.0.1。

rule 規則支持通配符,如 “ 10.75.0.* to 10.75.0.1 ” 表示 10.75.0 段的所有 Client 只能請求 10.75.0.1 這臺 Server。

其他優化

在 RPC 調用中還有一些小的細節,例如,Server 端業務異常時會把異常棧序列化并傳遞到 Client,一個異常棧可能會有 1- 2k 的大小,如果 RPC 調用中異常大量增加,也會占用不小帶寬。考慮到大部分場景 Client 端只關心異常原因,并不關心異常棧內容,我們對業務異常時的異常棧進行了替換,避免傳輸不必要的棧信息。

此外還對 RPC 服務的注冊與注銷機制進行了一些調整,并支持了使用 consul 作為注冊中心。使 RPC 服務在混合云的部署能夠更加靈活。

后記

Motan RPC 框架在微博平臺的各個業務中發揮著重要作用,通過在微博將近三年的線上運行考驗,正在變的越來越優秀,功能也更加完善。Motan 的設計理念是簡單、易用,前輩大師們在設計 Motan 框架時就朝著這個方向努力,后續也將向這個方向繼續發展。

如何打造更為易用的高性能 RPC 框架?我覺得除了與時俱進的實現新的需求,更需要從細節上考慮框架的易用性,例如新功能的無縫升級、盡量降低業務方的接入成本、增加必要的切換開關提高靈活性,后臺指令預覽防止誤操作等,從每個細節完善 Motan 框架。

近期我們打算對 Motan 框架進行全面梳理,去掉特定的依賴,使用更通用的方式來實現,并完善配套的文檔。開源,是 Motan 設計之初的方向之一,我們要把 Motan 打造成一個更易用的高性能 RPC 開源框架。歡迎大家一起參與。

Q & A

1、Motan 是否開源?現在能下載代碼不?

我們正在進行梳理,近期準備開源核心部分。

2、支持 PHP 等跨語言調用嗎?

目前 Motan 還不支持 PHP 等其他語言調用,我們正在進行這方面工作,已經在小范圍測試。

3、圖里的耗時是壓縮耗時么?

圖里的耗時是單次壓縮耗時均值。

4、從零開始實現一個 RPC,一般需要考慮實現哪些東西,大致思路是什么?

簡單的 RPC 調用只需要考慮協議(方法描述)、序列化(參數值傳遞)、傳輸(TCP、HTTP 等)

5、Motan 支持異步調用嗎?如何實現?Load balance 支持哪些策略?是否支持自定義策略?

Motan 的請求在傳輸層面都是異步調用的,但是在使用本地引用時不能顯示異步執行。Load balance 策略支持低并發優先、一致性 hash、隨機、輪詢。支持通過 SPI 機制擴展其他策略。

6、感覺和 Dubbo 好像好像,里面的序列化協議,通信協議,框架模塊定義,架構分層,服務分組都一樣一樣的,命令行改配置好像也不方便,Dubbo 的管理界面很方便,相比 Dubbo 具體怎么輕量?Dubbo 用起來好像也只要一個注冊中心就可以了。

與 Dubbo 的分層來對比,Motan 的模塊層次要更簡單一些,沒有 exchange 和 directory 等,Motan 提供了一些 SLA 策略,例如并發限制等。

7、如果設計監控的話一般涉及到那些指標,如何快速發現服務出現問題?

一般通過監控統計日志中的耗時、框架異常、業務異常數量,來發現服務問題

RPC 的調用情況是在內存中通過 Metric 進行統計的,然后統一輸出到統計日志中。

8、 重新實現了一套 Dubbo 是出于什么樣的考慮,或者說業務上 Dubbo 無法解決哪方面的需求?

Dubbo 功能上比較豐富,但當時我們想要一個比較輕量的 RPC 框架,方便我們做一些適合自己業務場景的改造和功能 feature,以達到內部業務平滑改造和遷移的目的。這種情況下,在 dubbo 上改的成本可能比重新寫一套更高。最終我們決定開發 motan RPC。從另外角度,選擇復用和選擇自行開發需要看團隊的研發能力與資源,我們當時剛好有合適的工程師有興趣來做這個。

9、 Motan 支持功能擴展嗎?

支持通過 SPI 方式進行擴展

10、 Registry 如何保證高可用?Server 與 Registry 是雙向心跳還是單向?或者說如何保證 Server 上下線狀態及時感知?

Registry 自身需要較強的容災來保證高可用,例如 ZK、Consul 都支持很強的容災。另外,當 Registry 不幸各個節點都掛掉的話,會影響服務的發布與注銷,不會影響 Client 的正常調用。Server 與 Registry 是單向心跳。Server 上下線 Client感知有可能會有延遲,一般都是秒級。

12、 一致性hash的負載策略能具體講一下嗎?

根據請求 Request 參數計算出一個 hashcode,按 hashcode 每次請求同一個 server。一致性 hash 的策略主要用于有狀態的RPC服務場景,比如有session的IM服務等。

 本文由用戶 81365341 自行上傳分享,僅供網友學習交流。所有權歸原作者,若您的權利被侵害,請聯系管理員。
 轉載本站原創文章,請注明出處,并保留原始鏈接、圖片水印。
 本站是一個以用戶分享為主的開源技術平臺,歡迎各類分享!