在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等系列源码。点击连接:加入笔者的知识星球,一块儿探讨高并发、分布式服务架构,分享阅读源码心得。