在Alibaba Sentinel 限流與熔斷初探(技巧篇) 的示例中我選擇了 sentinel-demo-apache-dubbo 做爲突破點,故本文就從該項目入手,看看 Sentinel 是如何對 Dubbo 作的適配,讓項目使用方無感知,只須要引入對應的依便可。java
sentinel-apache-dubbo-adapter 比較簡單,展開以下:
上面的代碼應該比較簡單,在正式進入源碼研究以前,我先拋出以下二個問題:數據庫
Dubbo 提供了 Filter 機制對功能進行無縫擴展,有關 Dubbo Filter 機制,你們能夠查閱筆者的源碼研究 Dubbo 系列:Dubbo Filter機制概述。apache
接下來咱們帶着上面的問題1開始本章的研究。架構
@併發
@Activate(group = "consumer") // @1 public class SentinelDubboConsumerFilter implements Filter { public SentinelDubboConsumerFilter() { RecordLog.info("Sentinel Apache Dubbo consumer filter initialized"); } @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { Entry interfaceEntry = null; Entry methodEntry = null; try { String resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboConsumerPrefix()); // @2 interfaceEntry = SphU.entry(invoker.getInterface().getName(), ResourceTypeConstants.COMMON_RPC, EntryType.OUT); // @3 methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC, EntryType.OUT); // @4 Result result = invoker.invoke(invocation); // @5 if (result.hasException()) { // @6 Throwable e = result.getException(); // Record common exception. Tracer.traceEntry(e, interfaceEntry); Tracer.traceEntry(e, methodEntry); } return result; } catch (BlockException e) { return DubboFallbackRegistry.getConsumerFallback().handle(invoker, invocation, e); // @7 } catch (RpcException e) { Tracer.traceEntry(e, interfaceEntry); Tracer.traceEntry(e, methodEntry); throw e; } finally { if (methodEntry != null) { // @8 methodEntry.exit(); } if (interfaceEntry != null) { interfaceEntry.exit(); } } } }
代碼@1:經過 @Activate 註解定義該 Filter 在客戶端生效。app
代碼@2:在 Sentinel 中一個很是核心的概念就是資源,即要定義限流的目標,當出現什麼異常(匹配用戶配置的規則)對什麼進行熔斷操做,Dubbo 服務中的資源一般是 Dubbo 服務,分爲服務接口級或方法級,故該方法返回 Dubbo 的資源名,其主要實現特徵以下:分佈式
代碼@3:調用 Sentinel 核心API SphU.entry 進入 Dubbo InterfaceName。從方法的名稱咱們也能很容易的理解,就是使用 Sentienl API 進入資源名爲 Dubbo 接口提供者類全路徑限定名,即認爲調用該方法,Sentienl 會收集該資源的調用信息,而後Sentinel 根據運行時收集的信息,再配合限流規則,熔斷等規則進行計算是否須要限流或熔斷。本節咱們不打算深刻研究 SphU 的核心方法研究,先初步瞭解該方法:ide
String name 資源的名稱。高併發
int resourceType 資源的類型,在 Sentinel 中目前定義了 以下五中資源:源碼分析
EntryType type
進入資源的方式,主要分爲 EntryType.OUT、EntryType.IN,只有 EntryType.IN 方式才能對資源進行阻塞。
代碼@4:調用 Sentinel 核心API SphU.entry 進入 Dubbo method 級別。
代碼@5:調用 Dubbo 服務提供者方法。
代碼@6:若是出現調用異常,能夠經過 Sentinel 的 Tracer.traceEntry 跟蹤本次調用資源進入的狀況,詳細 API 將在該系列的後續文章中詳細介紹。
代碼@7:若是是因爲觸發了限流、熔斷等操做,拋出了阻塞異常,可經過 註冊 ConsumerFallback 來實現消費者快速失敗,將在下文詳細介紹。
代碼@8: SphU.entry 與 資源的 exit 方法須要成對出現,不然會出現統計錯誤。
@Activate(group = "provider") public class SentinelDubboProviderFilter implements Filter { public SentinelDubboProviderFilter() { RecordLog.info("Sentinel Apache Dubbo provider filter initialized"); } @Override public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException { // Get origin caller. String application = DubboUtils.getApplication(invocation, ""); Entry interfaceEntry = null; Entry methodEntry = null; try { String resourceName = DubboUtils.getResourceName(invoker, invocation, DubboConfig.getDubboProviderPrefix()); // @1 String interfaceName = invoker.getInterface().getName(); // Only need to create entrance context at provider side, as context will take effect // at entrance of invocation chain only (for inbound traffic). ContextUtil.enter(resourceName, application); interfaceEntry = SphU.entry(interfaceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN); // @2 methodEntry = SphU.entry(resourceName, ResourceTypeConstants.COMMON_RPC, EntryType.IN, invocation.getArguments()); Result result = invoker.invoke(invocation); if (result.hasException()) { Throwable e = result.getException(); // Record common exception. Tracer.traceEntry(e, interfaceEntry); Tracer.traceEntry(e, methodEntry); } return result; } catch (BlockException e) { return DubboFallbackRegistry.getProviderFallback().handle(invoker, invocation, e); // @3 } catch (RpcException e) { Tracer.traceEntry(e, interfaceEntry); Tracer.traceEntry(e, methodEntry); throw e; } finally { if (methodEntry != null) { methodEntry.exit(1, invocation.getArguments()); } if (interfaceEntry != null) { interfaceEntry.exit(); } ContextUtil.exit(); } } }
Dubbo 服務提供者與消費端的適配套路差很少,這裏就重點闡述一下其不一樣點。
代碼@1:若是啓用前綴,默認服務提供者的資源會加上前綴:dubbo:provider:,能夠經過在配置文件中配置屬性 csp.sentinel.dubbo.resource.provider.prefix 改變其默認值。
代碼@2:服務端調用 SphU.entry 時其進入類型爲 EntryType.IN。
代碼@3:一樣能夠在 拋出阻塞異常(BlockException) 時指定快速失敗回調處理邏輯。
Sentinel Dubbo FallBack 機制比較簡單,就是提供一個全局的 FallBack 回調,能夠分別爲服務提供端,服務消費端指定。只需實現 DubboFallback 接口,其聲明以下:
而後須要調用 DubboFallbackRegistry 的 setConsumerFallback 和 setProviderFallback 方法分別註冊消費端,服務端相關的監聽器。一般只須要在啓動應用的時候,將其進行註冊便可。
本文只是以 Sentienl 對 Dubbo 的適配實現來了解 Sentinel 核心相關的 API,其核心實現就是利用 Dubbo 的 Filter 機制進行無縫的過濾攔截。但本文只是提到 Sentinel 以下核心方法:
上述這些方法,將在後面的文章中進行深刻探究,即從下一篇文章開始,咱們將真正進入 Sentinel 的世界中,讓咱們一探究竟限流、熔斷一般是如何實現的。
本文就介紹到這裏了,點贊是一種美德,您的點贊是我持續分享的最大動力,謝謝。
推薦閱讀:源碼分析 Alibaba Sentinel 專欄。
一、Alibaba Sentinel 限流與熔斷初探(技巧篇)
做者信息:丁威,《RocketMQ技術內幕》做者,目前擔任中通科技技術平臺部資深架構師,維護 中間件興趣圈公衆號,目前主要發表了源碼閱讀java集合、JUC(java併發包)、Netty、ElasticJob、Mycat、Dubbo、RocketMQ、mybaits等系列源碼。點擊連接:加入筆者的知識星球,一塊兒探討高併發、分佈式服務架構,分享閱讀源碼心得。