Sentinel Cluster流程分析

 前面介紹了sentinel-core的流程,提到在進行流控判斷時,會判斷當前是本地限流,仍是集羣限流,如果集羣模式,則會走另外一個分支,這節便對集羣模式作分析。緩存

一.基本概念

 namespace:限流做用於,用於區分一個規則做用於什麼範圍服務器

 flowId:表明全局惟一的規則 ID,Sentinel 集羣限流服務端經過此 ID 來區分各個規則,所以務必保持全局惟一。通常 flowId 由統一的管控端進行分配,或寫入至 DB 時生成。數據結構

 thresholdType:表明集羣限流閾值模式。其中單機均攤模式下配置的閾值等同於單機可以承受的限額,token server 會根據客戶端對應的 namespace(默認爲 project.name 定義的應用名)下的鏈接數來計算總的閾值(好比獨立模式下有 3 個 client 鏈接到了 token server,而後配的單機均攤閾值爲 10,則計算出的集羣總量就爲 30);而全局模式下配置的閾值等同於整個集羣的總閾值。app

二.通訊框架

 sentinel-cluster基於netty提供了一套遠程通訊框架,分爲客戶端和服務,其使用了jdk自帶的SPI,提供了一些接口的默認實現。以下圖爲sentinel-cluster-client客戶端模塊的默認實現類。框架

file

 InitFunc的加載是經過InitExecutor加載的,InitExecutor在sentinel-core模塊中。InitExecutor會在全局訪問內加載全部InitFunc的實現類,並調用其init方法完成初始化。該模塊中配置的InitFunc實現類爲DefaultClusterClientInitFunc,該類會初始化通訊協議中各類類型的編碼和解碼處理類。編解碼器將調用註冊工廠RequestDataWriterRegistry和ResponseDataDecodeRegistry的方法進行註冊,供後續使用。系統提供了PING,FLOW(流控)和PARAM_FLOW(熱點參數流控)三種編解碼器。socket

file

file

 上圖爲sentinel-cluster的通訊協議格式,請求和響應中有個4個字節的消息id和1個字節的消息類型,剩下的就是消息體,對於響應格式,有1個字節的狀態信息。須要說明的是,在初始化Netty客戶端時,增長了兩個filter:ide

file

 也就是說在發送一個消息時,會自動加上長度爲2個字節的消息長度頭部,在讀取時也會自動省略2個字節的消息長度頭部。 爲了解析上面的消息格式,在提供了註冊方法之上,sentinel還提供了ClientEntityCodeProvider,統一了報文的處理。函數

file

 如上,該類在static靜態代碼塊中進行了初始化,使用SPI,獲取RequestEntityWriter和ResponseEntityDecoder的實現類,這兩種實現類也在該模塊中指定了默認實現:DefaultResponseEntityDecoder和DefaultRequestEntityWriter。即處理過程爲ui

ClientEntityCodecProvider->ResponseEntityDecoder->ResponseDataDecodeRegisty-> EntityDecoder

ClientEntityCodecProvider->RequestEntityWriter->RequestDataWriterRegisty-> EntityWriter

 系統還提供了TokenClientHandler類,用於響應數據流,進行相應的處理編碼

file

 如上只列出了比較重要的屬性和方法。該類繼承了ChannelInboundHandlerAdapter並實現了對應的方法,currentState屬性用於標記客戶端當前的狀態,disconnectCallback則用於負責在斷線時進行重連。TokenClientHandler實現channelActive方法,會在鏈接創建時會發送PING請求給服務端;實現channelUnregistered方法,會在鏈接斷開時調用disconnectCallback,在必定時間後進行重連,等待時間跟失敗次數有關;實現channelRead方法,會在有響應數據時,接收響應內容,並進行處理,處理流程以下:

file

 在通過Netty處理解析爲消息類型對象後,會判斷該響應的類型,若是是PING消息的響應,則直接輸出日誌,不然將從TokenClientPromiseHolder中根據消息id設置對應的響應內容,以便消息發送線程可以得到響應。  上面提到的TokenClientPromiseHolder用於緩存請求消息。以下圖,發送消息後,會獲取對應的ChannelPromise對象,並根據消息存於TokenClientPromiseHolder中。ChannelPromise會等待Netty請求響應回來,對應的流程如上面InBound流程。在請求正常響應後,會根據消息id再從TokenClientPromiseHolder中獲取對應的響應結果。

file

file

 Cluster模塊的核心接口爲TokenService ,ClusterTokenServer和ClusterTokenClient。其中ClusterTokenClient內部主要類爲NettyTransportClient,在上面已經進行了說明,下面說下其餘兩個接口。TokenService ,ClusterTokenServer在模塊中的關係以下圖:

file

 其中接口都由SPI給出了默認的實現,以下:

file

 下面對涉及到的接口和類進行說明。

 TokenService:token服務接口,提供了requestToken和requestParamToken方法,分別表示獲取流控令牌和獲取熱點參數令牌。提供的默認實現爲DefaultTokenService,會在TokenServiceProvider初始化時使用SPI進行加載。

 ClusterTokenServer:服務端上層接口,提供了start和stop方法用於服務端的啓動和中止。

 NettyTransportServer:ClusterTokenServer的netty實現,同客戶端對應,有以下的pipeline配置

file

 其中編解碼器的處理同客戶端相似,只是增長了服務端的處理器:TokenServerHandler。TokenServerHandler繼承自ChannelInboundHandlerAdapter用以在鏈接創建和有數據交互時進行相應的處理:

  1. 實現channelActive:在鏈接創建時將其緩存起來

  2. 實現channelInactive:在鏈接斷開時移除緩存

  3. 實現channelRead:在有數據到來時,進行處理。這裏會使用RequestProcessorProvider加載的RequestProcessor實現類,根據請求的類型(type字段)選擇相應的處理類進行處理。系統如今提供的處理類有FlowRequestProcessor和ParamFlowRequestProcessor,這二者最後都將經過TokenServiceProvider得到DefaultTokenService對象,調用其來完成請求。

 SentinelDefaultTokenServer:包裝了NettyTransportServer方法,增長了ServerTransportConfigObserver用於監聽服務端配置項的更改,從而更新自身。

 EmbeddedClusterTokenServer:繼承自TokenService和ClusterTokenServer,用於內嵌服務端模式,默認實現爲DefaultEmbeddedClusterTokenServer。

 DefaultEmbeddedClusterTokenServer:主要組合了DefaultTokenService和SentinelDefaultTokenServer對象用以實現接口方法。

 結合上面服務端的實現,能夠獲得客戶端請求一個token的流程以下:

file

  1. 客戶端調用DefaultClusterTokenClient的requestToken方法獲取token,其內部會委託NettyTransportClient編碼後發給服務端
  2. 服務端NettyTransportServer收到請求後,由TokenServerHandler的channelRead方法處理這裏會根據請求內容中的type,委託給對應的消息處理處理,如FlowRequestProcessor
  3. FlowRequestProcessor會調用TokenServiceProvider獲取對應的TokenService實現類,默認爲DefaultTokenService。而後委託爲該類進行處理。

三.統計邏輯

 由上可知,cluster模式下,token的獲取是由DefaultTokenService來負責的,分爲兩種:普通流控和熱點參數流控。兩者的實現基本一致,這裏只對普通流控作講解,即DefaultTokenService中的requestToken方法,以下爲處理流程。

file

 當請求requestToken方法時,請求參數包括:

 ruleId:規則id

 acquireCount:須要獲取的token數

 prioritized:是否支持優先

  1. DefaultTokenService會先根據ruleId,使用ClusterFlowRuleManager得到對應的FlowRule規則對象。ClusterFlowRuleManager會在更新規則或者加載規則時根據ruleId緩存在Map中,且分配惟一一個ClusterMetric。

  2. 得到對應的FlowRule對象後,會調用ClusterFlowRuleChecker,判斷是否可以獲取所須要的token

  3. ClusterFlowRuleChecker會先根據規則Id得到該規則所對應的namespace,而後判斷該namespace在全局狀態下是否超過流控,該步驟主要由GlobalRequestLimiter提供,該類存儲着各個namespace對應的RequestLimiter對象。RequestLimiter繼承自LeapArray,只提供了QPS一個維度的滑動窗口實現,默認實現爲一秒內10個格子,以下圖。全局流控主要使用RequestLimiter的tryPass方法,計算當前qps是否大於規則設定的全局qps。

  4. 全局流控經過後,會根據ClusterMetricStatistics獲取ruleId對應的ClusterMetric,以獲取ruleId對應的統計維度。首先會判斷當前時間是否有可用的token,這裏會根據規則設定的thresholdType,區分設定的閾值模式,若是是全局模式,直接根據設定的值進行限流,若是是單機均攤模式,會將該值乘上已有的額客戶端數達到設定的閾值。若是有則更新統計信息並返回成功,若是沒有且不支持優先,則直接返回獲取失敗。若是支持優先,則嘗試從下一個格子借用token(注:本地模式的借用會從後面的格子借用,只要不超過最大的等待時間),若是借用成功則更新統計信息並返回成功,不然返回失敗。ClusterMetric的結構以下,繼承自ClusterMetriceLeapArray,該滑動窗口提供了cluster模式下多種模式的統計數據,還支持請求優先。

file

四.服務端啓動模式

 Sentinel服務端啓動模式能夠分爲Alone獨立模式和Embedded嵌入模式。

 獨立模式(Alone),即做爲獨立的 token server 進程啓動,獨立部署,隔離性好,可是須要額外的部署操做。獨立模式適合做爲 Global Rate Limiter 給集羣提供流控服務。

file

  1. 在獨立模式下,咱們能夠直接建立對應的 ClusterTokenServer 實例並在 main 函數中經過 start 方法啓動 Token Server。

  2. 嵌入模式(Embedded),即做爲內置的 token server 與服務在同一進程中啓動。在此模式下,集羣中各個實例都是對等的,token server 和 client 能夠隨時進行轉變,所以無需單獨部署,靈活性比較好。可是隔離性不佳,須要限制 token server 的總 QPS,防止影響應用自己。嵌入模式適合某個應用集羣內部的流控。

file

 系統提供了 HTTP API 用於在 embedded 模式下轉換集羣流控身份:

http://<ip>:<port>/setClusterMode?mode=<xxx>

 其中 mode 爲 0 表明 client,1 表明 server,-1 表明關閉。

 該請求會由ModifyClusterModeCommandHandler處理並最終調用ClusterStateManager.applyState方法來設置當前節點的狀態。須要說明的是,嵌入模式能夠不用顯示啓動服務端,而是由上面的applyState模式來設置,該方法會在內部啓動服務。固然也能夠不顯示啓動客戶端,一樣經過上面的方法,能夠將當前節點設置爲客戶端模式。在將當前節點設置爲客戶端時,會先獲取當前嵌入模式下的服務端對象,若是不爲空,則中止該對象,並啓動服務端;反之在設置服務端時,會先獲取客戶端對象,若是不爲空,則先停掉,再啓動嵌入模式下服務端對象。應用啓動接入dashboard後,能夠經過管理臺來控制各節點的角色,或者經過從配置中心加載規則來更改規則。

五.Handler

 sentinel-transport-common中定義了一套handler接口,用於對外提供HTTP接口同系統交互,從而可以獲取系統數據或者對應用節點下發命令。

 common模塊提供了以下幾個基本接口:

  1. CommandCenter:命令中心,做爲服務啓動,定義了start和stop方法,主要提供handler的初始化和註冊服務。

  2. HeartbeatSender:心跳發送接口,用於給控制檯dashboard定時發送心跳

  3. CommandHandler:請求處理接口,請求對象爲CommandRequest,響應對象爲CommandResponse

  4. CommandMapping:註解,用於爲Handler添加元數據,包括處理器名(URL路徑名)和描述

file

 針對上面的接口,common模塊提供了相對應的Provider類,用於以SPI的方式加載默認/自定義的實現,如上圖,包括:

  1. CommandCenterProvider:根據SPI,加載設定的實現,若是有多個實現,則根據Order註解,選擇優先級最高的一個

  2. HeartbeatSenderProvider:根據SPI,加載設定的實現,若是有多個實現,則根據Order註解,選擇優先級最高的一個

  3. CommandHandlerProvider:會加載全部的Handler實現類,不一樣模塊提供的Handler實現只要以SPI的方式,在META-INF中提供對應的全限定名就會被該類掃描並使用。實現類須要增長CommandMapping註解以指定URL。

 以下爲common模塊提供的Handler實現

file

file

 上圖中common的SPI接口中還有一個InitFunc實現,包括CommandCenterInitFunc和HeartbeatSenderInitFunc兩個實現類,這兩個類實現了InitFunc接口,會在InitExecutor被調用時初始化全部的InitFunc實現。對應的做用爲:

 CommandCenterInitFunc:使用CommandCenterProvider獲取對應的CommandCenter實現,依次執行beforeStart和start方法,以啓動服務。即只要加載了sentinel-transport-common模塊並經過SPI提供CommandCenter的實現,便會在InitFunc被調用時啓動服務。

 HeartbeatSenderInitFun:HeartbeatSenderProvider獲取對應的HeartbeatSender實現,啓動定時器,每隔5秒執行一次sendHeartbeat方法。即只要加載了sentinel-transport-common模塊並經過SPI提供HeartbeatSender的實現,便會在InitFunc被調用時啓動心跳定時器。

 上面提到,只要提供了CommandCenter和HeartbeatSender的實現,並經過SPI註冊對應的實現,並會自動啓動對應的服務,而位於sentinel-transport-simple-http和sentinel-transport-netty的模塊爲這兩個接口提供了默認實現。

 sentinel-transport-simple-http提供的實現爲SimpleHttpCommandCenter和SimpleHttpHeartbeatSender。

 SimpleHttpCommandCenter:基於socket,以阻塞模式提供了簡單的http服務器,會在啓動前經過CommandHandlerProvider緩存全部的Handler對象,當請求進來時新開線程處理,並在線程中調用對應的Handler進行處理並返回

 SimpleHttpHeartbeatSender:使用內建的SimpleHttpRequest向dashboard發送Http心跳請求

 sentinel-transport-netty提供的實現爲NettyHttpCommandCenter和HttpHeartbeatSender。

 NettyHttpCommandCenter:基於netty,以服務端模式啓動,會在啓動前經過CommandHandlerProvider緩存全部的Handler對象,內建的HttpServerHandler對象會在請求進來時獲取解碼後的對象,並根據請求類型調用對應的Handler進行處理並返回

 HttpHeartbeatSender:使用httpclient客戶端想dashboard發送Http心跳請求

 綜上,sentinel-cluster-server-default模塊提供了以下的Handelr實現,用於給dashboard提供集羣信息並接受從dashboard發送過來的命令。

file

 其中Fetch開頭的爲讀取消息,Modify開頭的爲修改系統消息。

六.集羣管理接口

 Sentinel預留了諸多管理接口,用於動態加載規則或者配置,而後更新本地的狀態,這裏對涉及到cluster模式下的幾個管理接口進行說明。在這以前,先介紹下demo中以Nacos爲配置中心的接入方式。

 接入Nacos涉及到另外兩個模塊,sentinel-datasource-extension和sentinel-datasource-nacos。Extension模塊定義了ReadableDataSource接口,用於從數據源讀取數據,返回配置數據SentinelProperty。Extension模塊提供了一個抽象類實現AbstractDataSource,實現了loadConfig方法。該類引入了Converter接口和DynamicSentinelProperty類,Converter接口用於將數據源中讀取的數據結構轉換爲SentienlProperty存儲的數據格式;DynameicSentinelProperty類爲SentinelPorperty的默認實現,該類可以添加多個PropertyListener監聽器,在添加時觸發監聽器的configLoad方法進行監聽器的初次動做,並在數據發生變動時,逐個通知監聽器,調用監聽器的configLoad方法,提醒監聽器進行更新。AbstractDataSource實現了loadConfig方法,該方法會調用readSource方法,從數據源讀取原始數據,並調用Converter進行數據轉換。

file

 Nacos模塊提供了NacosDataSource實現,繼承自AbstractDataSource,以接入Nacos配置中心。NacosDataSource在初始化時會在Nacos上申請一個配置集,並添加監聽器,而後執行一遍loadConfig,從配置中心加載一遍配置並,更新property中的值並通知配置集上的監聽器。Nacos上的監聽器會在配置發生變化時,調用Convert記性處理,並更新配置集,同時通知配置集上的監聽器。

 由上可知,能夠經過使用DynamicSentinelProperty動態配置集上的監聽器,配合數據眼監聽配置變化,從而讓系統作出相應的動做。事實上,sentinel內置的大部分管理接口都是這樣處理的,以下爲集羣相關的主要管理接口,均以Manager結尾。這些管理接口的結構都同FlowRuleManager同樣,內部維護這一個或者多個配置源,並在配置源上設置了監聽器,當配置源有數據變化時,會調用配置源的updateValue方法,更新配置源數據而且通知監聽器。

  1. FlowRuleManager

     這個在講解sentinel-core模塊時有介紹過,主要是存儲本地限流規則集SentinelProperty<List<FlowRule>>。該規則集上有FlowPropertyListener監聽器,會在規則發生變動時從新構建,加載規則。

  2. ParamFlowRuleManager

     同FlowRuleManager,主要用於熱點參數限流規則管理。

  3. ClusterClientConfigManager

     集羣客戶端配置管理,主要管理:

    1. 集羣客戶端配置,用於設定客戶端超時時間,配置集爲SentinelProperty<ClusterClientConfig>和監聽器ClientConfigPropertyListener。會在規則發生變動時,更新客戶端的請求超時時間

    2. 集羣服務端信息配置,用於設定服務端的ip和端口信息,配置集爲SentinelProperty<ClusterClientAssignConfig>和監聽器ClientAssignPropertyListener。會在規則發送變動時,更新本地配置,並通知ServerChangeObserver觀察者服務端節點發送了變化,由以前的內容能夠看到,DefaultClusterTokenClient爲該接口的觀察者,會在服務端信息發送變動時先斷開同以前的連接,再同心的服務端節點創建新的連接。

  4. ClusterServerConfigManager

     集羣服務端配置管理,主要管理:

    1. 集羣服務端傳輸配置,用於設定服務端端口和idle時間,配置集爲SentinelProperty<ServerTransportConfig>和監聽器ServerGlobalTransportPropertyListener。會在規則發生變動時,更新本地配置,並通知ServerTransportConfigObserver觀察者配置發生了變化。由以前的內容能夠看到,SentinelDefaultTokenServer爲該接口的觀察者,會在服務端信息發送變動時,中止自身應用,再從新啓動。

    2. 集羣服務端全局流控配置,用於設定全局流控配置項,包括滑動窗口實現大小,窗口格子數,容許經過的最大qps等。配置集爲SentinelProperty<ServerFlowConfig>和監聽器ServerGlobalFlowPropertyListener,會在規則更新時從新設置這些配置內容。

    3. 集羣服務端namespace集合配置,用於設定集羣中的namespace集合,配置集爲SentinelProperty<Set<String>>和監聽器ServerNamespaceSetPropertyListener,會在配置發生變動時移除老namesapce的配置,並從新載入新namesapce的配置,包括對應的全侷限流器GlobalRequestLimiter,集羣限流規則,集羣熱點限流規則。

  5. ClusterFlowRuleManager

     集羣限流規則配置管理,主要管理:

    1. 集羣規則配置,用於設定集羣規則,配置集爲SentinelProperty<List<FlowRule>>和監聽器FlowRulePropertyListener,會在配置發生變動時,移除對應namespace下的緩存的配置,並從新構建對應的規則。對於一個新的flowId,會爲其分配一個對應的ClusterMetricStatistics統計節點。
  6. ClusterParamFlowRuleManager

     集羣熱點限流規則配置管理,同ClusterFlowRuleManager

  7. ClusterStateManager

     集羣全局狀態管理,主要管理:

    1. 本機角色配置,配置集爲SentinelProperty<Integer>和監聽器ClusterStatePropertyListener,會在規則發生變動時,調整本機的角色。角色包括:服務端,客戶端和非集羣模式。若規則爲非集羣模式,則會中止相關的客戶端或者服務端;若設置爲服務端模式,則會使用嵌入模式啓動服務,若以前爲客戶端則會關閉客戶端鏈接;若設置爲客戶端模式,則會啓動客戶端鏈接,若以前爲服務端則會中止服務。

 上述幾個管理接口均可以接入配置中心如Nacos,以經過配置中心和管理臺來改變各配置項。

file

我的公衆號:啊駝

相關文章
相關標籤/搜索