源码分析 Sentinel 之 Dubbo 适配原理
在Alibaba Sentinel 限流与熔断初探(技巧篇) 的示例中我选择了 sentinel-demo-apache-dubbo 作为突破点,故本文就从该项目入手,看看 Sentinel 是如何对 Dubbo 做的适配,让项目使用方无感知,只需要引入对应的依即可。
sentinel-apache-dubbo-adapter 比较简单,展开如下:

上面的代码应该比较简单,在正式进入源码研究之前,我先抛出如下二个问题:
- 1、限流、熔断相关的功能是在 Dubbo 的客户端实现还是服务端实现?为什么?
- 2、如何对 Dubbo 进行功能扩展而无需改动业务代码?
Dubbo 提供了 Filter 机制对功能进行无缝扩展,有关 Dubbo Filter 机制,大家可以查阅笔者的源码研究 Dubbo 系列:Dubbo Filter机制概述。
接下来我们带着上面的问题1开始本章的研究。
@
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 语句。
- ResourceTypeConstants.COMMON
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 的世界中,让我们一探究竟限流、熔断通常是如何实现的。
本文就介绍到这里了,点赞是一种美德,您的点赞是我持续分享的最大动力,谢谢。
作者信息:丁威,《RocketMQ技术内幕》作者,目前担任中通科技技术平台部资深架构师,RocketMQ官方社区优秀布道师、CSDN2019博客之星TOP10,维护公众号 中间件兴趣圈公众号,目前主要发表了源码阅读java集合、JUC(java并发包)、Netty、ElasticJob、Mycat、Dubbo、RocketMQ、mybaits等系列源码。点击链接:加入笔者的知识星球,一起探讨高并发、分布式服务架构,分享阅读源码心得。
源码分析 Sentinel 之 Dubbo 适配原理的更多相关文章
- 5.Sentinel源码分析—Sentinel如何实现自适应限流?
Sentinel源码解析系列: 1.Sentinel源码分析-FlowRuleManager加载规则做了什么? 2. Sentinel源码分析-Sentinel是如何进行流量统计的? 3. Senti ...
- 4.Sentinel源码分析— Sentinel是如何做到降级的?
各位中秋节快乐啊,我觉得在这个月圆之夜有必要写一篇源码解析,以表示我内心的高兴~ Sentinel源码解析系列: 1.Sentinel源码分析-FlowRuleManager加载规则做了什么? 2. ...
- 并发编程学习笔记(9)----AQS的共享模式源码分析及CountDownLatch使用及原理
1. AQS共享模式 前面已经说过了AQS的原理及独享模式的源码分析,今天就来学习共享模式下的AQS的几个接口的源码. 首先还是从顶级接口acquireShared()方法入手: public fin ...
- 6.Sentinel源码分析—Sentinel是如何动态加载配置限流的?
Sentinel源码解析系列: 1.Sentinel源码分析-FlowRuleManager加载规则做了什么? 2. Sentinel源码分析-Sentinel是如何进行流量统计的? 3. Senti ...
- 7.Sentinel源码分析—Sentinel是怎么和控制台通信的?
这里会介绍: Sentinel会使用多线程的方式实现一个类Reactor的IO模型 Sentinel会使用心跳检测来观察控制台是否正常 Sentinel源码解析系列: 1.Sentinel源码分析-F ...
- Guava 源码分析之Cache的实现原理
Guava 源码分析之Cache的实现原理 前言 Google 出的 Guava 是 Java 核心增强的库,应用非常广泛. 我平时用的也挺频繁,这次就借助日常使用的 Cache 组件来看看 Goog ...
- 从SpringBoot源码分析 配置文件的加载原理和优先级
本文从SpringBoot源码分析 配置文件的加载原理和配置文件的优先级 跟入源码之前,先提一个问题: SpringBoot 既可以加载指定目录下的配置文件获取配置项,也可以通过启动参数( ...
- MyBatis源码分析(2)—— Plugin原理
@(MyBatis)[Plugin] MyBatis源码分析--Plugin原理 Plugin原理 Plugin的实现采用了Java的动态代理,应用了责任链设计模式 InterceptorChain ...
- Spring 源码分析之 bean 依赖注入原理(注入属性)
最近在研究Spring bean 生命周期相关知识点以及源码,所以打算写一篇 Spring bean生命周期相关的文章,但是整理过程中发现涉及的点太多而且又很复杂,很难在一篇文章中把Spri ...
随机推荐
- Eclipse中项目过大引起的 IDE 加载缓慢,JVM 内存不足的情况解决
如果 IDE 加载项目非常缓慢,甚至常常出现卡死的情况,有可能是开发工具设置的 JVM 内存不够引起的.解决办法:找到 Eclipse 的安装目录,修改 Eclipse.ini 配置文件.修改此配置文 ...
- undefined reference to 问题汇总及解决方法 ----- 还有一种问题没有解决(可能是顺序问题)
1.链接时缺失了相关的目标文件 2.链接时缺少了相关的库文件 3.链接的库文件中有使用了另一个库文件 4.多个库文件链接顺序问题 5.定义与实现不一致 6.在c++代码中链接C语言的库 转载地址: ...
- ZOJ-1163-The Staircases
dp[i][j]表示i个砖头构成的最高台阶不高于j的楼梯数目 Accepted 1163 C++11 0 2280 #include "bits/stdc++.h" using n ...
- labview程序结构
三种范式:面向过程.面向事件,面向对象:六种模式:标准状态机.消息队列.生产者/消费者(事件).生产者/消费者(数据).用户界面事件处理.主/从 事件结构相当于是一个while语句里边嵌套了一个条件结 ...
- SPA(单页面web应用)和MPA(多页面web应用)的区别
转:https://blog.csdn.net/amaniz/article/details/79203562 vue多页面应用开发请参见: https://github.com/amunamuna/ ...
- Zabbix 监控进程参考
1)zabbix自动发现占用内存最大top10进程并监控资源 http://blog.csdn.net/ybx13218464908/article/details/47819401
- Python Web 基础向(四) 浅谈数据层
数据层一般会给人带来一些困扰,在于其定位不准确.聚合Model的工作也可以放在逻辑层做,但会导致逻辑层变重,经常出现大段晦涩代码.因此我的建议是保留Model聚合层,尽管会导致工作量的略微增加,但却可 ...
- LeetCode43(字符串相乘)
题目: 给定两个以字符串形式表示的非负整数 num1 和 num2,返回 num1 和 num2 的乘积,它们的乘积也表示为字符串形式. 示例 1: 输入: num1 = "2", ...
- 吴裕雄--天生自然KITTEN编程:演唱会
- 码海拾遗:基于MySQL Connector/C++的MySQL操作(连接池)
1.MySQL安装及简单设置 (1)安装:在OSX系统下,可以使用万能的“brew install”命令来进行安装:brew isntall mysql(默认安装最新版的MySQL) (2)启动:br ...