報告老闆,微服務高可用神器已祭出,您花巨資營銷的高流量來了沒?

碼大叔發表於2020-04-28

在這裡插入圖片描述

我得意地來到老闆的辦公室:“報告老闆,高可用神器已祭出,您花巨資營銷的高流量什麼時候到?”
老闆呵呵一聲:“高流量下週一來報導,你可以準備下和他做工作交接了”
窗戶外一陣冷風吹過,接下來是不是會發生點什麼。

@

一、從阿里雙11的高併發高流量開始

正如眾所周知,每年的雙11除了是購物狂歡節,同樣也成了科技的大考和狂歡,讓我們得以看到那麼多高階的充滿想象力的黑技術。在整個雙11高併發高流量的過程中,Sentinel 承接了核心場景,完美地保障了阿里巴巴歷年雙十一的穩定性。

隨著微服務的流行,服務和服務之間的穩定性變得越來越重要。Sentinel以流量為切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。

在這裡插入圖片描述
通過Sentinel可以應對或支撐以下場景:

  1. 持續峰值:限流、慢呼叫降級
  2. 秒殺(也可稱之為脈衝流量):限流、慢呼叫降級
  3. 削峰填谷:消費端可能會出現大批量的訊息同時到達,若瞬時請求所有訊息會導致系統負載過高。Sentinel利用勻速器模式將訊息均攤到一段時間內,讓系統負載保持在處理能力水位的同時儘可能地處理更多訊息,從而起到“削峰填谷”的效果。
  4. 冷啟動:當流量突然增大的時候,我們希望系統從空閒狀態到繁忙狀態的切換的時間長一些,即如果系統在此之前長期處於空閒的狀態,我們希望處理請求的速率緩慢增加,經過預期的時間以後,到達系統處理請求速率的設定值;
  5. 熱點商品自動探測、防護:自動統計訪問頻次最高的熱點引數並進行流量控制;
  6. 叢集流量不均勻:通過叢集限流來解決叢集各個機器的流量不均導致整體限流效果不精確的問題;
  7. 完備的實時監控:Sentinel 同時提供實時的監控功能。可以在控制檯中看到接入應用的單臺機器秒級資料,甚至 500 臺以下規模的叢集的彙總執行情況。

作為一個功能完備的高可用流量控制框架,核心的 sentinel-core 打包後只有 286KB(最新的1.7.2版本),非常輕量級,可以放心地引入 sentinel-core 而不需擔心依賴問題(打臉,沒有坑是不可能的)。根據官方提供的資料,引入 Sentinel 帶來的效能損耗非常小,單機 QPS 不太大的時候損耗幾乎可以忽略不計(單機 4.3W QPS 的損耗約為 2.36%)

二、核心功能及原理說明

2.1 模組說明

在這裡插入圖片描述

  • sentinel-core 核心模組,限流、降級、系統保護等都在這裡實現
  • sentinel-dashboard 控制檯模組,可以對連線上的sentinel客戶端實現視覺化的管理
  • sentinel-transport 傳輸模組,提供了基本的監控服務端和客戶端的API介面,以及一些基於不同庫的實現
  • sentinel-extension 擴充套件模組,主要對DataSource進行了部分擴充套件實現
  • sentinel-adapter 介面卡模組,主要實現了對一些常見框架的適配
  • sentinel-demo 樣例模組,可參考怎麼使用sentinel進行限流、降級等
  • sentinel-benchmark 基準測試模組,對核心程式碼的精確性提供基準測試
  • sentinel-logging 日誌模組,可將sentinel的日誌通過slf4j整合到專案中
  • sentinel-cluster 叢集,1.4版本中提供了叢集流控的功能

從使用上來看,主要分為兩大部分:
核心庫(Java 客戶端):不依賴任何框架/庫,能夠執行於 Java 7 及以上的版本的執行時環境,同時對 Dubbo / Spring Cloud 等框架也有較好的支援(見 主流框架適配)。
控制檯(Dashboard):Dashboard 主要負責管理推送規則、監控、管理機器資訊等。

2.2 總體框架

在這裡插入圖片描述

sentinel主要是基於7種不同的Slot形成了一個連結串列,每個Slot都各司其職,自己做完分內的事之後,會把請求傳遞給下一個Slot,直到在某一個Slot中命中規則後丟擲BlockException而終止。前三個Slot負責做統計,後面的Slot負責根據統計的結果結合配置的規則進行具體的控制,是Block該請求還是放行。

  • NodeSelectorSlot 負責收集資源的路徑,並將這些資源的呼叫路徑,以樹狀結構儲存起來,用於根據呼叫路徑來限流降級;
  • ClusterBuilderSlot 則用於儲存資源的統計資訊以及呼叫者資訊,例如該資源的 RT, QPS, thread count 等等,這些資訊將用作為多維度限流,降級的依據;
  • StatisticSlot 則用於記錄、統計不同緯度的 runtime 指標監控資訊;
  • FlowSlot 則用於根據預設的限流規則以及前面 slot 統計的狀態,來進行流量控制;
  • AuthoritySlot 則根據配置的黑白名單和呼叫來源資訊,來做黑白名單控制;
  • DegradeSlot 則通過統計資訊以及預設的規則,來做熔斷降級;
  • SystemSlot 則通過系統的狀態,例如 load1 等,來控制總的入口流量;
    在這裡插入圖片描述
    Sentinel 還提供簡單易用、完善的 SPI 擴充套件介面。讓開發人員可以通過實現擴充套件介面來快速地定製邏輯。例如定製規則管理、適配動態資料來源等。

2.3 核心概念:Resource/Entry/Context/Node

在這裡插入圖片描述

  • Resource
    在Sentinel中把所有要保護的東西都稱之為資源,它可以是服務,服務裡的方法,甚至是一段程式碼。只要通過 Sentinel API 定義的程式碼,就是資源,能夠被 Sentinel 保護起來。大部分情況下,可以使用方法簽名,URL,甚至服務名稱作為資源名來標示資源。

  • Context
    Context是一個用來儲存呼叫鏈當前狀態的後設資料的類,每次進入一個資源時,就會建立一個Context,相同的資源名可能會建立多個Context。一個Context中包含了三個核心的物件:
    1)當前呼叫鏈的根節點:EntranceNode
    2)當前的入口:Entry
    3)當前入口所關聯的節點:Node
    Context中只會儲存一個當前正在處理的入口Entry,另外還會儲存呼叫鏈的根節點。需要注意的是,每次進入一個新的資源時,都會建立一個新的Context。

  • Entry
    每次呼叫 SphU#entry() 都會生成一個Entry入口,該入口中會儲存了以下資料:入口的建立時間,當前入口所關聯的節點,當前入口所關聯的呼叫源對應的節點。Entry是一個抽象類,他只有一個實現類,在CtSph中的一個靜態類:CtEntry

  • Node
    節點是用來儲存某個資源的各種實時統計資訊的,他是一個介面,通過訪問節點,就可以獲取到對應資源的實時狀態,以此為依據進行限流和降級操作。

2.4 簡單上手及dashboard一覽

官方有比較完善的演示程式碼:

/**
 * 配置規則
 */
private static void initFlowRules(){
    List<FlowRule> rules = new ArrayList<>();
    FlowRule rule = new FlowRule();
    rule.setResource("HelloWorld");
    rule.setGrade(RuleConstant.FLOW_GRADE_QPS);
    // Set limit QPS to 20.
    rule.setCount(20);
    rules.add(rule);
    FlowRuleManager.loadRules(rules);
}

public static void main(String[] args) {
    // 配置規則.
    initFlowRules();

    while (true) {
        // 1.5.0 版本開始可以直接利用 try-with-resources 特性,自動 exit entry
        try (Entry entry = SphU.entry("HelloWorld")) {
            // 被保護的邏輯
            System.out.println("hello world");
	} catch (BlockException ex) {
            // 處理被流控的邏輯
	    System.out.println("blocked!");
	}
    }
}

如果是SpringCloud專案,可參考https://github.com/sentinel-group/sentinel-guides/tree/master/sentinel-guide-spring-cloud

在這裡插入圖片描述
整個dashboard的功能體驗下來,就是不停的 “我,這功能也有。我,還能這麼玩啊”,網上案例比較多,這裡就不多描述了。

三、生產環境下使用的一些思考

3.1 流量控制規則管理及推送

在前面已經提到了,Sentinel支援實時動態地配置規則,而不是預先在程式碼裡定義好就無法改變了。實際情況下流量是動態的,比如某一個促銷活動可能導致流量大增,甚至可能有一些不在預期內的流量。為了達到最好的防護效果,動態的規則可以將即將掛掉的應用從掛掉的邊緣拉回來。但預設情況下,在dashboard設定規則後是通過API 將規則推送至客戶端並直接更新到記憶體中,擴充套件寫資料來源(WritableDataSource),這樣的方法簡單,無任何依賴 ,但缺點也同樣很明顯:不保證一致性;規則儲存在記憶體中,重啟即消失。所以產線建議採用Push模式
在這裡插入圖片描述
如上圖所示,整個流程:配置中心控制檯/Sentinel 控制檯 → 配置中心 → Sentinel 資料來源 → Sentinel。Sentinel目前已經支援了ZooKeeper、攜程的 Apollo, 阿里自身的Nacos 等的動態資料來源。接下來以Nacos為例:

1、在Spring Cloud應用中引入Sentinel模組和Nacos儲存擴充套件:

implementation ('com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel:2.1.2.RELEASE'){
    exclude group:'com.alibaba',module: 'fastjson'}
implementation 'com.alibaba.csp:sentinel-datasource-nacos:1.7.2.RELEASE'

2、在應用中新增配置資訊

spring:
  cloud:
    sentinel:
      transport:
        # sentinel dashboard的地址
        dashboard: localhost:8080
      datasource:
        ds:
          nacos:
            # nacos的地址
            server-addr: localhost:8848
            # nacos中儲存規則的groupId
            dataId: madashu-test-sentinel
            # nacos中儲存規則的dataId
            groupId: DEFAULT_GROUP
            # 儲存的規則型別
            rule-type: flow

3、在Nacos中設定相應的規則

JSON:
[
    {
        "resource": "/madashu/hello",
        "limitApp": "default",
        "grade": 1,
        "count": 5,
        "strategy": 0,
        "controlBehavior": 0,
        "clusterMode": false
    }
]
  • resource:資源名
  • limitApp:流控針對的呼叫來源,default不區分來源
  • grade:限流閾值型別(0-根據併發數量來限流 1-根據QPS來進行流量控制)
  • count:限流閾值
  • strategy:呼叫關係限流策略
  • controlBehavior:流量控制效果(直接拒絕、WarmUP、勻速排隊)
  • clusterMode:是否叢集模式

這裡官方社群提供了限流規則儲存和訂閱的Demo,如果需要設定熔斷降級、系統保護、閘道器限流等,參考配置即可。基本方式:Dashboard將xxRuleEntityVO模型序列化到儲存到nacos,應用從nacos訂閱後發序列成xxRule領域模型。

3.2 單機限流的閾值如何配置

這個千萬不能照搬網上的配置或者拍腦袋,否則將會導致產線大規模的誤殺或者癱瘓,一定要根據容量規劃和水位設定來配置。具體可以參考單機容量規劃的思路,在軟負載中調整某個節點的流量權重和比例直到逼近極限為止。記錄下極限狀態的QPS,按照單機房70%的水位設定標準,這樣就可以推算出該資源的單機限流閾值了。

四、從 Hystrix 遷移到 Sentinel

之前的微服務專案中基本上都是用Hystrix,從Hystrix遷移到Sentinel並不是一行配置一行程式碼的事,不要受網上一些教程的誤導,需要具體評估自己的專案中使用到了Hystrix的場景。

4.1 為什麼Sentinel不支援執行緒池隔離

比如相對於Hystrix,Sentinel 沒有提供執行緒池隔離這樣比較重的隔離方式,而是提供了訊號量隔離這種比較輕量級的隔離方式。執行緒池隔離的好處是隔離度比較高,可以針對某個資源的執行緒池去進行處理而不影響其它資源,但是代價就是執行緒數目比較多,執行緒上下文切換的 overhead 比較大,特別是對低延時的呼叫有比較大的影響。另外,託管的執行緒切換可能會導致基於 ThreadLocal 的上下文傳遞丟失的問題(如 Spring 事務管理)。

4.2. 遷移方案

Hystrix 功能 遷移方案
執行緒池隔離/訊號量隔離 Sentinel 不支援執行緒池隔離;訊號量隔離對應 Sentinel 中的執行緒數限流
熔斷器 Sentinel 支援按平均響應時間、異常比率、異常數來進行熔斷降級。
Command 建立 直接使用 Sentinel SphU API 定義資源即可,資源定義與規則配置分離,詳見此處
規則配置 在 Sentinel 中可通過 API 硬編碼配置規則,也支援多種動態規則源
註解支援 Sentinel 也提供註解支援,可以很方便地遷移
開源框架支援 Sentinel 提供 Dubbo、Spring Cloud、gRPC 的適配模組,開箱即用;若之前使用 Spring Cloud Netflix,可遷移至 Spring Cloud Alibaba

阿里開源的專案文件都還是比較完善的,遷移過程中如果遇到了問題,可參考:https://github.com/alibaba/Sentinel/wiki/Guideline:-從-Hystrix-遷移到-Sentinel

五、實戰踩坑:Jar包相容的那些坑

5.1 fastjson序列化異常

之前有一個Spring Cloud專案,在某個AOP中使用fastjson列印出入參,功能執行的很完美。後來引入了sentinel,新增了jar包spring-cloud-starter-alibaba-sentinel:2.1.2.RELEASE,然後很悲劇地發現fastjson序列化竟然異常了!

java.lang.NoSuchMethodError: 
com.alibaba.fastjson.serializer.JavaBeanSerializer.processValue
(Lcom/alibaba/fastjson/serializer/JSONSerializer;
Lcom/alibaba/fastjson/serializer/BeanContext;Ljava/lang/Object;
Ljava/lang/String;Ljava/lang/Object;)
Ljava/lang/Object;Ljava/lang/Integer;
at com.alibaba.fastjson.serializer.ASMSerializer_12_XXX.writeNormal(Unknown Source) ~[?:?]

經過排查發現,sentinel這個jar包依賴了fastjson1.2.62,而這個版本中是有bug的,具體參見,https://github.com/alibaba/fastjson/issues/2790。

解決方案
排除掉依賴的jar包即可,fastjson1.2.60中是不存在該問題的。最新的1.2.68中是否修復了該問題沒有驗證,有興趣的小夥伴可以驗一下。

implementation ('com.alibaba.cloud:spring-cloud-starter-alibaba-sentinel:2.1.2.RELEASE'){
    exclude group:'com.alibaba',module: 'fastjson'}

5.2. Sleuth和Sentinel,我們決鬥吧

在微服務中,一個請求往往經過多個業務模組,比如使用者發起支付,可能經過了會員服務、交易服務、支付服務、賬戶服務等,此時一旦某個服務發生了問題,定位時將會非常的困難。在Spring Cloud專案上,一般會引入鏈路呼叫跟蹤框架Spring Cloud Sleuth+zipkin,此時務必要注意:如果在專案中使用了Feign,熔斷可能已經失效了。因為Sleuth為了傳遞鏈路呼叫跟蹤的資訊對Feign進行了擴充套件,而Sentinel為了實現熔斷限流也對Feign進行了擴充套件,也就導致了兩個本互不相干的框架竟然產生了衝突。

解決方案
方案1:二選一,做個決斷吧,哈哈。Sleuth是支援Hystrix的,而Hystrix已停止了維護,Sentinel正火,說不定很快Sleuth也會支援Sentinel。
方案2:換成其他的鏈路呼叫跟蹤框架,這個網上有很多開源的產品,筆者之前的公司時也曾研發並開源了這樣一套系統。
方案3:最近剛發現網上有一位大佬提供了修改原始碼的方案,實現還是很簡單的,將Sentinel融入到Sleuth中
在這裡插入圖片描述
詳細可參見:https://www.cnblogs.com/yinjihuan/p/12730654.html

以上只是對Sentinel一些粗淺的體驗,在Sentinel中有很多優秀的設計思想、先進的演算法、值得拜讀的程式碼等,後續再慢慢品味。

參考,致謝
https://blog.csdn.net/g6U8W7p06dCO99fQ3/article/details/104454238/
https://www.cnblogs.com/yinjihuan/p/12730654.html
https://github.com/alibaba/Sentinel/wiki/Guideline:-從-Hystrix-遷移到-Sentinel
https://www.jianshu.com/p/5a468b6a07fe

感謝關注公眾號【碼大叔】,公眾號回覆【進群】兩個字,帶你加入一個高品質圈子,由網際網路公司HR、架構師、行業資深大佬組成的微信群,空位不多噢

碼大叔,28歲+大齡資深程式設計師、架構師技術社群
關注微服務、大資料、架構設計、技術管理
在這裡插入圖片描述

相關文章