作者:京東物流 劉達
一、Sentinel是什么?
Sentinel是從阿里技術體系內誕生并由相關社區從微服務到云原生階段持續孵化的流量治理組件,在服務熔斷限流以及秒級/分鐘級監控方面提供了開箱即用的解決方案,此外作為支持云原生的重要探索,還提供了GO語言實現。Sentinel目前擁有著活躍的開源社區,從1.8.x版本開始,通過深度參與到SpringCloudAlibaba套件的支持,實現與Java主流開發框架SpringBoot及SpringCloud的兼容,進而成為了Java技術棧開源熔斷限流的事實標準。雖然其2.x版本持續難產(可能是適配OpenSergo規范的影響),不過好在1.8.x并沒停止迭代,今年以來已經在1.8.7(JDK8)的基礎之上做了1.8.8(JDK17)的升級,完成了主流JDK版本的支持,基本能夠滿足Javaer的開發需求。
考慮到一般部署環境以JDK8居多,而且1.8.8除了JDK的升級外,沒有做實質的功能改造,所以本篇文章主要基于1.8.7版本展開,即在JDK8的環境下對Sentinel的能力進行討論。
二、Sentinel能帶來什么?
就如前文所介紹的,Sentinel提供了完善的熔斷、限流以及秒級/分鐘級監控方案。最核心的一點,就是它把應用規則的目標統一抽象成了一個概念——資源。按照官方說明,資源可以是Java應用程序中的任何內容,比如服務、接口方法或者一段代碼。只要用Sentinel提供的資源創建及釋放API包裹起來,就可以稱之為一個資源。資源的抽象為Sentinel的使用提供了極大的靈活性,這讓開發者可以把代碼鏈路中的任何一個部分都做到流量治理規則中,當然為了提高代碼的可讀性以及規范性,我們還是推薦以服務入口(HttpRPC)、接口或者接口的實現方法作為最小資源單元。
同時,Sentinel提供了豐富的控制規則:
?流量控制(FlowRule)
區分來源、限流類型(QPS/線程數)、支持上下游鏈路、多種流控效果(拒絕、排隊、慢啟動)
熱點限流是對流量控制的補充,對Top-K的熱點參數進行限流處理
?系統負載限流(SystemRule)
可以理解為應用級的流量控制策略,包含負載、單機平均RT,單機QPS等。不過,因為粒度較粗且部分指標的獲取在容器環境下存在適配問題,所以社區中大部分人對這個的理解是還有待完善,成熟的案例比較少。
?熔斷降級(DegradeRule)
在熔斷降級方面,則是直接對標的Hystrix框架,一是在實現機制上做了優化,相對Hystrix基于線程池的隔離控制策略,通過并發控制機制減少了線程池的創建和占用。另外,在配置上也更加簡化,相信有過Hystrix使用經驗,都會對其繁雜的注解配置項感到困惑,而當引入Sentinel時則無需擔心這些。
如果僅僅是這些,Sentinel的定位也僅限于一個組件包,類似Hystrix一樣。那為何本文開篇說Sentinel是一套解決方案呢?因為它還做到了另外的三點,接下來繼續進行說明:
第一點:包容性
Sentinel通過對Adapter模塊組持續迭代,目前已經兼容了主流的一系列調用協議及組件,比如dubbo2,dubbo3,httpclient,okhttp,grpc,sofa,quarkus,webflux,webmvc等。此外還完成了對Java開源網關的支持,包括zuul1,zuul2,springcloud-gateway。并且作為java應用流量控制的標準框架,很多其他開源框架也在項目內部做了相關的兼容。
第二點:界面化
Sentinel提供了方便易用的Dashboard,支持常用的一系列操作。比如應用的注冊發現,規則參數的配置、查看,接口簇點鏈路的自發現和樹狀展示,秒級的監控視圖等等,雖然開源方案展示的數據以內存或者對應用節點信息的拉取為依托,但這已經提供了非常強的指導性。其對應的商業化版本AHAS已經在阿里云中得到廣泛應用。
第三點:擴展性
雖然core包提供的是基于應用內存的數據存取策略,但是sentinel同時對控制規則數據的拉取鏈路做了通用的抽象,并衍生出了一系列datasource包。包含apollo、consul、etcd、nacos、redis、zk等,他們具備的共同點就是客戶端能夠及時的獲取到配置數據變更事件,而Sentinel依賴的控制規則就是以配置數據的形式維護在這些中間件中,并實現對客戶端的分發。
前文介紹了這么多,那引入Sentinel能帶來什么改變呢?
我覺得,首先,限流策略更加豐富,能夠更好的應對日趨復雜的限流需求;其次,對于資源的流量管理實現了統一,從單一入口維護應用的所有流量控制規則,無需從網關、JSF等分別對應用的入口進行配置;限流的維度更加細化,從入口級下沉到了應用內資源;在熔斷的支持上,除了JSF客戶端調用外,應用內部或作為客戶端對外部的任何協議調用都可以增加熔斷的管理,服務的穩定性也會隨之提升。
三、Sentinel的重點實現
概念之后再來說說Sentinel中的關鍵實現。這一部分準備分三塊來說明。第一是它的核心規則控制鏈路,第二它的datasource實現結構,第三Dashboard與應用的交互邏輯。此處主要是專注在架構實現上,像業內熟知的一些控制算法比如令牌桶等,不再贅述。
1、核心規則控制鏈路
為了更好的理解這條鏈路的結構,此處引入一下官方圖
??
上圖即Sentinel的默認ProcessorSlotChain所配置的鏈路結構,默認實現依托DefaultProcessorSlotChain。
??
從代碼上看是一個標準的鏈表結構,鏈表中裝配的節點對象類型為AbstractLinkedProcessorSlot,也就是圖中這些Slot的父類,我們或可以稱之為Slot模版類。通過代碼的查看,會發現它聲明了兩個方法,entry、exit分別作為Slot的入口和出口,并且在方法入參中攜帶了當前調用鏈路的上下文信息。通過官方圖例能夠看出,請求在進入處理鏈條后,按先后順序會經過三類Slot,前置的NodeSelectorSlot、ClusterBuilderSlot用于調用鏈路的提取,中間StatisticSlot是通用的滑動窗口計數功能,比如RT、通過數、拒絕數、異常數等,后置的ParamFlowSlot、SystemSlot等則為真正的規則驗證鏈條。如下圖所示,
??
在上圖中,會發現在模版類中有額外的兩個方法,fireEntry和fireExit,這兩個方法在AbstractLinkedProcessSlot中均有默認的實現,主要是用于在當前節點entry或exit方法執行過程中觸發對next節點的調度,這就使得Slot鏈表觸發順序和完成順序并不一定相同。這樣的好處是在基礎鏈路上提供了充足的靈活性,上游Slot可以按需觸發下游Slot并獲取執行結果。例如此處用于統計計數的StatisticSlot和用于規則驗證的一系列Slot列表之前的關系,正常通過和規則攔截的調用都能被StatisticSlot抓取到進行統計。
2、Datasource實現
作為配置文件的讀取來源,Sentinel提供了標準實現框架,并以此為基礎,對主流的開源分布式配置中心也做了充分的適配,還是以類結構圖作為切入點
??
可以看到,Sentinel提供的Datasource規范中,通過loadConfig,readSource,getProperty,close四個方法,覆蓋了數據源的讀取、加載、查詢和釋放整個流程。落地到具體實現方案,它提供了兩個大的方向,前者是支持熱更新,后者是不可變的配置來源,如jar包中的文件。
熱更新模式下,基于數據源的特性,又細分了兩種,我把它總結為輪詢模式和訂閱模式。像Eureka、本地文件這種,不支持數據變更事件的訂閱,Sentinel提供的方案是按照一定的時間間隔,定期的去拉取最新數據比對是否需要更新,這種就是前面講的輪詢模式。還有一種,依托于越來越成熟的各種開源分布式配置中間件,應用通過SDK集成,能夠方便的訂閱指定的配置項,并實時接收數據變更事件進行處理邏輯,相對于輪詢模式配置生效的更加及時,是目前線上案例中最常用的模式。
對于不可變模式,Sentinel也僅提供了一種實現,僅限于無配置變更需求的場景,支持一次性加載,好處是輕量依賴項少,但更新需要重啟。
3、Dashboard與應用的交互
核心鏈路和數據來源之后,再來說說Sentinel關于控制面的實現。
首先,要說明的是,即便是到最新的版本,Sentinel Dashboard也不是必須的。尤其是在1.6.x以前,datasource和dashboard這些模塊都還未完善,項目引用Sentinel的方式主要是以硬編碼為主,控制規則是借助RuleManager的靜態方法進行初始化,調用鏈路中通過SphU或SphO實現資源的創建,使用Tracer去顯式的記錄異常(@SentinelResource是在1.8.x才趨于完善)?,F在,官方文檔在大部分基礎用例中也是以這些形式為主。那Dashboard的作用是什么?
Dashboard為我們提供了相對完整的控制面,基本涵蓋了Sentinel的所有功能,包含各種規則的查看和配置、節點的自動注冊發現以及健康狀態監控、簇點鏈路的采集等,在可觀測方面提供了秒級的監控視圖,而在后臺為這些功能的數據提供了基于內存的實現,為我們實現硬編碼到控制臺的轉變提供了明確的方向。接下來從三個角度說明Dashboard的重點實現,分別是節點信息的發現、規則數據的維護、秒級監控的采集。邏輯鏈路如下所示:
這張鏈路圖基本把Sentinel從客戶端到Dashboard服務端的交互過程做了明確的展示。
A. 首先,Sentinel控制臺對客戶端的發現機制并不依賴于其他中間件,是獨立的相對輕量的實現。邏輯上很簡單,即在控制臺暴露了一個MachineRegistryController,客戶端配置Dashboard地址后,定時的上報心跳數據。
B. Sentinel對應用的Metrics數據采集是以定時拉取的方式,應用SDK會暴露http入口,用于提供給Dashboard服務側進行調用。Dashboard中相關的規則配置也是以類似的方式進行,額外增加了數據的寫入動作用于更新規則。
C. sentinel-core本身對Dashboard側的交互沒有依賴,而是通過transport包中以集成Spiloader的方式追加相關的初始化動作,實現像注冊心跳、遠端控制等附加能力,即sentinel-core仍然是獨立可用的。
SlotChain、Dashboard和Datasource是一套組合拳,共同構成了Sentinel的流控解決方案。SlotChain完成了核心的規則控制邏輯,而Datasource則是解決了配置數據怎么更新怎么同步的問題,最后Dashboard則是將配置數據的界面化維護以及運行時數據的展示入口提供給了開發者,讓線上的落地成為了可能。
?
四、Sentinel源碼包結構
從工程結構上看,Sentinel以sentinel-core為基礎,衍生了一系列的模塊:
1、sentinel-dashboard:sentinel控制臺,提供簡易的登錄方式,功能上支持節點注冊展示,限流、熔斷等流控規則的查看與下發,節點Metric信息的采集和聚合展示等。
2、sentinel-transport:內部通信模塊,實現客戶端節點的心跳注冊以及消息接收功能。
3、sentinel-logging:sentinel為了和應用日志做隔離,單獨指定的日志輸出問題,這個模塊提供了基于slf4j的默認實現,同時可以作為參考實現,以SPI的方式定制其他日志輸出通道。
4、sentinel-adapter:協議適配模塊,基本兼容了所有開源的協議,一是在資源生成時指定prefix,另外就是依賴調用鏈路中的FiIter做協議解析,以支持抽取流控規則依賴的參數,比如origin等。
5、sentinel-extension:這個模塊組則是作為一版擴展實現,不過大部分主要是依賴各種分布式配置型中間件實現的datasource模塊,實現規則數據的存取。
6、sentinel-cluster:主要是提供集群限流的部分簡易實現,目前看這種基于token分發中心的集群限流方案在性能上損耗相對單機型還是要大一些。
?
五、Sentinel的落地問題及改造方案
在上文中,對Sentinel的邏輯架構以及源碼已經做了詳細介紹。如果在我們生產環境中落地,有哪些問題?
我們知道,Sentinel提供的默認實現,數據的讀取以內存為主要媒介。Dashboard基于節點上報的心跳信息做匯總,收集到AppManagement中,用于左側菜單欄的應用展示,并開始根據心跳上傳時間判斷各應用內節點的在線離線狀態,當單個應用內所有節點都離線就對該應用進行隱藏。規則數據的讀取以及Metric統計信息是通過遠程調用節點的通信入口進行。由此可以衍生出下面四個場景的落地方案:
1、規則數據的讀寫
在第三部分中也介紹到,Sentinel對于規則數據的同步機制,提供了一組datasource模塊,主要以各分布式配置中間件為中心,實現數據的寫入和變更監聽,達到動態變更規則數據的目的。如果開發者并不想引入這些額外的中間件,那么就需要根據自己的需求來擴展datasource模塊。
接入新的配置源,首先從Dashboard開始,我們可以參考FlowControllerV2中提供的Nacos配置樣例,對應于各個規則,實現基于appName的DynamicRuleProvider和DynamicRulePublisher的讀寫入口類,將規則數據以Json的形式寫入新的配置源中,當然為了更大的擴展性和業務隔離性,我們可以在appName的參數基礎上,再追加類似Datacenter這種參數,以區分不同的業務群,避免單個Dashboard中應用過量造成處理壓力。
另外,就要考慮sentinel-datasource-{CustomizedDatasource}的開發,基于sentinel-datasource-extension這個數據源監聽模版,參考sentinel-datasource-nacos中NacosDataSource實現對應的AbstractDataSource,到此為止只是實現了Datasource類的定義和實現。而真正打通Dashboard到節點側的數據鏈路,還需要對Datasource的調用和初始化流程。Sentinel包中的demo工程,有針對于各個datasource的初始化樣例。
而我們如果想真正落地到項目中,僅僅這些還是不夠的,而是需要把Datasource對各個規則的加載和監聽封裝成獨立的Starter,這樣才能最大限度降低springboot集成的復雜度。對于這個想法,我們則需要轉向另一個開源項目:spring-cloud-alibaba-sentinel-datasource和spring-cloud-starter-alibaba-sentinel。
前者將不同Datasource和對應的配置節點做了關聯,如下
①DatasourceFactoryBean的聲明
public class NacosDataSourceFactoryBean implements FactoryBean
②Properties初始化參數中攜帶對應的FactoryBean
public NacosDataSourceProperties() { super(NacosDataSourceFactoryBean.class.getName());}
后者,則是在初始化SentinelDataSourceHandler的過程中,根據SentinelProperties中各Datasource子節點配置與否決定是否初始化到Bean實例,如下
??
通過這幾部分的構建,我們就可以實現自己的Sentinel-Starter,實現基于自定義數據源做規則數據的運維。
?
2、Metric統計數據及日志的托管
目前Metric信息是采用從Dashboard定時拉取再從應用維度聚合的方式進行。節點中的Metrics信息是相對完整的,我們可以從兩方面來進行收集:
第一種方式,節點主動上報,收集至統一的存儲,再以Grafana的形式做數據展示。具體方式的話,可以選用直接上傳到定制的OAP服務,也可以擴展sentinel-log模塊的實現做基于日志的上傳(因為sentinel的設計機制中,自身的狀態日志和業務日志是分離的)。不過,這會造成一定的性能損耗,落地到項目中需要進行詳細的壓測和評估。
第二種方式,由中控進行定時采樣,時間間隔可以自定義,也就是以拉的方式進行,采樣率可以在中控進行統一的動態控制,合理配置能夠更容易達到性能損耗和觀測性的平衡。
此處不再細說,方案比較多,見仁見智。
?
3、Dashboard的適配
Dashboard面向線上業務場景,需要解決的有以下三點:
①數據存儲問題:上文中規則數據的Datasource擴展不再討論,除此以外,Sentinel中節點、應用、簇點鏈路都是基于內存存儲的,這就有一個問題——重啟丟失。因為這些信息都是基于節點心跳的,數據丟失后意味著重新收集,那么之前在這些數據上做的規則配置就會丟失查看的入口。另外,應用的展示是以應用內節點的在線狀態為準的,當應用內所有節點都離線,也會造成應用列表中缺失。所以解決方案就是需要將應用、節點保存至持久化存儲中,例如Mysql,而簇點鏈路信息由于樹狀結構的特殊性,可以直接放到Redis中存儲以及更新。
②權限問題:Dashboard通過輕量的自定義Filter實現的最簡單的登錄控制。落地的話就需要首先接入ERP統一登錄,另外還需要做應用權限的管理,避免權限穿透和誤操作問題。
③業務劃分:需要按照業務進行應用層面的劃分。實現多數據中心的支持,避免中心節點集成應用過多。
?
4、協議的適配
相比前幾點,此處的改動是最輕量的。當開發使用的調用協議不在Sentinel官方兼容,那么就需要對協議做適配性開發。按照sentinel-adapter中對其他協議的適配方式,我們需要改造的大體可以總結為兩點,第一,常規調用上下文參數的解析,比如調用來源等;第二,定義協議入口的資源前綴,與其他資源做區分。
六、小結
本文圍繞Sentinel展開,從其基本概念到核心實現,再到探討開源方案的挑戰和如何在實際項目中應用和改進Sentinel。Sentinel以其高度的靈活性和可擴展性贏得了廣大開發者的青睞,不論是新系統的開發還是現有系統的維護,Sentinel都能為我們提供極具價值的支持。
?
參考內容:
【1】https://sentinelguard.io/zh-cn/?
審核編輯 黃宇
-
內存
+關注
關注
8文章
3040瀏覽量
74167 -
開源
+關注
關注
3文章
3374瀏覽量
42598 -
JDK
+關注
關注
0文章
81瀏覽量
16604 -
Sentinel
+關注
關注
0文章
10瀏覽量
7160
發布評論請先 登錄
相關推薦
評論