在 Alibaba Sentinel 限流与熔断初探(技巧篇) 的示例中我选择了 sentinel-demo-apache-dubbo 作为突破点, 故本文就从该项目入手, 看看 Sentinel 是如何对 Dubbo 做的适配, 让项目使用方无感知, 只需要引入对应的依即可.
sentinel-apache-dubbo-adapter 比较简单, 展开如下:
上面的代码应该比较简单, 在正式进入源码研究之前, 我先抛出如下二个问题:
1, 限流, 熔断相关的功能是在 Dubbo 的客户端实现还是服务端实现? 为什么?
2, 如何对 Dubbo 进行功能扩展而无需改动业务代码?
Dubbo 提供了 Filter 机制对功能进行无缝扩展, 有关 Dubbo Filter 机制, 大家可以查阅笔者的源码研究 Dubbo 系列: Dubbo Filter 机制概述 https://mp.weixin.qq.com/s/uv7ev-D-9wo3Oct3NCcKTQ .
接下来我们带着上面的问题 1 开始本章的研究.
@
目录
1, 源码分析 SentinelDubboConsumerFilter
2, 源码分析 SentienlDubboProviderFilters
3,Sentienl Dubbo FallBack 机制
4, 总结
1, 源码分析 SentinelDubboConsumerFilter
- @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 在客户端生效.
代码 @2: 在 Sentinel 中一个非常核心的概念就是资源, 即要定义限流的目标, 当出现什么异常 (匹配用户配置的规则) 对什么进行熔断操作, Dubbo 服务中的资源通常是 Dubbo 服务, 分为服务接口级或方法级, 故该方法返回 Dubbo 的资源名, 其主要实现特征如下:
如果启用用户定义资源的前缀, 默认为 false , 可以通过配置属性: csp.sentinel.dubbo.resource.use.prefix 来定义是否需要启用前缀. 如果启用前缀, 消费端的默认前缀为 dubbo:consumer:, 可以通过配置属性 csp.sentinel.dubbo.resource.consumer.prefix 来自定义消费端的资源前缀.
Dubbo 资源的名称表示方法为: interfaceName + ":" + methodName + "(" + "paramTyp1 参数列表, 多个用 , 隔开" + ")".
代码 @3: 调用 Sentinel 核心 API SphU.entry 进入 Dubbo InterfaceName. 从方法的名称我们也能很容易的理解, 就是使用 Sentienl API 进入资源名为 Dubbo 接口提供者类全路径限定名, 即认为调用该方法, Sentienl 会收集该资源的调用信息, 然后 Sentinel 根据运行时收集的信息, 再配合限流规则, 熔断等规则进行计算是否需要限流或熔断. 本节我们不打算深入研究 SphU 的核心方法研究, 先初步了解该方法:
String name 资源的名称.
int resourceType 资源的类型, 在 Sentinel 中目前定义了 如下五中资源:
ResourceTypeConstants.COMMON
同样类型.
ResourceTypeConstants.COMMON_web
Web 类资源.
ResourceTypeConstants.COMMON_RPC
RPC 类型.
ResourceTypeConstants.COMMON_API_GATEWAY
接口网关.
ResourceTypeConstants.COMMON_DB_SQL
数据库 SQL 语句.
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 方法需要成对出现, 否则会出现统计错误.
2, 源码分析 SentienlDubboProviderFilters
- @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) 时指定快速失败回调处理逻辑.
3,Sentienl Dubbo FallBack 机制
Sentinel Dubbo FallBack 机制比较简单, 就是提供一个全局的 FallBack 回调, 可以分别为服务提供端, 服务消费端指定. 只需实现 DubboFallback 接口, 其声明如下:
然后需要调用 DubboFallbackRegistry 的 setConsumerFallback 和 setProviderFallback 方法分别注册消费端, 服务端相关的监听器. 通常只需要在启动应用的时候, 将其进行注册即可.
4, 总结
本文只是以 Sentienl 对 Dubbo 的适配实现来了解 Sentinel 核心相关的 API, 其核心实现就是利用 Dubbo 的 Filter 机制进行无缝的过滤拦截. 但本文只是提到 Sentinel 如下核心方法:
- SphU.entry
- Entry.exit
- Tracer.traceEntry
上述这些方法, 将在后面的文章中进行深入探究, 即从下一篇文章开始, 我们将真正进入 Sentinel 的世界中, 让我们一探究竟限流, 熔断通常是如何实现的.
本文就介绍到这里了, 点赞是一种美德, 您的点赞是我持续分享的最大动力, 谢谢.
来源: https://www.cnblogs.com/dingwpmz/p/12687767.html