服務化實戰之 dubbo、dubbox、motan、thrift、grpc等RPC框架比較及選型

概述

前段時間項目要作服務化,因此我比較瞭如今流行的幾大RPC框架的優缺點以及使用場景,最終結合自己項目的實際狀況選擇了使用dubbox做爲rpc基礎服務框架。下面就簡單介紹一下RPC框架技術選型的過程。php

RPC簡述

該系列文章將講述如下RPC框架的helloword實例以及其實現原理簡述,因爲每一種RPC框架的原理實現不一樣且都比較複雜,若是想深刻研究還請自行到官網或者其餘技術博客學習。
RPC框架職責
RPC框架要向調用方屏蔽各類複雜性,要向服務提供方也屏蔽各種複雜性:html

  • 調用方感受就像調用本地函數同樣
  • 服務提供方感受就像實現一個本地函數同樣來實現服務

RPC框架是架構(微)服務化的首要基礎組件,它能大大下降架構微服務化的成本,提升調用方與服務提供方的研發效率,屏蔽跨進程調用函數(服務)的各種複雜細節
RPC框架的職責是:讓調用方感受就像調用本地函數同樣調用遠端函數、讓服務提供方感受就像實現一個本地函數同樣來實現服務java

對於不是很理解服務化的同窗能夠看看這兩篇文章:
服務化架構的演進與實踐
淺談服務化架構git

服務化的好處
互聯網架構爲何要作服務化?程序員

服務架構的拆分原則

來源:李林峯的文章:華爲內部如何實施微服務架構?基本就靠這5大原則github

服務拆分原則:圍繞業務功能進行垂直和水平拆分。大小粒度是難點,也是團隊爭論的焦點。web

很差的實踐

  • 以代碼量做爲衡量標準,例如500行之內。
  • 拆分的粒度越小越好,例如以單個資源的操做粒度爲劃分原則。

建議的原則

  • 功能完整性、職責單一性。
  • 粒度適中,團隊可接受。
  • 迭代演進,非一蹴而就。
  • API的版本兼容性優先考慮。

代碼量多少不能做爲衡量微服務劃分是否合理的原則,由於咱們知道一樣一個服務,功能自己的複雜性不一樣,代碼量也不一樣。還有一點須要重點強調,在項目剛開始的時候,不要指望微服務的劃分一蹴而就。
微服務架構的演進,應該是一個按部就班的過程。在一個公司、一個項目組,它也須要一個按部就班的演進過程。一開始劃很差,沒有關係。當演進到一個階段時,微服務的部署、測試和運維等成本都很是低的時候,這對於你的團隊來講就是一個好的微服務。算法

服務架構的開發原則

微服務的開發還會面臨依賴滯後的問題。例如:A要作一個身份證號碼校驗,依賴服務提供者B。因爲B把身份證號碼校驗服務的開發優先級排的比較低,沒法知足A的交付時間點。A會面臨要麼等待,要麼本身實現一個身份證號碼校驗功能。spring

之前單體架構的時候,你們須要什麼,每每喜歡本身寫什麼,這實際上是沒有太嚴重的依賴問題。可是到了微服務時代,微服務是一個團隊或者一個小組提供的,這個時候必定沒有辦法在某一個時刻同時把全部的服務都提供出來,「需求實現滯後」是必然存在的。docker

一個好的實踐策略就是接口先行,語言中立,服務提供者和消費者解耦,並行開發,提高產能。不管有多少個服務,首先須要把接口識別和定義出來,而後雙方基於接口進行契約驅動開發,利用Mock服務提供者和消費者,互相解耦,並行開發,實現依賴解耦。

採用契約驅動開發,若是需求不穩定或者常常變化,就會面臨一個接口契約頻繁變動的問題。對於服務提供者,不能由於擔憂接口變動而遲遲不對外提供接口,對於消費者要擁抱變動,而不是抱怨和抵觸。要解決這個問題,一種比較好的實踐就是管理 + 技術左右開弓:

  • 容許接口變動,可是對變動的頻度要作嚴格管控。
  • 提供全在線的API文檔服務(例如Swagger UI),將離線的API文檔轉成全在線、互動式的API文檔服務。
  • API變動的主動通知機制,要讓全部消費該API的消費者可以及時感知到API的變動。
  • 契約驅動測試,用於對兼容性作迴歸測試。

服務架構的測試原則

微服務開發完成以後須要對其進行測試。微服務的測試包括單元測試、接口測試、集成測試和行爲測試等,其中最重要的就是契約測試:
這裏寫圖片描述

利用微服務框架提供的Mock機制,能夠分別生成模擬消費者的客戶端測試樁和提供者的服務端測試樁,雙方能夠基於Mock測試樁對微服務的接口契約進行測試,雙方都不須要等待對方功能代碼開發完成,實現了並行開發和測試,提升了微服務的構建效率。基於接口的契約測試還能快速的發現不兼容的接口變動,例如修改字段類型、刪除字段等。

服務架構的部署原則

測試完成以後,須要對微服務進行自動化部署。微服務的部署原則:獨立部署和生命週期管理、基礎設施自動化。須要有一套相似於CI/CD的流水線來作基礎設施自動化,具體能夠參考Netflix開源的微服務持續交付流水線Spinnaker:
這裏寫圖片描述

最後一塊兒看下微服務的運行容器:微部署能夠部署在Dorker容器、PaaS平臺(VM)或者物理機上。使用Docker部署微服務會帶來不少優先:

  • 一致的環境,線上線下環境一致。
  • 避免對特定雲基礎設施提供商的依賴。
  • 下降運維團隊負擔。
  • 高性能接近裸機性能。
  • 多租戶。

相比於傳統的物理機部署,微服務能夠由PaaS平臺實現微服務自動化部署和生命週期管理。除了部署和運維自動化,微服務雲化以後還能夠充分享受到更靈活的資源調度:

  • 雲的彈性和敏捷。
  • 雲的動態性和資源隔離。

服務架構的治理原則

服務部署上線以後,最重要的工做就是服務治理。微服務治理原則:線上治理、實時動態生效。
微服務經常使用的治理策略:

  • 流量控制:動態、靜態流控制。
  • 服務降級。
  • 超時控制。
  • 優先級調度。
  • 流量遷移。
  • 調用鏈跟蹤和分析。
  • 服務路由。
  • 服務上線審批、下線通知。
  • SLA策略控制。
  • 微服務治理模型以下所示:

這裏寫圖片描述

最上層是爲服務治理的UI界面,提供在線、配置化的治理界面供運維人員使用。SDK層是提供了微服務治理的各類接口,供服務治理Portal調用。最下面的就是被治理的微服務集羣,集羣各節點會監聽服務治理的操做去作實時刷新。例如:修改了流控閾值以後,服務治理服務會把新的流控的閾值刷到服務註冊中心,服務提供者和消費者監聽到閾值變動以後,獲取新的閾值並刷新到內存中,實現實時生效。因爲目前服務治理策略數據量不是特別大,因此能夠將服務治理的數據放到服務註冊中心(例如etcd/ZooKeeper),沒有必要再單獨作一套。

服務最佳實踐

介紹完微服務實施以後,下面咱們一塊兒學習下微服務的最佳實踐。
服務路由:本地短路策略。關鍵技術點:優先調用本JVM內部服務提供者,其次是相同主機或者VM的,最後是跨網絡調用。經過本地短路,能夠避免遠程調用的網絡開銷,下降服務調用時延、提高成功率。原理以下所示:

這裏寫圖片描述
服務調用方式:同步調用、異步調用、並行調用。一次服務調用,一般就意味着會掛一個服務調用線程。採用異步調用,能夠避免線程阻塞,提高系統的吞吐量和可靠性。可是在實際項目中異步調用也有一些缺點,致使使用不是特別普遍:
須要寫異步回調邏輯,與傳統的接口調用使用方式不一致,開發難度大一些。
一些場景下須要緩存上下文信息,引入可靠性問題。
並行調用適用於多個服務調用沒有上下文依賴,邏輯上能夠並行處理,相似JDK的Fork/Join, 並行服務調用涉及到同步轉異步、異步轉同步、結果匯聚等,技術實現難度較大,目前不少服務框架並不支持。採用並行服務調用,能夠把傳統串行的服務調用優化成並行處理,可以極大的縮短服務調用時延。

微服務故障隔離:線程級、進程級、容器級、VM級、物理機級等。關鍵技術點:

  • 支持服務部署到不一樣線程/線程池中。
  • 核心服務和非核心服務隔離部署。
  • 爲了防止線程膨脹,支持共享和獨佔兩種線程池策略。

這裏寫圖片描述

談到分佈式,就繞不開事務一致性問題:大部分業務能夠經過最終一致性來解決,極少部分須要採用強一致性。
這裏寫圖片描述
具體的策略以下:

  • 最終一致性,能夠基於消息中間件實現。
  • 強一致性,使用TCC框架。服務框架自己不會直接提供「分佈式事務」,每每根據實際須要遷入分佈式事務框架來支持分佈式事務。

微服務的性能三要素:

  • I/O模型,這個一般會選用非堵塞的,Java裏面可能用java原生的。
  • 線程調度模型。
  • 序列化方式。

公司內部服務化,對性能要求較高的場景,建議使用異步非阻塞I/O(Netty) + 二進制序列化(Thrift壓縮二進制等) + Reactor線程調度模型。
這裏寫圖片描述
最後咱們一塊兒看下微服務的接口兼容性原則:技術保障、管理協同。

  • 制定並嚴格執行《微服務前向兼容性規範》,避免發生不兼容修改或者私自修改不通知周邊的狀況。
  • 接口兼容性技術保障:例如Thrift的IDL,支持新增、修改和刪除字段、字段定義位置無關性,碼流支持亂序等。
  • 持續交付流水線的每日構建和契約化驅動測試,可以快速識別和發現不兼容。

如今流行的RPC框架:

服務治理型

  • dubbo
  • dubbox
  • motan

多語言型

  • grpc
  • thrift
  • avro
  • Protocol Buffers (google)
    這裏寫圖片描述

上圖來自於dubbo。服務治理型RPC框架結構大多如此,大體分爲服務提供者,服務消費者,註冊中心,監控報警中心幾大模塊。

服務性能

在服務化,或者微服務化過程當中,首先考慮的問題就是性能問題,由於在服務化以後,會增長如下額外的性能開銷:

  1. 客戶端須要對消息進行序列化,主要佔用CPU計算資源。
  2. 序列化時須要建立二進制數組,耗費JVM堆內存或者堆外內存。
  3. 客戶端須要將序列化以後的二進制數組發送給服務端,佔用網絡帶寬資源。
  4. 服務端讀取到碼流以後,須要將請求數據報反序列化成請求對象,佔用CPU計算資源。
  5. 服務端經過反射的方式調用服務提供者實現類,反射自己對性能影響就比較大。
  6. 服務端將響應結果序列化,佔用CPU計算資源。
  7. 服務端將應答碼流發送給客戶端,佔用網絡帶寬資源。
  8. 客戶端讀取應答碼流,反序列化成響應消息,佔用CPU資源。

RPC框架高性能設計

要想提升效率,除了硬件的提高,主要考慮如下三個方面:

  1. I/O調度模型:同步阻塞I/O(BIO)仍是非阻塞I/O(NIO)。
  2. 序列化框架的選擇:文本協議、二進制協議或壓縮二進制協議。
  3. 線程調度模型:串行調度仍是並行調度,鎖競爭仍是無鎖化算法。

IO調度如今主流的就是netty。
高性能序列化目前性能最好的是ice,google 的 pb協議,FB的thrift協議等
線程沒啥好說的,確定多線程了。固然也能夠是AKKA(java)

總結

綜上所述,服務化是如今大型互聯網公司主流的架構模式,如今還有更流行的微服務,docker部署等等。

我的建議採用dubbox,集成其餘各類協議,在該系列文章最後有各個協議的性能對比。

之因此建議採用dubbox是由於,dubbox有比價完善的服務治理模型,其包含ZK註冊中心,服務監控等,能夠很方便的爲咱們服務。
雖然dubbo自己不支持多語言,可是咱們能夠集成其餘的序列化協議,好比thrift、avro,使其能夠支持多種入門語言,讓部門間的協做溝通更加靈活方便

固然,在實際使用過程當中,尤爲是集成其餘協議的過程當中,確定須要對協議自己有比較深刻的瞭解,才能正確的使用。

motan

新浪微博開源的RPC框架

helloword示例直接去官網下載運行便可

github地址:https://github.com/weibocom/motan
文檔地址:https://github.com/weibocom/motan/wiki/zh_quickstart
用戶指南;https://github.com/weibocom/motan/wiki/zh_userguide

# grpc

中文版官方文檔:gRPC 官方文檔中文版

helloWord示例,我就是根據這個文章作的,寫得挺詳細的:rpc框架之gRPC 學習 - hello world

grpc原理: grpc原理分析

dubbo

dubbo 已經與12年年末中止維護升級,忽略

thrift

請參考我寫的另外一篇文章:thrift學習筆記(一) thrift簡介及第一個helloword程序

dubbox

dubbox 是噹噹團隊基於dubbo升級的一個版本。是一個分佈式的服務架構,可直接用於生產環境做爲SOA服務框架。
dubbo官網首頁:http://dubbo.io/ 上面有詳細的用戶指南和官方文檔,介紹的比較詳細,這裏再也不贅述。

噹噹官方的github地址:https://github.com/dangdangdotcom/dubbox

升級爲spring4.X(及其餘依賴組件)版本dubbox的github的地
址:https://github.com/yjmyzz/dubbox

參考資料【博客:菩提樹下的楊過 的文章寫得很是全面,介紹的已經很是詳細了】:
dubbox升級spring到4.x及添加log4j2支持
分佈式服務框架 dubbo/dubbox 入門示例
dubbox 的各類管理和監管
dubbo/dubbox 增長原生thrift及avro支持

# 各個RPC框架性能比較

測試環境

jdk7
win7 64位
idea
我的筆記本配置:
這裏寫圖片描述

person對象:

private int age;
private String name;
private boolean sex;
private int childrenCount;

測試數據,入參:

private int ageStart;
private int ageEnd;

返回值:

Person.PersonBuilder builder = Person.builder();
List<Person> list = new ArrayList<Person>();
for (short i = 0; i < 10; i++) {
    list.add(builder.age(i).childrenCount(i).name("test" + i).sex(true).build());
}
return list;

各協議測試使用的配置

  • grpc


rpc getPersonList (yjmyzz.grpc.study.dto.QueryParameter) returns (yjmyzz.grpc.study.dto.PersonList) {}

  • motan

<motan:basicService export="demoMotan:8002"
group="motan-demo-rpc" accessLog="false" shareChannel="true" module="motan-demo-rpc"
application="myMotanDemo" registry="registry" id="serviceBasicConfig"/>

  • dubbox
<dubbo:protocol name="dubbo" serialization="kryo" optimizer="com.alibaba.dubbo.demo.SerializationOptimizerImpl"/>

<dubbo:service interface="com.alibaba.dubbo.demo.person.PersonService" ref="personService" protocol="dubbo"/>
  • thrift

TNonblockingServer + TFramedTransport

測試結果

rgpc 100000 次NettyServer調用,耗時:53102毫秒,平均1883次/秒 【簡單grpc】
rgpc 100000 次NettyServer調用,耗時:52138毫秒,平均1917次/秒 【簡單grpc】
rgpc 100000 次NettyServer調用,耗時:51800毫秒,平均1930次/秒 【簡單grpc】
rgpc 100000 次NettyServer調用,耗時:51313毫秒,平均1948次/秒 【簡單grpc】

rgpc 100000 次NettyServer調用,耗時:56812毫秒,平均1760次/秒[2016-10-08 19:17:31] Dubbo service server started! 【dubbox.kryo】
rgpc 100000 次NettyServer調用,耗時:55133毫秒,平均1813次/秒[2016-10-08 19:18:42] Dubbo service server started!【dubbox.kryo】
rgpc 100000 次NettyServer調用,耗時:52280毫秒,平均1912次/秒[2016-10-08 19:20:01] Dubbo service server started! 【dubbox.kryo】

rgpc 100000 次NettyServer調用,耗時:44414毫秒,平均2251次/秒[2016-10-08 19:13:34] Dubbo service server started! 【dubbox.fst】
rgpc 100000 次NettyServer調用,耗時:44805毫秒,平均2231次/秒[2016-10-08 19:12:25] Dubbo service server started! 【dubbox.fst】
rgpc 100000 次NettyServer調用,耗時:46245毫秒,平均2162次/秒[2016-10-08 19:14:43] Dubbo service server started! 【dubbox.fst】

rgpc 100000 次NettyServer調用,耗時:12203毫秒,平均8194次/秒[2016-10-09 19:52:34] Dubbo service server started!【dubbox.thrift】
rgpc 100000 次NettyServer調用,耗時:14142毫秒,平均7071次/秒[2016-10-09 19:30:17] Dubbo service server started!【dubbox.thrift】
rgpc 100000 次NettyServer調用,耗時:13762毫秒,平均7266次/秒[2016-10-09 19:30:43] Dubbo service server started!【dubbox.thrift】


rgpc 100000 次NettyServer調用,耗時:44334毫秒,平均2255次/秒 【motan】
rgpc 100000 次NettyServer調用,耗時:37844毫秒,平均2642次/秒 【motan】
rgpc 100000 次NettyServer調用,耗時:39007毫秒,平均2563次/秒 【motan】
rgpc 100000 次NettyServer調用,耗時:38610毫秒,平均2590次/秒 【motan】

測試結果說明

使用的本身的筆記本電腦測試的,測試的方式可能不太專業,但可以說明問題。
經過上面結果能夠看到,thrift的性能最好,並且是至關的好

網上其餘人作的測試

ice-dubbo-thrift-grpc性能測試對比
RPC框架的性能比較

總結

影響RPC性能的因素主要有:

  • 序列化性能
  • IO性能
  • 線程模式

序列化的話,確定是Google的PB協議和thrift最好,IO和線程的話,先流行的性能比較好的都是採用多線程非阻塞IO。

grpc是Google出品,使用了PB協議,可是因爲它出現的比較晚,還不怎麼成熟,並且採用http協議,很是適合如今的微服務,不過性能上差了許多,並且像服務治理與監控都須要額外的開發工做,因此放棄grpc。
thrift和grpc同樣,性能優越,可是開發難度相比較於dubbox和motan也是高了一點點,須要編寫proto文件(其實對於程序員來講這算不上難度)。像服務治理與監控也是須要額外的開發工做。
dubbo比較老了,直接棄用。
dubbox和後來的motan都比較適合咱們的團隊。dubbox後來通過當當的開發,引入了rest風格的http協議,而且還支持kryo/fst/dubbo/thrift等其餘協議,並且其餘團隊也在使用dubbo,集成方便,服務治理監控功能齊全,因此最終採用dubbox。

其實我我的而言仍是喜歡thrift,畢竟性嫩優越,在大型分佈式系統中,哪怕一點點性能提高累計在一塊兒也是很可觀的。不過再具體選型的過程當中還要結合團隊目前的情況和團隊其餘開發人員的接受程度進行取捨。

相關文章
相關標籤/搜索