HSF學習總結

_吹雪_發表於2018-10-12

HSF其實是一個RPC框架,RPC是Remote Procedure Call,就是遠端服務呼叫.
這個功能為什麼要寫個框架而不是十幾行程式碼呢,因為在分散式場景下並不是這種點對點通訊的模式。
rpc要素:where how
地址:註冊中心ConfigServer, 這個中心用來管理整個分散式叢集裡所有的服務對應服務提供者ip的對應關係,
這玩意是不能寫在配置檔案裡的,在分散式場景下擴容、縮容機器ip的變化實在是太正常了,
必須要有一個註冊中心來管理地址;hsf的註冊中心是ConfigServer;以前開源的rpc框架dubbo也有自己的 註冊中心,
像twitter的fonango的註冊中心是zk
介面:基於介面程式設計,不像http請求返回json資料或者json rpc協議或者webservice那樣都有一個共同的問題,
就是對開發人員不友好,換句話說就是需要侵入業務流程,不得不讓上層的呼叫為rpc呼叫寫一些複雜醜陋的程式碼

configServer可以看成一個具有聚合功能的記憶體資料庫, 服務提供者將自己的地址和提供的服務註冊到ConfigServer,服務消費者要呼叫的時候會向ConfigServer查詢,其實是訂閱這個服務,ConfigServer知道它要訂閱之後會做一個聚合的操作,聚合之前對應關係是ip對應服務列表,聚合之後是服務對應ip列表, 聚合之後會向服務訂閱者推送這個資料,會告訴訂閱者提供這個服務的地址有哪些,後面就沒有ConfigServer的事情了,服務消費者拿到這個地址列表之後會隨機選擇一臺機器發起呼叫,後面rpc整個流程都是點對點,不牽涉到任何環境的變更。

沒有地址列表,談rpc是沒有意義的。 地址列表永遠是rpc這個場景下最最重要的東西;

ConfigServer是推送模式,不光在消費者第一次訂閱時會推送, 後面每當服務提供者變化時都會推送;
這種推送模型有哪些好的和不好的地方 ?

真正的環境只有兩套,一個線上環境和線下環境,它們之間的網路是隔離的,但預發環境和線上環境是通的,
但怎麼保證預發環境的detail不會呼叫線上的ic呢?其實靠的就是ConfigServer,預發的ConfigServer和線上的ConfigServer是不同的,預發的detail連的是預發的ConfigServer,預發的ConfigServer肯定沒有線上ic的地址,所有永遠不會呼叫到線上的ic,集團的這些環境都是通過中介軟體的ConfigServer這個產品進行區分的,就是線下環境會有效能環境、二套環境啊,ConfigServer不同罷了,裡面的ip列表都不一樣。

HSF中服務名約等於介面名
任何HSF程式設計,都依賴於介面
服務提供介面實現
客戶端基於介面程式設計,內部HSF代理去進行遠端通訊
客戶端基於介面程式設計,當客戶端執行到這個介面的時候,客戶端本地是沒有任何實現的,hsf會在內部作代理,
當執行到這個介面的某個方法的時候,hsf會遠端傳送一個tcp的請求,傳送給這個服務端,服務端在本地執行這個介面的實現,拿到響應之後傳送回給這個客戶端,hsf會把這個結果當作這個介面的返回值去給上層的應用;

隔離容器–Pandora(taobao-hsf.sar)

1.所有中介軟體(hsf/tddl/notify)都在Pandora內部
2.提供類隔離機制
為什麼把他們打在一起,因為Pandora提供了一種類隔離的機制,這樣的好處是當應用特別複雜的時候,會依賴特別多的jar包,而這些jar包可能又會依賴不同版本的中介軟體,排除起來會非常的痛苦,當classpath目錄下有兩個packagename和classname都相同的類的時候,jvm會隨機載入一個,這個時候如果不把多餘的依賴排除乾淨的話,是無法保證在執行時期所使用的hsf到底是哪個版本,如果使用sar包的話,惟一確定的一點就是hsf就是sar包裡的版本,它只會用sar包裡的版本,還有一點,比如說hsf會依賴google的guava庫,當你的應用也要用guava庫,它們之間是相互隔離的,這個隔離就是通過Pandora來做到的。

Web容器–湯姆貓,J老闆
1.HSF和這些玩意其實沒什麼關係
hsf-standalone可以只用一個main方法就可以啟動hsf

配置檔案配置好後,如果是在web容器中使用hsf的,先讓web容器跑起來,web容器會初始化spring,
spring會在beanFactory初始化所有bean的時候幫你把hsf釋出出去,服務就提供出去了,就什麼都不用管了。

介面名和版本號作為一個二元組區分一個hsf服務

深入性的原理:
隔離容器Pandora
HSF自身架構
軟負載策略
非同步呼叫
執行緒池

sar是什麼(Pandora就是taobao-hsf.sar,所有的中介軟體都在裡面,不僅是hsf, 只是為了相容才沒改名字)
幹什麼:載入的隔離、模組化
載入隔離的作用:

  1. 針對三方包來說,比如之前提到的hsf會依賴google的guava庫,如果應用也要依賴guava庫,它們之前是隔離的,
    這是通過hsf進入Pandora之後,Pandora會為它劃分出一塊單獨的classpath,其實是通過一個單獨的ClassLoader來實現的,這樣的話hsf使用的三方包就不會和應用使用的三方包衝突
  2. 針對二方包來說,比如說應用要依賴hsf程式設計,而每個二方包的jar包會依賴的hsf的jar包,而依賴的版本不一樣,
    由於jvm的隨機載入性,可能會出問題,但是如果把tao-hsf.sar放在WEB目錄的deploy目錄下,
    它會提供一種載入隔離的機制,就不用擔心依賴的hsf版本的不同而造成執行時載入的問題了。

模組化:一般不用關心,pandora-framework來實現應用拆分成不同的元件,即元件化。

怎麼玩的:匯出、匯入
怎麼實現載入的邏輯的呢,怎麼保證應用一定會用pandora內部的hsf,而不是用當前classpath下依賴的hsf的jar包呢
這就是匯出的概念:pandora是生存在web容器中的,比如說tomcat,tomcat針對每一個war包,它有它自己的war-classloader這個東西,
這個東西會持有pandora的calssloader,當tomcat要載入類的時候,它會優先從pandora的classloader中載入類,
那pandora的classloader很奇怪也會持有tomcat的classloader,這樣做之後,載入的流程就變為:
如果pandora中的hsf這個元件把hsfSpringProviderBean這個類選擇匯出的話,那麼當tomcat要載入這個類,
也就是應用要載入hsfSpringProviderBean的時候,它會優先從pandora的classloader中去尋找,
那麼pandora發現這個類是被hsf匯出的類,它就會把它自己持有的hsf返回給tomcat,tomcat返回給上層的應用。
這樣就保證了在tomcat的deploy目錄下放置pandora的話,使用的hsf版本一定是pandora中的hsf版本.

匯入:比如說hsf要使用fastjson,大部分情況下hsf是不會使用通用的三方庫的(fastjson/commons-lang),
但如果應用通hsf傳輸的DO中包含這些東西,比如應用返回的DO是一個JSONObject的話,那麼hsf就要選擇使用匯入這個功能。回到剛剛那個邏輯:tomcat持有pandora的classloader,然後pandora持有tomcat的classloader, hsf是被pandora去載入的,那麼hsf在嘗試載入jsonobject這個類進行反序列化的時候,它會在pandora的classloader中優先尋找,但是pandora的classloader中是不包含這個玩意的,因為hsf自身不依賴它,那麼pandora就會拿它持有的那個tomcat的classloader去load這個class,就是jsonobject,這個就相當於tomcat對於這種匯入的類會使用應用自身cp那些類,這就是匯入的邏輯, 其實匯入真正的使用場景是對於spring這個東西,因為hsf要配置spring才能使用,
但是hsf自身也是依賴spring程式設計的,而spring-context如果大家依賴的不一樣的話,會造成多份spring-context,那這樣的話,應用拿不到hsf的類,hsf拿不到應用的類,所有hsf必須和應用用同一份spring-context,所以hsf選擇匯入spring。

這就介紹了pandora是怎麼實現載入隔離的,就是兩個概念,一個匯入,一個匯出;

有什麼好處:對應用無侵入,平滑升級

HSF自身架構
hsf分成三層:
第一個是Proxy層,主要處理hsf和應用互動的一些邏輯,比如做介面的代理,執行業務的方法
第二個是Remoting層,主要處理網路層中的應用層資料,它處理的是rpc協議
第三個是Processer層,主要處理hsf自身的一些邏輯,比如說序列化反序列化,異常處理等
處理完所有的邏輯之後,hsf是基於netty程式設計的,netty負責處理io模組來進行通訊,這樣的話,
ConfigServer/Diamond與Proxy層進行互動之後,就組成了整個的consumer/provider的呼叫過程,

軟負載策略–hsf的選址邏輯
什麼是軟負載,說白了就是導流量, 怎麼導流量呢?
第一種方式就是歸組,
軟負載策略–歸組:歸組規則是可以通過Diamond配置動態推送的,可以動態的把機器配置成A組別還是B組別,也有利於動態擴容
軟負載策略–路由:路由規則–通過配置將客戶端流量導到某些機器上。如何配置:Diamond規則,內容是Groovy指令碼
與歸組規則區別:粒度到方法引數;有保護機制(有算空保護,即當算出來的ip地址是空的時候會隨機地全量呼叫)
歸組規則的粒度只能到某個服務,不能到某個方法。
簡單的來看,都是通過配置將流量導致某些機器上, 那什麼時候用歸組規則,什麼時候用路由規則。
軟負載策略–同機房優先:在閥值範圍內,客戶端會優先呼叫同機房的客戶端
如何篩選同機房ip:
在同一個虛擬機器房(HSF邏輯)
ip前兩段相同
閥值的作用:
同機房機器數/總機器數>閥值,會優先呼叫同機房的機器
同機房機器掛掉->(同機房機器比例<閥值)->隨機呼叫
權重規則–對於provider叢集中的某些機器,增加被呼叫到的概率
未配置權重:每臺機器被訪問的概率相同
總機器數為m,配置某ip的權重為n後,這個ip被訪問的概率為:(1+n)/(m+n),
做法就是在m個ip的列表中在塞進去n個相同的ip即可

非同步呼叫有兩種方式Future呼叫和Callback呼叫,作用都是差不多的,都是解放真正的呼叫執行緒。
個人認為,大部分的非同步場景是可以通過訊息來解決的。
訊息是rpc的一種

執行緒池–HSF的業務執行緒池
HSF為所有Provider開一個ThreadPool
支援不同的服務配置單獨的執行緒池
定製化的ThreadPoolExecutor
任務佇列為SynchronousQueue(不是LinkedBlockingQueue,可以快速遞交,快速失敗)
執行緒複用時間為5min
拒絕策略觸發時會列印Jstack

常見問題:
找不到地址
應用啟動後沒查到自己釋出的服務
超時
序列化錯誤
找不到方法
執行緒池滿

啟動出錯永遠只看第一個,後面一般都是web context掛掉造成的