HSF原理分析

_吹雪_發表於2018-08-05

1. HSF的基本概念

HSF全稱為High-Speed Service Framework,旨在為淘系的應用提供一個分散式的服務框架,HSF從分散式應用層面以及統一的釋出/呼叫方式層面為大家提供支援,從而可以很容易的開發分散式的應用以及提供或使用公用功能模組,而不用考慮分散式領域中的各種細節技術,例如遠端通訊、效能損耗、呼叫的透明化、同步/非同步呼叫方式的實現等等問題。

2. 知識準備

2.1 物件的序列化

物件的序列化過程在RPC過程中是很重要的一個環節,序列化對於遠端呼叫的響應速度、吞吐量、網路頻寬消耗等同樣也起著至關重要的作用。
在HSF1.0時只支援兩種序列化方式:java 和 hessian,在HSF2.0之後就支援了五種序列化方式:java、hessian、hessian2、json、kyro。
但是目前版本中常用的序列化方式還是java 和 hessian兩種。 如果想細緻的瞭解也可以多做了解。

2.2 動態代理

對於消費方來說,所存在的只有一個介面,雖然底層的實現原理我們知道,但是為了在使用時的高度透明,在JAVA語言層面上的表現形式則是通過動態代理的方式實現的,很多的邏輯都在InvocationHandler 中處理的。關於如何實現動態代理,還動態代理的一些使用的細節也可以稍作了解。

2.3 網路通訊NIO

如果在網路傳輸過程中,採取普通的BIO,會有很多的問題存在,例如如果呼叫端有多個請求過來,那麼就得需要多個執行緒去處理,每個執行緒都使用獨立的連線,在遠端的提供者端有對應的多個執行緒來執行相應的服務。這種方式會使得呼叫者和提供者之間建立大量的連線,而且是阻塞的方式,連線並不能得到充分的利用(摘自《大型網站系統與JAVA中介軟體》)。採用NIO則就可以避免這樣的損耗,但是HSF在使用時並不是採用直接的NIO程式設計,而是通過第三方的框架Netty。

3. HSF 實現原理

3.1 提供服務的大致流程如下:

  • server啟動時候向configserver註冊
  • client啟動時候向configserver請求list
  • client快取list,發現不可用的server,從快取中remove
  • configserver通過心跳包維護可用server的list
  • list有更新的時候,configserver通過帶version的報文通知client更新

從以上幾個問題出發,看下HSF的實現方式。

3.2 HSF的整體實現方式

從圖中可以看出,HSF的實現方式可以理解為是C/S的架構,但是和傳統的C/S架構相比還是有很大的不同,HSF沒有真正的伺服器,每個應用都可以成為服務的呼叫方和提供方。具體工作方式如下:

ConfigServer:遠端呼叫對端的地址就是由ConfigServer 來推送的,這樣使用者只需要配置自己的服務端或者消費端,不需要對自己的地址進行管理。

Diamond:持久化的配置中心,用於配置服務呼叫的規則。

服務:服務是呼叫方和提供方交流的依憑,一般是一個介面,表示一個業務行為以及相關的資料含義。通過使用HSFApiProviderBean能夠暴露一個服務,將機器的地址註冊到configserver,並且能夠通過12200埠進行服務提供,通過HSFApiConsumerBean能夠包裝出一個客戶端,它是服務介面的一個代理,並且它從configserver上訂閱了服務的地址列表,能夠在這個列表上完成隨機呼叫,做到負載均衡與HA((High Available,高可用性群集)。

網路通訊:HSF的底層網路通訊是使用netty框架實現的,是基於epoll的NIO的網路通訊框架,HSF在此使用的是長連線,通過合理的服務部署及負債均衡,基本不存在I/O方面的限制。

4. HSF設計架構

關於HSF的架構基本可以理解為C/S結構設計方式。(雖然HSF沒有自己的伺服器)
Server端除了configServer外還有一個diamond用來儲存一些持久化的配置資訊,這裡不進行過多的介紹。

Client是HSF的重點,下面是各模組的功能介紹:
Proxy:這一層主要負責介面的代理。基本上所有的RPC框架都會用到代理模式,相信大家不陌生。需要注意的是HSF的代理層還進行了軟負載和單元化的處理。
Remoting:這一層是HSF的應用層協議,定義了報文格式,各個欄位的含義等資訊,內容比較多,之後單獨寫一篇文章來介紹。
Processer:這一層主要是處理HSF自身的業務邏輯,包括埋點、限流、鑑權等。
Netty:上面三層會將一次服務呼叫或者服務返回包裝成一個報文,然後通過這層傳輸。

HSF呼叫流程

上圖是HSF整個的呼叫過程,從左向右看:
第一條線路相當於consumer進行服務呼叫的過程,首先經過proxy層,將請求經過代理類包裝出去;然後是Remoting層進行協議的包裝,(處理HSF自身的業務邏輯,包括埋點、限流、鑑權等)最後io層傳送出去。
第二條線路相當於provider將結果返回後解析的過程,與上一流程剛好相反。
右邊的provider兩條呼叫流程相信大家都能按照上面的過程理解,就不一一講解了。

5. HSF處理請求流程

5.1 HSF提供端初始化

5.2 HSF消費端初始化

5.3 消費方請求到提供方,響應一次呼叫

5.4 實現細節

心跳檢測:
1、客戶端主動關閉連線:客戶端第一次與服務端建立連結後,就會週期性(27s)傳送心跳包的callback呼叫,如果連續三次收不到服務端的心跳包回應,客戶端主動關閉連結
2、服務端主動關閉連線:當連線59s沒有呼叫(對方網路不可用,或者full gc太久),相當於兩次(2*27s)收不到心跳包的時間

從上圖可以看出RPC要解決以下幾個問題:

如何解決網路通訊問題,主要是通過在客戶端和伺服器之間建立TCP連線,遠端過程呼叫的所有交換的資料都在這個連線裡傳輸。連線可以是按需連線,呼叫結束後就斷掉,也可以是長連線,多個遠端過程呼叫共享同一個連線。

如何解決定址問題,客戶端如何找到制定的服務端,也就是保證準確有效的完成一次服務呼叫的前提。

引數的傳遞及服務端在收到客戶端請求後如何實現其具體功能並返回,由於網路傳輸協議是二進位制的,記憶體中的引數值必須要解決序列化,反序列化,以及對半包,粘包的處理。

6. HSF的優點:

6.1 服務的自動註冊、發現

通過註冊中心,實現服務的註冊/登出與服務的發現。當服務啟動後,會呼叫publish來將服務釋出到中心,而服務的消費者,通過呼叫訂閱介面傳入的監聽器來更新服務提供者列表。HSF提供了三種註冊中心實現,分別是ConfigServer,Zookeper,和配置檔案模式。

這裡僅對Zookeper進行分析,ZookeeperMetadataAddressService 實現了上述介面,在初始化中例項化了一個ZookeeperRegistry來進行管理,其使用了一個封裝了zookeperclient的例項-ZkclientZookeeperClient,在註冊服務的時候根據url引數中的Constants.DYNAMIC_KEY來確定建立Persistent節點還是Ephemeral節點,Ephemeral節點生命週期與本機連線繫結,這樣就可以實現本機離線後的服務自動登出的功能。

6.2 服務提供者與消費者之間長連線

HSF採用長連線方式進行通訊,相比短連線,長連線更具效能優勢,避免連線重複建立與銷燬帶來的緩衝區申請與釋放。HSF抽象了連線AbstractClient(Client),並採用了netty框架作為底層實現。netty是一個效能非常優秀的通訊框架,基於Reactor模式,內部採用了管線模式來解耦不同層次的邏輯之間的耦合問題。HSF為了強化TCP連線的可用性,增加HeartBeat功能,使用了一個Netty提供的 HashedWheelTimer 的定時任務排程器來執行心跳包的傳送(補充:此HashedWheelTimer原理採用輪片式的桶結構,避免每次操作對全部任務的迭代操作,只對將要到期的桶進行操作,此原理也可用於快取系統設計,在需要進行垃圾回收的情況下只需要按照桶為單位進行記憶體回收)。

6.3 非侵入性

HSF最大優點是非侵入性,它使用了JAVA的Proxy機制來實現這一特點,在通過xml配置檔案配置Consumer的時候,實際上是呼叫了 HSFApiConsumerBean ,在它的初始化方法中,讀取了配置的實現介面,並在ProcessComponent中用一個封裝了Proxy註冊功能,並實現了InvocationHandler介面的類HSFServiceProxy去管理。使用者在自己的程式碼中無需做任何特殊處理,就像使用本地方法一樣去呼叫其方法。

6.4 版本管理

這一特性可以很靈活的幫助上線運營的服務在升級過程中避免服務不可用的情況。

6.5 服務治理

可以通過網頁視覺化檢視、管理、測試服務的可用。

6.6 擴充套件靈活

可以接入自動服務降級功能(熔斷) - 根據配置或服務的執行結果,在呼叫級控制服務是否呼叫執行,避免服務整體癱瘓,提升服務的可用性。

參考:
https://blog.csdn.net/qq_16681169/article/details/72512819

相關文章