1. 程式人生 > >【轉】服務化框架技術選型與京東JSF解密

【轉】服務化框架技術選型與京東JSF解密

宣告:本文轉載自微信公眾號“開濤的部落格”,轉載務必宣告。

作者:章耿,原京東資深架構師,曾負責京東服務框架,配置中心等基礎平臺。近十年工作經驗,專注於基礎中介軟體等底層技術架構,對分散式系統/服務化/DevOps建設有一定經驗。

  |前言

  首先本文不討論為什麼要服務化,包括服務化的優點缺點。

  其次本文也不討論什麼是微服務,也不討論微服務和SOA的區別。

  最後本文也不討論哪個技術最優。

  |服務化框架構成最基本的服務框架

  基本的服務化框架包括如下模組:統一的RPC框架,服務註冊中心,管理平臺。

  有了這三個模組,就能實現基本的服務化。下面對三個模組進行具體分析。

  RPC框架選型

  為什麼一定要是統一的RPC框架,而不是隨便啥框架,這裡主要是為了技術對齊,減少開發人員的學習成本,減少團隊間溝通成本。

  好,那麼選擇一個RPC框架,我們都需要考量什麼東西呢?

  這裡我總結下:

  • 程式碼規範:例如是對已有程式碼透明,還是程式碼生成;

  • 通訊協議:例如是TCP還是HTTP;

  • 序列化協議:例如是二進位制還是文字,是否需要跨語言,效能;

  • IO模型:非同步/同步,阻塞/非阻塞;

  • 負載均衡:客戶端軟負載,代理模式,服務端負載。

  另外如果是從開源裡面選擇,那麼我們還需要考量:

  • 成熟度:包括學習成本,社群熱度,文件數,是否有團隊維護,穩定性(盲目追求的不一定是最適合);

  • 可擴充套件性:是否有SPI支援擴充套件,是否支援上下相容;

  • 跨語言:是否支援跨語言;

  • 效能:要想作為RPC框架,效能一般都不會太差 [滑稽臉]。

  下面是常見的一些開源框架的比較,大家可以看一下。

  thrift

  RESTful

  dubbo

  gRPC

  程式碼規範

  基於Thrift的IDL生成程式碼

  基於JAX-RS規範

  無程式碼入侵

  基於.Proto生成程式碼

  通訊協議

  TCP

  HTTP

  TCP

  HTTP/2

  序列化協議

  thrift

  JSON

  多協議支援,預設hessian

  protobuf

  IO框架

  Thrift自帶

  Servlet容器

  Netty3

  Netty4

  負載均衡

  

  

  客戶端軟負載

  

  跨語言

  多種語言

  多種語言

  Java

  多種語言

  可擴充套件性

  一般

  

  

  

  Ps:SOAP,RMI,Hessian,ICE就不列舉了。

  選型小結:

  • 如果需要與前端互動的,適合短連結、跨語言的RPC框架,例如RESTful、gRPC等;

  • 如果純粹後臺互動的,適合長連結、序列化為二進位制的RPC框架,例如thrift、dubbo等更高效;

  • 如果是小公司,新公司從頭開始推廣服務化框架的,可以選擇規範化的RPC框架,例如thrift、RESTful、gRPC;

  • 如果是已有大量業務程式碼的再推廣服務框架的,那麼最好選擇無程式碼入侵的RPC框架,例如dubbo、RESTful。

  註冊中心選型

  註冊中心相當於是服務提供者和服務呼叫者之間的引路人,在服務治理中的作用極為重要。

  選擇註冊中心基本要考量:

  • 服務註冊:接收註冊資訊的方式;

  • 服務訂閱:返回訂閱資訊的方式,推還是拉;

  • 狀態檢測:檢測服務端存活狀態。

  重點提一下這個狀態檢測,因為這個要是檢測不準確會誤判,導致嚴重後果,例如Zookeeper根據服務端註冊的臨時節點進行狀態檢測,如果服務端和Zookeeper之間的網路閃斷,導致Zookeeper認為服務端已經死了,從而摘掉這個節點;但是其實客戶端和服務端直接的網路是好的,這樣就有可能把節點全部摘掉,導致無可用節點。

  如果是從開源裡面選擇,那麼還需要考量:

  • 成熟度:包括學習成本,社群熱度,文件數(盲目追求的不一定是最適合);

  • 維護成本:註冊中心維護;

  • 資料解構:是否能快速定位結果,是否能遍歷;

  • 效能和穩定性;

  • CAP原則:CP(關注一致性)還是AP(關注可用性)。

  下面是常見的一些使用開源專案做註冊中心的比較,大家可以看一下。

  ZooKeeper

  etcd

  Consul

  Eureka

  一致性

  強一致性paxos

  強一致性Raft

  強一致性Raft

  弱一致性

  資料結構

  Tree

  K/V

  K/V

  K/V

  通訊協議

  TCP

  HTTP、gRPC

  HTTP、DNS

  HTTP

  客戶端

  ZKClient

  /

  /

  Eureka-client

  CAP原則

  CP

  CP

  CP

  AP

  Ps:Redis和MySQL沒有列舉。

  選型小結:

  • 規模小選擇CP,RPC框架可以直接接入資料來源;

  • 規模大選擇AP, RPC框架不可以直接接入資料來源;

  • 存在跨機房,跨地域的儘量不要選有強一致性協議的註冊中心;

  • RPC框架必須要有註冊中心不可用的容災策略;

  • 服務狀態檢測十分重要。

  簡易管理端

  管理端沒啥特殊要求,最起碼能看到服務提供者和呼叫者即可。

  |完善的服務化框架

  如果需要一個完善的服務化框架,那麼必須增加外部模組,常見的模組如下圖:

  

  介面文件管理

  提供一個介面文件管理以及介面查詢的入口,可以是一個公共的WIKI,也可以是獨立的系統,等等。

  這裡可以定義介面的文件,包括介面描述,方法定義,欄位定義。可以定義介面的SLA,包括支援的併發數,tp99多少,建議配置是什麼。還有就是介面的負責人等一些查詢的入口。

  配置中心

  提供一個配置管理的地方,這裡說的配置主要指的是服務相關的一些配置。

  配置包括分組配置、路由策略、黑白名單、降級開關、限流資訊、超時時間、重試次數等等,任何可以動態變更的所有資料。

  這樣服務提供者和服務呼叫者可以不需要重啟自己的應用,直接進行配置的變更。

  配置中心可以獨立於註冊中心,也可以和註冊中心合併。

  監控中心

  監控服務關注介面維度,例項(例如所在JVM例項)維度的資料。RPC框架可以定時上報呼叫次數,耗時,異常等資訊。監控中心可以統計出服務質量資訊,也可以進行監控報警。

  分散式跟蹤

  區別於監控中心,以呼叫鏈的模式對服務進行。RPC框架作為分散式跟蹤系統的一個天然埋點,可以很好的進行一個數據輸出。

  服務治理(重點)

  我這邊列了常見的服務治理功能,例如:

  • 服務路由:

  1. 權重:例如機器配置高的權重高,機器配置低的權重低;

  2. IP路由:例如某幾臺機器只能調某幾臺機器;

  3. 分組路由:例如自動根據配置調某個分組;

  4. 引數路由:例如根據方法名進行讀寫分類,或者根據引數走不同的節點;

  5. 機房路由:例如只走同機房,或者同機房優先。

呼叫授權:

  • 應用授權:只有授權後的應用才能調這組服務

  • token:只有token對的調這組服務

  • 黑白名單:只有名單允許的才能調這組服務

動態分組:

  1. 服務端切分組:可以根據分組的情況,對服務提供者進行一個動態的分組排程;

  2. 客戶端切分組:可以對呼叫者進行一個分組排程。

呼叫限流:

  1. 服務端限流:服務端基於令牌桶或者漏桶模型進行限流;

  2. 客戶端限流:根據客戶端的標識,進行呼叫次數限流。

灰度部署:

  1. 灰度上線:先啟動,驗證後在提供服務;

  2. 預發標識:表示該服務為預釋出服務;

  3. 介面測試:方便的提供介面自動化功能測試功能。

配置下發:

  1. 服務配置;

  2. 全域性配置。

服務降級:

  1. Mock:出現異常或者測試情況下,返回Mock資料;

  2. 熔斷:客戶端超時或者服務端超時;

  3. 拒絕服務:服務端壓力大時,自動拒絕服務,保護自己。

閘道器

  RPC框架大部分場景都是自己呼叫的,什麼時候會需要一個閘道器呢?

  閘道器可以提供如下功能:

  • 統一的鑑權服務;

  • 限流服務;

  • 協議轉換:外部協議轉統一內部協議;

  • Mock:服務測試,降級等;

  • 其它一些統一處理邏輯(例如請求解析,響應包裝)。

  服務註冊中心Plus

  需要邏輯處理能力,例如對資料進行篩選過濾整合,計算服務路由等功能

  同時還需要有與RPC框架互動的功能。

  管理端Plus

  管理端除了之前的簡單服務管理功能外,還需要提供配置資訊展示,監控資訊展示,各種維度的資料展示。也就是下面提到的服務治理功能,都可以在管理端進行管理。另外,常見的服務治理功能,我們都可以作為開放服務供開發人員進行一個呼叫。

  |京東實踐第一代SAF背景

  2012年初,京東從.NET轉Java。各個部門,各個業務線都沒有一個統一的服務化框架,有的是dubbo,有的是WebService,有的是Hessian等等。

  同時各個業務系統自己有非常多的業務程式碼。通過統計介面規模在1K左右,服務節點在50K左右,機器規模在8K左右,機房比較少拓撲簡單。

  所以當時的願景和目標比較明確:

  • 京東系統服務化、API化的從無到有;

  • 統一京東的RPC呼叫框架;

  • 穩定可靠;

  • 提供簡單的服務治理功能。

  第一代SAF選擇

  OK,結合我們的情況和上面的一些選型小結,我們當時的選擇如下:

  • RPC框架:基於dubbo2.3.2做配置擴充套件,以及功能擴充套件包括:rest(resteasy)、webservice(cxf)、kryo/thrift序列化、呼叫壓縮等;

  • 註冊中心:Zookeeper,RPC框架直接接入資料來源;

  • 監控中心:監控服務+HBase;

  • 管理平臺:讀取Zookeeper做管理平臺,提供基本的上下線、黑白名單等功能。

  於2012年4月上線,最大規模時,介面數3K,接入最大IP數20K。

  第二代JSF背景

  隨著京東業務的不斷快速增長,介面、機器數也呈數量級增長。同時京東成立子公司,在全國各地新建機房,部署結構也變得比較複雜。

  加上SAF遺留的一些問題,大概面臨如下幾點:

  • RPC框架較重,效能有提高的空間;

  • 註冊中心無業務邏輯,直接對外暴露;

  • 京東複雜的部署架構需要更強大靈活的服務治理功能;

  • 監控資料不完整,維度不夠;

  • 無應用依賴關係;

  • 跨語言呼叫需求。

  第二代JSF選擇

  所以在2014年初,我們進行了第二代JSF的一個全部自研過程。

  我們主要做了如下技術選型:(全部自研)

  • RPC框架:輕量級,更佳的效能,相容舊版本協議;

  • 註冊中心:基於DB作為資料來源,前置Index服務;支援十倍接入量;部分邏輯放在註冊中心減少客戶端負擔;

  • 監控中心:監控Proxy服務+InfluxDB(2015後改為ElasticSearch);

  • 管理端:基於DB,功能更強大,提供完善的服務治理管理功能;打通京東應用管理平臺,提供應用依賴關係梳理;

  • HTTP閘道器:基於Netty,支援跨語言呼叫。

  開發週期:7人/年(2014.1-2015.1)。包括開發、測試、預發、上線、推廣。

  JSF架構簡圖

  

  JSF註冊中心

  京東的註冊中心是自研的,基於DB做的資料最終一致,也就是上面說的AP系統。註冊中心主要實現的就是服務列表的註冊訂閱推送,服務配置的獲取下發,服務狀態的實時檢視等功能。註冊中心節點是無狀態的,可水平擴充套件的。整個註冊中心叢集下的所有註冊中心幾點都是等價的。

  每個機房部署多個註冊中心節點。同機房的RPC框架會優先連本機房的註冊中心節點。

  主要亮點如下:

  • 引入Index服務概念:該服務就是一個最簡單HTTP的服務,用於找註冊中心節點(同機房或者壓力最小或者其它特定場景),可以認為是不會掛的服務,RPC框架會優先連該服務拿註冊中心地址,這樣子的好處是註冊中心地址變化後,RPC框架不用修改任何設定;

  • 註冊中心記憶體有服務列表全量快取,連不上資料庫也保證可讀;

  • 資料庫的資料結構更適合各種維度展示、過濾、分析等,例如根據分組/IP/應用/機房等不同維度;

  • 註冊中心就是個JSF服務,監控到壓力大即可進行動態水平擴充套件,dogfooding,註冊中心其實是第一個JSF介面;

  • 服務列表推送邏輯改進:例如原來100個Provider,現在加1個節點,之前的SAF是需要下發101個節點,自己判斷加了哪個節點,進行長連結建立;現在的改進是:修改為下發一個add事件,告知RPC框架加了1個節點,RPC框架進行長連結建立;這樣做大大減少了推送的資料量;

  • 註冊中心與RPC框架可各種互動:註冊中心和RPC框架是長連結,而且JSF是支援Callback的,註冊中心可以呼叫RPC框架進行服務列表變化之外的操作;例如檢視狀態,檢視配置,配置下發等。

  JSF RPC框架

  

  RPC框架作為服務化裡面的最基本的元件,其實都大同小異,因為RPC呼叫都繞不開代理、網路、序列化這些操作。

  JSF的RPC框架也類似,主要分為圖中的幾個模組,下面大概列下一些功能特性:

  • Config:Spring/API/Annotation

  • Proxy: Javassist/JDK

  • Invoker/Filter:內建+自定義,Filter可擴充套件

  • Client:Failover(預設)/FailFast/TransportPinpoint/MultiClientProxy

  • 呼叫方式:同步(預設)/非同步並行/非同步回撥/Callback/泛化

  • Loadbalance:Random(預設)/Roundrobin/ConsistentHash/ LocalPreference/LeastActiveCall

  • 路由:引數路由,分組路由,(IP級別路由邏輯在註冊中心做)

  • 長連線維護:可用/死亡/亞健康

  • 協議:JSF(預設)/SAF(dubbo)/HTTP/Telnet/HTTP2

  • 第三方:REST/Webservice

  • 序列化:MsgPack(預設)/Hessian/Json/Java/protobuf(c++)

  • 壓縮:Snappy/LZMA

  • 網路:基於Netty4.0,長連線複用

  • 執行緒模型:BOSS+WORKER+BIZ

  • 容災:本地檔案

  • 請求上下文:IP,引數,隱式傳參

  • 事件監聽:響應事件,連線事件,狀態事件

  • 分散式跟蹤支援:進行資料埋點

  JSF管理平臺

  提供強大管理功能,包括服務管理,監控管理,註冊中心管理等功能。

  

  

  

  我們針對服務治理的功能,提供了很多API,可以授權給開發人員或者外部系統使用。例如單元測試呼叫,限流配置/開關,動態分組,上下線等都提供了開放API。

  JSF HTTP閘道器

  閘道器是為了方便跨語言通過HTTP+JSON呼叫JSF服務,而不需要使用JSF的RPC框架。

  特性如下:

  • 基於Netty4.0實現HTTP閘道器,沒有使用Servlet容器,輕量高效;

  • 支援服務自動發現:一般的HTTP服務,外面為了解決單點問題,都會用域名+VIP等實現高可用,故障轉移等;現在網關同時原生接入了JSF的註冊中心,知道了服務的提供者資訊(JSF協議支援HTTP呼叫);服務提供者也不用關係擴容縮容導致服務的IP埠發生變化,閘道器會自動維護服務列表;

  • 服務限流:針對方法級+應用進行授權,固定時間只能呼叫指定次數;同一個方法也只能佔用閘道器內的部分執行緒;

  • 結果統一包裝:對異常等響應進行包裝。

  JSF遇到京東彈性雲

  京東的JSF服務開發在京東彈性雲的研發推廣之前完成,自從京東彈性雲落地以來,也遇到不少問題。例如:

  • 硬體指標:例如使用JDK獲取的Docker的指標有些是物理機的,我們需要特殊處理;

  • 網路:結合京東的“胖”容器,每個容器其實有實際IP,對外提供服務;

  • 輕量:提高啟動速度;

  • 開放服務:在容器銷燬或者非優雅停機的情況下,提供API進行服務治理。

  JSF規模

  • 介面數:萬級

  • 服務節點數:百萬級

  • 接入例項數:十萬級

  • 框架呼叫量:每天千億級別

  • 監控資料:每天120億條資料, 1.2T資料量

  • HTTP閘道器:每天百億級別

  |總結

  沒有最好,只有最適合!意思就是不要人云亦云,盲目看大公司用什麼,現在什麼最新,或者什麼效能最好。

  因為架構不是讓你一下子設計出來使用一輩子,好的架構都是慢慢演化而來的。不同的架構會做出不同的技術選型。所以無論什麼時候都要結合自己的現狀以及未來幾年的規劃,來進行技術選型。

  It’s just the beginning!

  服務化框架的選擇只是開始,真正的變革是選擇後,公司整體業務和開發的變革。這個大家有空可以看看康威定律。

  ======公眾號推薦======

  IPD-Chat為京東商城基礎平臺部門官方公眾號。

  基礎平臺部門包括資料庫、容器叢集、中介軟體、圖片儲存、應用架構、機器學習、系統保障等技術方向。運營多個數據中心數萬臺伺服器,支撐京東無數線上任務。IPD-Chat公眾號會對基礎平臺目前的技術實踐方案、最新專案動態等進行分享,純技術乾貨,共同尋求突破~歡迎大家多多關注、共同交流!

  『中生代技術』:連線技術大咖的橋樑,促進科技技術的交流。