前言
首先本文不討論為什么要服務(wù)化,包括服務(wù)化的優(yōu)點缺點。
其次本文也不討論什么是微服務(wù),也不討論微服務(wù)和SOA的區(qū)別。
最后本文也不討論哪個技術(shù)最優(yōu)。
服務(wù)化框架構(gòu)成
最基本的服務(wù)框架
基本的服務(wù)化框架包括如下模塊:統(tǒng)一的RPC框架,服務(wù)注冊中心,管理平臺。
有了這三個模塊,就能實現(xiàn)基本的服務(wù)化。
下面對三個模塊進(jìn)行具體分析。
RPC框架選型
為什么一定要是統(tǒng)一的RPC框架,而不是隨便啥框架,這里主要是為了技術(shù)對齊,減少開發(fā)人員的學(xué)習(xí)成本,減少團(tuán)隊間溝通成本。
好,那么選擇一個RPC框架,我們都需要考量什么東西呢?
這里我總結(jié)下:
- 代碼規(guī)范:例如是對已有代碼透明,還是代碼生成。
- 通訊協(xié)議:例如是TCP還是HTTP
- 序列化協(xié)議:例如是二進(jìn)制還是文本,是否需要跨語言,性能
- IO模型:異步/同步,阻塞/非阻塞
- 負(fù)載均衡:客戶端軟負(fù)載,代理模式,服務(wù)端負(fù)載
另外如果是從開源里面選擇,那么我們還需要考量:
- 成熟度:包括學(xué)習(xí)成本,社區(qū)熱度,文檔數(shù),是否有團(tuán)隊維護(hù),穩(wěn)定性(盲目追求的不一定是最適合)
- 可擴(kuò)展性:是否有SPI支持?jǐn)U展,是否支持上下兼容
- 跨語言:是否支持跨語言
- 性能:要想作為RPC框架,性能一般都不會太差
下面是常見的一些開源框架的比較,大家可以看一下。
| x | Thrift | RESTful | dubbo | gRPC |
|---|---|---|---|---|
| 代碼規(guī)范 | 基于Thrift的IDL生成代碼 | 基于JAX-RS規(guī)范 | 無代碼入侵 | 基于.Proto生成代碼 |
| 通訊協(xié)議 | TCP | HTTP | TCP | HTTP/2 |
| 序列化協(xié)議 | thrift | JSON | 多協(xié)議支持,默認(rèn)hessian | protobuf |
| IO框架 | Thrift自帶 | Servlet容器 | Netty3 | Netty4 |
| 負(fù)載均衡 | 無 | 無 | 客戶端軟負(fù)載 | 無 |
| 跨語言 | 多種語言 | 多種語言 | Java | 多種語言 |
| 可擴(kuò)展性 | 一般 | 好 | 好 | 差 |
Ps:SOAP,RMI,Hessian,ICE就不列舉了。
選型小結(jié):
- 如果需要與前端交互的,適合短鏈接、跨語言的RPC框架,例如RESTful、gRPC等
- 如果純粹后臺交互的,適合長鏈接、序列化為二進(jìn)制的RPC框架,例如thrift、dubbo等更高效
- 如果是小公司,新公司從頭開始推廣服務(wù)化框架的,可以選擇規(guī)范化的RPC框架,例如thrift、RESTful、gRPC
- 如果是已有大量業(yè)務(wù)代碼的再推廣服務(wù)框架的,那么最好選擇無代碼入侵的RPC框架,例如dubbo、RESTful
注冊中心選型
注冊中心相當(dāng)于是服務(wù)提供者和服務(wù)調(diào)用者之間的引路人,在服務(wù)治理中的作用極為重要。
選擇注冊中心基本要考量:
- 服務(wù)注冊:接收注冊信息的方式
- 服務(wù)訂閱:返回訂閱信息的方式,推還是拉
- 狀態(tài)檢測:檢測服務(wù)端存活狀態(tài)
重點提一下這個狀態(tài)檢測,因為這個要是檢測不準(zhǔn)確會誤判,導(dǎo)致嚴(yán)重后果,
例如Zookeeper根據(jù)服務(wù)端注冊的臨時節(jié)點進(jìn)行狀態(tài)檢測,如果服務(wù)端和Zookeeper之間的網(wǎng)絡(luò)閃斷,導(dǎo)致Zookeeper認(rèn)為服務(wù)端已經(jīng)死了,從而摘掉這個節(jié)點。
但是其實客戶端和服務(wù)端直接的網(wǎng)絡(luò)是好的,這樣就有可能把節(jié)點全部摘掉,導(dǎo)致無可用節(jié)點。
如果是從開源里面選擇,那么還需要考量:
- 成熟度:包括學(xué)習(xí)成本,社區(qū)熱度,文檔數(shù)(盲目追求的不一定是最適合)
- 維護(hù)成本:注冊中心維護(hù)
- 數(shù)據(jù)解構(gòu):是否能快速定位結(jié)果,是否能遍歷
- 性能和穩(wěn)定性:
- CAP原則:CP(關(guān)注一致性)還是AP(關(guān)注可用性)
下面是常見的一些使用開源項目做注冊中心的比較,大家可以看一下。
| ZooKeeper | etcd | Consul | Eureka | |
|---|---|---|---|---|
| 一致性 | 強(qiáng)一致性paxos | 強(qiáng)一致性Raft | 強(qiáng)一致性Raft | 弱一致性 |
| 數(shù)據(jù)結(jié)構(gòu) | Tree | K/V | K/V | K/V |
| 通訊協(xié)議 | TCP | HTTP、gRPC | HTTP、DNS | HTTP |
| 客戶端 | ZKClient | - | - | Eureka-client |
| CAP原則 | CP | CP | CP | AP |
Ps:Redis和MySQL沒有列舉。
選型小結(jié):
- 規(guī)模小選擇CP,RPC框架可以直接接入數(shù)據(jù)源
- 規(guī)模大選擇AP, RPC框架不可以直接接入數(shù)據(jù)源
- 存在跨機(jī)房,跨地域的盡量不要選有強(qiáng)一致性協(xié)議的注冊中心
- RPC框架必須要有注冊中心不可用的容災(zāi)策略
- 服務(wù)狀態(tài)檢測十分重要
簡易管理端
管理端沒啥特殊要求,最起碼能看到服務(wù)提供者和調(diào)用者即可。
完善的服務(wù)化框架
如果需要一個完善的服務(wù)化框架,那么必須增加外部模塊,常見的模塊如下圖:

接口文檔管理
提供一個接口文檔管理以及接口查詢的入口,可以是一個公共的WIKI,也可以是獨立的系統(tǒng),等等。
這里可以定義接口的文檔,包括接口描述,方法定義,字段定義
可以定義接口的SLA,包括支持的并發(fā)數(shù),tp99多少,建議配置是什么
還有就是接口的負(fù)責(zé)人等一些查詢的入口。
配置中心
提供一個配置管理的地方,這里說的配置主要指的是服務(wù)相關(guān)的一些配置。
配置包括分組配置、路由策略、黑白名單、降級開關(guān)、限流信息、超時時間、重試次數(shù)等等,任何可以動態(tài)變更的所有數(shù)據(jù)。
這樣服務(wù)提供者和服務(wù)調(diào)用者可以不需要重啟自己的應(yīng)用,直接進(jìn)行配置的變更。
配置中心可以獨立于注冊中心,也可以和注冊中心合并。
監(jiān)控中心
監(jiān)控服務(wù)關(guān)注接口維度,實例(例如所在JVM實例)維度的數(shù)據(jù)。
RPC框架可以定時上報調(diào)用次數(shù),耗時,異常等信息。
監(jiān)控中心可以統(tǒng)計出服務(wù)質(zhì)量信息,也可以進(jìn)行監(jiān)控報警。
分布式跟蹤
區(qū)別于監(jiān)控中心,以調(diào)用鏈的模式對服務(wù)進(jìn)行。
RPC框架作為分布式跟蹤系統(tǒng)的一個天然埋點,可以很好的進(jìn)行一個數(shù)據(jù)輸出。
服務(wù)治理(重點)
我這邊列了常見的服務(wù)治理功能,例如:
-
服務(wù)路由:
- 權(quán)重:例如機(jī)器配置高的權(quán)重高,機(jī)器配置低的權(quán)重低
- IP路由:例如某幾臺機(jī)器只能調(diào)某幾臺機(jī)器
- 分組路由:例如自動根據(jù)配置調(diào)某個分組
- 參數(shù)路由:例如根據(jù)方法名進(jìn)行讀寫分類,或者根據(jù)參數(shù)走不同的節(jié)點
- 機(jī)房路由:例如只走同機(jī)房,或者同機(jī)房優(yōu)先
-
調(diào)用授權(quán):
- 應(yīng)用授權(quán):只有授權(quán)后的應(yīng)用才能調(diào)這組服務(wù)
- token:只有token對的調(diào)這組服務(wù)
- 黑白名單:只有名單允許的才能調(diào)這組服務(wù)
-
動態(tài)分組:
- 服務(wù)端切分組:可以根據(jù)分組的情況,對服務(wù)提供者進(jìn)行一個動態(tài)的分組調(diào)度
- 客戶端切分組:可以對調(diào)用者進(jìn)行一個分組調(diào)度
-
調(diào)用限流:
- 服務(wù)端限流:服務(wù)端基于令牌桶或者漏桶模型進(jìn)行限流
- 客戶端限流:根據(jù)客戶端的標(biāo)識,進(jìn)行調(diào)用次數(shù)限流
-
灰度部署:
- 灰度上線:先啟動,驗證后在提供服務(wù)
- 預(yù)發(fā)標(biāo)識:表示該服務(wù)為預(yù)發(fā)布服務(wù)
- 接口測試:方便的提供接口自動化功能測試功能
配置下發(fā):
- 服務(wù)配置
- 全局配置
- 服務(wù)降級:
- Mock:出現(xiàn)異?;蛘邷y試情況下,返回Mock數(shù)據(jù)
- 熔斷:客戶端超時或者服務(wù)端超時
- 拒絕服務(wù):服務(wù)端壓力大時,自動拒絕服務(wù),保護(hù)自己
網(wǎng)關(guān)
RPC框架大部分場景都是P2P的,什么時候會需要一個網(wǎng)關(guān)呢?
網(wǎng)關(guān)可以提供如下功能:
- 通訊鏈路打通(例如跨機(jī)房網(wǎng)絡(luò)不通)
- 統(tǒng)一的鑒權(quán)服務(wù)
- 限流服務(wù)
- 協(xié)議轉(zhuǎn)換:外部協(xié)議轉(zhuǎn)統(tǒng)一內(nèi)部協(xié)議
- Mock:服務(wù)測試,降級等
- 其它一些統(tǒng)一處理邏輯(例如請求解析,響應(yīng)包裝)
服務(wù)注冊中心Plus
需要邏輯處理能力,例如對數(shù)據(jù)進(jìn)行篩選過濾整合,計算服務(wù)路由等功能。
同時還需要有與RPC框架交互的功能。
管理端Plus
管理端除了之前的簡單服務(wù)管理功能外,還需要提供配置信息展示,監(jiān)控信息展示,各種維度的數(shù)據(jù)展示。也就是上面提到的服務(wù)治理功能,都可以在管理端進(jìn)行管理。
另外,常見的服務(wù)治理功能,我們都可以作為開放服務(wù)供開發(fā)人員進(jìn)行一個調(diào)用。
京東實踐
第一代SAF背景
2012年初,京東從.NET轉(zhuǎn)Java。各個部門,各個業(yè)務(wù)線都沒有一個統(tǒng)一的服務(wù)化框架,有的是dubbo,有的是WebService,有的是Hessian等等。
同時各個業(yè)務(wù)系統(tǒng)自己有非常多的業(yè)務(wù)代碼。通過統(tǒng)計接口規(guī)模在1K左右,服務(wù)節(jié)點在50K左右,機(jī)器規(guī)模在8K左右,機(jī)房比較少拓?fù)浜唵巍?/p>
所以當(dāng)時的愿景和目標(biāo)比較明確:
- 京東系統(tǒng)服務(wù)化、API化的從無到有
- 統(tǒng)一京東的RPC調(diào)用框架
- 穩(wěn)定可靠
- 提供簡單的服務(wù)治理功能
第一代SAF選擇
OK,結(jié)合我們的情況和上面的一些選型小結(jié),我們當(dāng)時的選擇如下:
- RPC框架:基于dubbo2.3.2做配置擴(kuò)展,以及功能擴(kuò)展包括rest(resteasy)、webservice(cxf)、kryo/thrift序列化、調(diào)用壓縮等
- 注冊中心:Zookeeper,RPC框架直接接入
- 監(jiān)控中心:監(jiān)控服務(wù)+HBase
- 管理平臺:讀取Zookeeper做管理平臺,提供基本的上下線、黑白名單等功能
于2012年4月上線,最大規(guī)模時,接口數(shù)3K,接入最大IP數(shù)20K。
第二代JSF背景
隨著京東業(yè)務(wù)的不斷快速增長,接口、機(jī)器數(shù)也呈數(shù)量級增長。
同時京東成立子公司,在全國各地新建機(jī)房,部署結(jié)構(gòu)也變得比較復(fù)雜。
加上SAF遺留的一些問題,大概面臨如下幾點:
- RPC框架較重,性能有提高的空間
- 注冊中心無業(yè)務(wù)邏輯,直接對外暴露
- 京東復(fù)雜的部署架構(gòu)需要更強(qiáng)大靈活的服務(wù)治理功能
- 監(jiān)控數(shù)據(jù)不完整,維度不夠
- 無應(yīng)用依賴關(guān)系
- 跨語言調(diào)用需求
第二代JSF選擇
所以在2014年初,我們進(jìn)行了第二代JSF的一個全部自研過程。
我們主要做了如下技術(shù)選型:(全部自研)
- RPC框架:輕量級,更佳的性能,兼容舊版本協(xié)議
- 注冊中心:基于DB作為數(shù)據(jù)源,前置Index服務(wù);支持十倍接入量;部分邏輯放在注冊中心減少客戶端負(fù)擔(dān)
- 監(jiān)控中心:監(jiān)控Proxy服務(wù)+InfluxDB(2015后改為ElasticSearch)
- 管理端:基于DB,功能更強(qiáng)大,提供完善的服務(wù)治理管理功能;打通京東應(yīng)用管理平臺,提供應(yīng)用依賴關(guān)系梳理;
- HTTP網(wǎng)關(guān):基于Netty,支持跨語言調(diào)用
開發(fā)周期:7人/年(2014.1-2015.1)。包括開發(fā)、測試、預(yù)發(fā)、上線、推廣。
JSF架構(gòu)簡圖

JSF注冊中心
京東的注冊中心是自研的,基于DB做的數(shù)據(jù)最終一致,也就是上面說的AP系統(tǒng)。
注冊中心主要實現(xiàn)的就是服務(wù)列表的注冊訂閱推送,服務(wù)配置的獲取下發(fā),服務(wù)狀態(tài)的實時查看等功能。
注冊中心節(jié)點是無狀態(tài)的,可水平擴(kuò)展的。整個注冊中心集群下的所有注冊中心幾點都是等價的。
每個機(jī)房部署多個注冊中心節(jié)點。同機(jī)房的RPC框架會優(yōu)先連本機(jī)房的注冊中心節(jié)點。
主要亮點如下:
- 引入Index服務(wù)概念
該服務(wù)就是一個最簡單HTTP的服務(wù),用于找注冊中心節(jié)點(同機(jī)房或者壓力最小或者其它特定場景),可以認(rèn)為是不會掛的服務(wù),
RPC框架會優(yōu)先連該服務(wù)拿注冊中心地址,這樣子的好處是注冊中心地址變化后,RPC框架不用修改任何設(shè)置。 - 注冊中心內(nèi)存有服務(wù)列表全量緩存,連不上數(shù)據(jù)庫也保證可讀
- 數(shù)據(jù)庫的數(shù)據(jù)結(jié)構(gòu)更適合各種維度展示、過濾、分析等
例如根據(jù)分組,IP,應(yīng)用,機(jī)房等不同維度 - 注冊中心就是個JSF服務(wù),監(jiān)控到壓力大即可進(jìn)行動態(tài)水平擴(kuò)展
dogfooding,注冊中心其實是第一個JSF接口 - 服務(wù)列表推送邏輯改進(jìn)
例如原來100個Provider,現(xiàn)在加1個節(jié)點,之前的SAF是需要下發(fā)101個節(jié)點,自己判斷加了哪個節(jié)點,進(jìn)行長鏈接建立;
現(xiàn)在的改進(jìn)是:修改為下發(fā)一個add事件,告知RPC框架加了1個節(jié)點,RPC框架進(jìn)行長鏈接建立;
這樣做大大減少了推送的數(shù)據(jù)量。 - 注冊中心與RPC框架可各種交互
注冊中心和RPC框架是長鏈接,而且JSF是支持Callback的,注冊中心可以調(diào)用RPC框架進(jìn)行服務(wù)列表變化之外的操作;
例如查看狀態(tài),查看配置,配置下發(fā)等
JSF RPC框架
RPC框架作為服務(wù)化里面的最基本的組件,其實都大同小異,因為RPC調(diào)用都繞不開代理、網(wǎng)絡(luò)、序列化這些操作。
JSF的RPC框架也類似,主要分為圖中的幾個模塊:

下面大概列下一些功能特性:
- Config:Spring/API/Annotation
- Proxy: Javassist/JDK
- Invoker/Filter:內(nèi)置+自定義,F(xiàn)ilter可擴(kuò)展
- Client:Failover(默認(rèn))/FailFast/TransportPinpoint/MultiClientProxy
- 調(diào)用方式:同步(默認(rèn))/異步并行/異步回調(diào)/Callback/泛化
- Loadbalance:Random(默認(rèn))/Roundrobin/ConsistentHash/ LocalPreference/LeastActiveCall
- 路由:參數(shù)路由,分組路由,(IP級別路由邏輯在注冊中心做)
- 長連接維護(hù):可用/死亡/亞健康
- 協(xié)議:JSF(默認(rèn))/SAF(dubbo)/HTTP/Telnet/HTTP2
- 第三方:REST/Webservice
- 序列化:MsgPack(默認(rèn))/Hessian/Json/Java/protobuf(c++)
- 壓縮:Snappy/LZMA
- 網(wǎng)絡(luò):基于Netty4.0,長連接復(fù)用
- 線程模型:BOSS+WORKER+BIZ
- 容災(zāi):本地文件
- 請求上下文:IP,參數(shù),隱式傳參
- 事件監(jiān)聽:響應(yīng)事件,連接事件,狀態(tài)事件
- 分布式跟蹤支持:進(jìn)行數(shù)據(jù)埋點
JSF管理平臺
提供強(qiáng)大管理功能,包括服務(wù)管理,監(jiān)控管理,注冊中心管理等功能。



我們針對服務(wù)治理的功能,提供了很多API,可以授權(quán)給開發(fā)人員或者外部系統(tǒng)使用。
例如單元測試調(diào)用,限流配置/開關(guān),動態(tài)分組,上下線等都提供了開放API。
JSF HTTP網(wǎng)關(guān)
網(wǎng)關(guān)是為了方便跨語言通過HTTP+JSON調(diào)用JSF服務(wù),而不需要使用JSF的RPC框架。
特性如下:
- 基于Netty4.0實現(xiàn)HTTP網(wǎng)關(guān),沒有使用Servlet容器,輕量高效。
- 支持服務(wù)自動發(fā)現(xiàn)
一般的HTTP服務(wù),外面為了解決單點問題,都會用域名+VIP等實現(xiàn)高可用,故障轉(zhuǎn)移等;
現(xiàn)在網(wǎng)關(guān)同時原生接入了JSF的注冊中心,知道了服務(wù)的提供者信息(JSF協(xié)議支持HTTP調(diào)用)。
服務(wù)提供者也不用關(guān)系擴(kuò)容縮容導(dǎo)致服務(wù)的IP端口發(fā)生變化,網(wǎng)關(guān)會自動維護(hù)服務(wù)列表。 - 服務(wù)限流
針對方法級+應(yīng)用進(jìn)行授權(quán),固定時間只能調(diào)用指定次數(shù)。
同一個方法也只能占用網(wǎng)關(guān)內(nèi)的部分線程 - 結(jié)果統(tǒng)一包裝
對異常等響應(yīng)進(jìn)行包裝
JSF遇到京東彈性云
京東的JSF服務(wù)開發(fā)在京東彈性云的研發(fā)推廣之前完成,自從京東彈性云落地以來,也遇到不少問題。
例如:
- 硬件指標(biāo):例如使用JDK獲取的Docker的指標(biāo)有些是物理機(jī)的,我們需要特殊處理
- 網(wǎng)絡(luò):結(jié)合京東的“胖”容器,每個容器其實有實際IP,對外提供服務(wù)
- 輕量:提高啟動速度
- 開放服務(wù):在容器銷毀或者非優(yōu)雅停機(jī)的情況下,提供API進(jìn)行服務(wù)治理
JSF規(guī)模
- 接口數(shù):萬級
- 服務(wù)節(jié)點數(shù):百萬級
- 接入實例數(shù):十萬級
- 框架調(diào)用量:每天千億級別
- 監(jiān)控數(shù)據(jù):每天120億條數(shù)據(jù), 1.2T數(shù)據(jù)量
- HTTP網(wǎng)關(guān):每天百億級別
總結(jié)
- 沒有最好,只有最適合!
意思就是不要人云亦云,盲目看大公司用什么,現(xiàn)在什么最新,或者什么性能最好。
因為架構(gòu)不是讓你一下子設(shè)計出來使用一輩子,好的架構(gòu)都是慢慢演化而來的。
不同的架構(gòu)會做出不同的技術(shù)選型。所以無論什么時候都要結(jié)合自己的現(xiàn)狀以及未來幾年的規(guī)劃,來進(jìn)行技術(shù)選型。 - It’s just the beginning!
其實服務(wù)化框架的選擇只是開始,真正的變革是選擇一個統(tǒng)一的服務(wù)化框架后,公司整體業(yè)務(wù)和開發(fā)的變革。建議大家有空可以看看康威定律。