1、什麼是Sentineljava
Sentinel,中文翻譯爲哨兵,是爲微服務提供流量控制、熔斷降級的功能,它和Hystrix提供的功能同樣,能夠有效的解決微服務調用產生的「雪崩效應」,爲微服務系統提供了穩定性的解決方案。隨着Hystrix進入了維護期,再也不提供新功能,Sentinel是一個不錯的替代方案。一般狀況下,Hystrix採用線程池對服務的調用進行隔離,Sentinel採用了用戶線程對接口進行隔離,兩者相比,Hystrix是服務級別的隔離,Sentinel提供了接口級別的隔離,Sentinel隔離級別更加精細,另外Sentinel直接使用用戶線程提供限制,相比Hystrix的線程池隔離,減小了線程切換的開銷。另外Sentinel的DashBoard提供了在線更改限流規則的配置,也更加的優化。node
2、開源生態spring
Sentinel和Hystrix數組
功能 | Sentinel | Hystrix |
---|---|---|
隔離策略 | 信號量隔離(併發線程數限流) | 線程池隔離/信號量隔離 |
熔斷降級策略 | 基於響應時間、異常比率、異常數 | 基於異常比率 |
實時統計實現 | 滑動窗口(LeapArray) | 滑動窗口(基於RxJava) |
動態規則配置 | 支持多種數據源 | 支持多種數據源 |
擴展性 | 多個擴展點 | 插件形式 |
基於註解的支持 | 支持 | 支持 |
限流 | 基於QPS,支持基於調用關係的限流 | 有限的支持 |
流量整形 | 支持預熱模式、勻速器模式、預熱排隊模式 | 不支持 |
系統自適應保護 | 支持 | 不支持 |
控制檯 | 可配置規則、查看秒級監控、機器發現等 | 簡單的監控查看 |
3、Sentinel特性瀏覽器
一、豐富的應用場景緩存
Sentinel承接了阿里巴巴近十年雙十一大促流量的核心場景,例如秒殺(突發流量控制在系統容量能夠承受的範圍)、消息削峯填谷、實時熔斷下游不可用應用等。併發
二、完備的實時監控app
Sentinel同時提供實時的監控功能。咱們能夠在控制檯中看到接入應用的單臺機器秒級數據,甚至500臺如下規模的集羣的彙總進行狀況。框架
三、普遍的開源生態async
Sentinel提供開箱即用的與其它開源框架的整合模塊,例如與spring cloud、dubbo、grpc的整合。咱們只須要引入響應的依賴並進行簡單的配置便可快速的接入Sentinel。
四、完美的SPI擴展點
Sentinel提供簡單易用、完善的SPI擴展點。咱們能夠經過實現擴展點,快速的定製邏輯。例如定製規則管理、適配數據源等。
4、資源和規則
資源是Sentinel的關鍵概念。它能夠是java應用程序中的任何內容,例如,由應用程序提供的服務,或由應用程序調用的其它應用提供的服務,甚至能夠是一段代碼。只是經過Sentinel API定義的代碼,就是資源,可以被Sentinel保護起來,大部分狀況下,可使用方法簽名,URL,甚至服務名稱做爲資源名來表示資源。
圍繞資源的實時狀態設定的規則,能夠包括流量控制規則、熔斷降級規則以及系統保護規則。全部規則能夠動態實時調整。
Sentinel中調用SphU或者SphO的entry方法獲取限流資源,不一樣的是前者獲取限流資源失敗時會跑BlockException異常,後者返回false,兩者的實現都是基於CtSph類完成的。
5、核心概念
一、Resource
resource是Sentinel中最重要的一個概念,Sentinel經過資源來保護具體的業務代碼或其它後方服務。Sentinel把複雜的邏輯給屏蔽了,用戶只須要爲受保護的代碼或服務定義一個資源,而後定義規則就能夠了,剩下的統統交給Sentinel來處理。而且資源和規則是解耦的,規則甚至能夠在運行時動態修改。定義完資源後,就能夠經過在程序中埋點來保護你本身的服務,埋點的方式有兩種:
(1)try-catch方式(經過SphU.entry(...)),當catch到BlockException時執行異常處理或fallback。
(2)if-else方式(經過SphO.entry(...)),當返回false時執行異常處理或fallback。
以上兩種方式都是經過硬編碼的形式定義資源而後進行資源埋點的,對業務代碼的侵入太大,從0.1.1版本開始,Sentinel加入了註解的支持,能夠經過註解來定義資源,具體的註解爲:SentinelResource。經過註解除了能夠定義資源外,還能夠指定blockHandler和fallback方法。
在Sentinel中具體表示資源的類:ResourceWrapper,它是一個抽象的包裝類,包裝了資源的Name和EntryType。他有兩個實現類,分別是:StringResourceWrapper和MethodResourceWrapper。顧名思義,StringResourceWrapper是經過對一串字符進行包裝,是一個通用的資源包裝類,MethodResourceWrapper是對方法調用的包裝。
二、Context
Context是對資源操做時的上下文環境,每一個資源操做(針對resource的entry和exit)必須屬於一個Context,若是程序中未指定Context,會建立name爲「Sentinel_default_context」的默認Context。一個Context生命週期內可能有多個資源操做,Context生命週期內的最後一個資源exit時會清理該Context,這也預示着整個Context生命週期的結束。Context主要屬性以下:
public class Context { // context名字,默認名字 "sentinel_default_context" private final String name; // context入口節點,每一個context必須有一個entranceNode private DefaultNode entranceNode; // context當前entry,Context生命週期中可能有多個Entry,全部curEntry會有變化 private Entry curEntry; // The origin of this context (usually indicate different invokers, e.g. service consumer name or origin IP). private String origin = ""; private final boolean async; }
一個Context生命週期內Context只能初始化一次,存到ThreadLocal中,而且只有在非NULL時纔會進行初始化。若是想在調用SphU.entry()或SphO.entry()前,自定義一個context,則經過ContextUtil.enter()方法來建立。context保存在ThreadLocal中,每次執行的時候會優先到ThreadLocal中獲取,爲null時會建立一個context。當Entry執行exit方法時,若是entry的parent節點爲null,表示當前context中最外層的entry了,此時將threadLocals中的context清空。
三、Entry
每次執行SphU.entry()或SphO.entry()都會返回一個Entry,Entry表示一次資源操做,內部會保存單籤invocation信息。在一個context聲明週期中屢次資源操做,也就是對應多個Entry,parent/child結構保存在Entry實例中,Entry類CtEntry結構以下:
class CtEntry extends Entry { protected Entry parent = null; protected Entry child = null; protected ProcessorSlot<Object> chain; protected Context context; } public abstract class Entry implements AutoCloseable { private long createTime; private Node curNode; /** * {@link Node} of the specific origin, Usually the origin is the Service Consumer. */ private Node originNode; private Throwable error; // 是否出現異常 protected ResourceWrapper resourceWrapper; // 資源信息 }
四、DefaultNode
Node默認實現類DefaultNode,該類還有一個子類EntranceNode;context有一個entranceNode屬性,Entry中有一個curNode屬性。
看到這裏,你是否是有疑問?爲何一個context有且僅有一個DefaultNode,咱們的resouece跑哪去了呢,其實,這裏的一個context有且僅有一個DefaultNode是在NodeSelectorSlot範圍內,NodeSelectorSlot是ProcessorSlotChain中的一環,獲取ProcessorSlotChain是根據Resource維度來的。總結爲一句話就是:針對同一個Resource,多個context對應多個DefaultNode;針對不一樣Resource,(不論是否是同一個context)對應多個不一樣DefaultNode。這還沒看明白 : ),好吧,我不bb了,上圖吧:
DefaultNode結構以下:
public class DefaultNode extends StatisticNode { private ResourceWrapper id; /** * The list of all child nodes. * 子節點集合 */ private volatile Set<Node> childList = new HashSet<>(); /** * Associated cluster node. */ private ClusterNode clusterNode; }
一個Resouce只有一個clusterNode,多個defaultNode對應一個clusterNode,若是defaultNode.clusterNode爲null,則在ClusterBuilderSlot.entry中會進行初始化。
同一個Resource,對應同一個ProcessorSlotChain,這塊處理邏輯在lookProcessChain方法中,以下:
ProcessorSlot<Object> lookProcessChain(ResourceWrapper resourceWrapper) { ProcessorSlotChain chain = chainMap.get(resourceWrapper); if (chain == null) { synchronized (LOCK) { chain = chainMap.get(resourceWrapper); if (chain == null) { // Entry size limit. if (chainMap.size() >= Constants.MAX_SLOT_CHAIN_SIZE) { return null; } chain = SlotChainProvider.newSlotChain(); Map<ResourceWrapper, ProcessorSlotChain> newMap = newHashMap<ResourceWrapper, ProcessorSlotChain>( chainMap.size() + 1); newMap.putAll(chainMap); newMap.put(resourceWrapper, chain); chainMap = newMap; } } } return chain; }
五、StatisticNode
StatisticNode中保存了資源的實時統計數據(基於滑動時間窗口機制),經過這些統計數據,sentinel才能進行限流、降級等一系列操做。StatisticNode屬性以下:
public class StatisticNode implements Node { /** * 秒級的滑動時間窗口(時間窗口單位500ms) */ private transient volatile Metric rollingCounterInSecond = newArrayMetric(SampleCountProperty.SAMPLE_COUNT, IntervalProperty.INTERVAL); /** * 分鐘級的滑動時間窗口(時間窗口單位1s) */ private transient Metric rollingCounterInMinute = new ArrayMetric(60, 60 * 1000,false); /** * The counter for thread count. * 線程個數用戶觸發線程數流控 */ private LongAdder curThreadNum = new LongAdder(); } public class ArrayMetric implements Metric { private final LeapArray<MetricBucket> data; } public class MetricBucket { // 保存統計值 private final LongAdder[] counters; // 最小rt private volatile long minRt; }
其中MetricBucket.counters數組大小爲MetricEvent枚舉值的個數,每一個枚舉對應一個統計項,好比PASS表示經過個數,限流可根據經過的個數和設置的限流規則配置count大小比較,得出是否觸發限流操做,全部枚舉值以下:
public enum MetricEvent { PASS, // Normal pass. BLOCK, // Normal block. EXCEPTION, SUCCESS, RT, OCCUPIED_PASS }
六、Slot
Slot是sentinel中很是重要的概念,sentinel的工做流程就是圍繞着一個個插槽所組成的插槽鏈來展開的。須要注意的是每一個插槽都有本身的職責,他們各司其職完美的配合,經過必定的編排順序,來達到最終的限流降級。默認的各個插槽之間的順序是固定的,由於有的插槽須要依賴其餘的插槽計算出來的結果才能進行工做。
sentinel經過SlotChainBuilder做爲SPI接口,使得Slot Chain具有了擴展的能力。咱們能夠經過實現SlotChainBuilder接口加入自定義Slot而且定義編排各個slot之間的排序,從而能夠給sentinel添加自定義的功能。
那SlotChain是在哪建立的呢?是在 CtSph.lookProcessChain() 方法中建立的,而且該方法會根據當前請求的資源先去一個靜態的HashMap中獲取,若是獲取不到纔會建立,建立後會保存到HashMap中。這就意味着,同一個資源會全局共享一個SlotChain。默認生成ProcessorSlotChain爲:
// DefaultSlotChainBuilder public ProcessorSlotChain build() { ProcessorSlotChain chain = new DefaultProcessorSlotChain(); chain.addLast(new NodeSelectorSlot()); chain.addLast(new ClusterBuilderSlot()); chain.addLast(new LogSlot()); chain.addLast(new StatisticSlot()); chain.addLast(new SystemSlot()); chain.addLast(new AuthoritySlot()); chain.addLast(new FlowSlot()); chain.addLast(new DegradeSlot()); return chain;
6、springcloud如何使用sentinel
學習了sentinel核心概念以後,感受整我的都很差了,真的是晦澀難懂,來個helloworld,輕鬆一下。
一、pom.xml
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId> </dependency>
二、 controller
@RestController public class TestController { @GetMapping(value = "/hello") @SentinelResource("hello") public String hello() { return "Hello Sentinel"; } }
三、引入dashboard
直接下載sentinel-dashboard的jar包。
默認是8080端口,在瀏覽器輸入:localhost:8080,默認帳號密碼:sentinel:sentinel,看到控制檯界面爲部署成功。
四、application.properties
server.port=8088 spring.application.name=spring-cloud-alibaba-sentinel-demo # sentinel dashboard spring.cloud.sentinel.transport.dashboard=localhost:8080
五、 啓動spring boot 項目,繼續訪問localhost:8080,會看到以下界面
六、 使用Sentinel實現接口限流(在控制檯)
七、測試
經過上面的配置,實現的是/hello接口qps最大是2,若是qps大於2,則快速失敗,配置完成,點擊保存,咱們快速刷新瀏覽器,會發現快速失敗
7、總結
本文主要介紹了Sentinel的概念、特性、與Hystrix的區別、一些核心概念和與SpringCloud的簡單整合。隨着微服務的流行,服務和服務之間的穩定性變得愈來愈重要。 Sentinel 以流量爲切入點,從流量控制、熔斷降級、系統負載保護等多個維度保護服務的穩定性。
每一篇博客都是一種經歷,程序猿生涯的痕跡,知識改變命運,命運要由本身掌控,願你遊歷半生,歸來還是少年。
欲速則不達,欲達則欲速!
更多精彩內容,首發公衆號【素小暖】,歡迎關注。