dubbo rpc filter实现剖析(一)
2.6.3版本,之前读的是2.4.9版本
本篇主要阐述dubbo rpc的filter的实现,包括作用,用法,原理,与Spring Cloud在这些能力的对比。
共提供了多少个?是哪些?发布时默认装配了哪些给他自身的扩展点机制?
从类与接口关系分析的结果文档中可以看到共20个:
241 Filter
--241.1 CacheFilter
--241.2 MonitorFilter
--241.3 AccessLogFilter
--241.4 ActiveLimitFilter
--241.5 ClassLoaderFilter
--241.6 CompatibleFilter
--241.7 ConsumerContextFilter
--241.8 ContextFilter
--241.9 DeprecatedFilter
--241.10 EchoFilter
--241.11 ExceptionFilter
--241.12 ExecuteLimitFilter
--241.13 GenericFilter
--241.14 GenericImplFilter
--241.15 TimeoutFilter
--241.16 TokenFilter
--241.17 TpsLimitFilter
--241.18 FutureFilter
--241.19 TraceFilter
--241.20 ValidationFilter
从发布的jar中的META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Filter中发现除了TpsLimitFilter之外,其余的都装上了。
cache=com.alibaba.dubbo.cache.filter.CacheFilter
validation=com.alibaba.dubbo.validation.filter.ValidationFilter
echo=com.alibaba.dubbo.rpc.filter.EchoFilter
generic=com.alibaba.dubbo.rpc.filter.GenericFilter
genericimpl=com.alibaba.dubbo.rpc.filter.GenericImplFilter
token=com.alibaba.dubbo.rpc.filter.TokenFilter
accesslog=com.alibaba.dubbo.rpc.filter.AccessLogFilter
activelimit=com.alibaba.dubbo.rpc.filter.ActiveLimitFilter
classloader=com.alibaba.dubbo.rpc.filter.ClassLoaderFilter
context=com.alibaba.dubbo.rpc.filter.ContextFilter
consumercontext=com.alibaba.dubbo.rpc.filter.ConsumerContextFilter
exception=com.alibaba.dubbo.rpc.filter.ExceptionFilter
executelimit=com.alibaba.dubbo.rpc.filter.ExecuteLimitFilter
deprecated=com.alibaba.dubbo.rpc.filter.DeprecatedFilter
compatible=com.alibaba.dubbo.rpc.filter.CompatibleFilter
timeout=com.alibaba.dubbo.rpc.filter.TimeoutFilter
trace=com.alibaba.dubbo.rpc.protocol.dubbo.filter.TraceFilter
future=com.alibaba.dubbo.rpc.protocol.dubbo.filter.FutureFilter
monitor=com.alibaba.dubbo.monitor.support.MonitorFilter
这些filter都有什么作用?如何使用?实现原理是什么?Spring Cloud是否也提供了这些能力?有什么差异?
CacheFilter
作用
缓存调用结果,比如配置在consumer端, 比如我们通过id查某个用户的信息,对于特定的一个id,在consumer端第一次调用时会给provider端发请求,后面再调用时,直接用consumer端缓存的结果返回,你不再发请求给provider端。
使用方式
consumer侧的配置:
<dubbo:reference id="userService" interface="org.simonme.dubbo.demo.provider.service.UserService" filter="cache">
<dubbo:parameter key="cache" value="lru" />
</dubbo:reference>
能支持的全部cache定义在META-INF/dubbo/internal/com.alibaba.dubbo.cache.CacheFactory 当然你也可以遵循dubbo扩展点机制进行扩展。
默认提供三种:
threadlocal=com.alibaba.dubbo.cache.support.threadlocal.ThreadLocalCacheFactory
lru=com.alibaba.dubbo.cache.support.lru.LruCacheFactory
jcache=com.alibaba.dubbo.cache.support.jcache.JCacheFactory
实现原理
缓存的key
是你远程方法调用时传递的所有参数按规则
组装成字符串作为key。
具体规则就是: 基本类型 直接拼接,复合类型转成json字符串后再拼接。
实现原理也不是很复杂,根据invoker的url找到其对应的cache对象,再跟据上述缓存的key
找到缓存的结果。
有个不是太要紧的小问题,因为是根据invoker的url找到其对应的cache对象的,又因为invoker的url中含有remote.timestamp参数,所以你如果启用了consumer侧的缓存,consumer一直在服务状态,此时provider服务做了重启,那么consumer侧的缓存失效,会重新调用provider端。
ThreadLocalCache 是ThreadLocal配合HashMap实现
LRUCache 是继承自LinkedHashMap,同时结合ReentrantLock实现的线程安全的lru cache(最近最少使用),LinkedHashMap自带lru性质,通过构造参数控制,默认是fifo。
jache是封装的JCache API (JSR 107)。
与Spring Cloud对比
Spring Cloud提供了consumer侧的缓存能力,Hystrix
组件支持用requestCache.enabled配置是否启用缓存,也支持用cacheKeyMethod注解指定getkey方法。
ValidationFilter
作用
在consumer和provider端提供了校验能力
使用方式
假设你要对consumer端进行校验,在配置文件中配置如下:
<dubbo:reference id="userService" interface="org.simonme.dubbo.demo.provider.service.UserService" filter="validation">
<dubbo:parameter key="validation" value="JValidator" />
<!-- 配置一个实现了javax.validation.spi.ValidationProvider<T>接口校验器 -->
<dubbo:parameter key="jvalidation" value="org.hibernate.validator.HibernateValidator" />
</dubbo:reference>
filter要是配置多个的话,用逗号拼接,但是逗号前后不能有空格。
此处使用了hibernate的validator ,在你需要校验的接口方法上加校验注解即可,示例如下:
public User queryUser(@Range(min=0,message="用户id值不能小于0")int id);
当consumer端调用时传递了校验不通过的参数时,会收到ConstraintViolationException的异常。
实现原理
dubbo对接了javax.validation.Validation,hibernate等都有对其对接的实现,按需使用即可。也就是说dubbo自己不做具体校验的事情。
与Spring Cloud对比
spring在很早就支持validator。
EchoFilter
作用
在provider端提供回声服务的服务端的实现。
使用方式
这个filter略有特殊,无需在provider端的dubbo:service标签的filter中去配置,只要你在consumer做了echo回声调用,他都会产生作用,调试的时候也能看到能走到EchoFilter中。
consumer端的示例代码:
public class HelloClientTest
{
@Autowired
private HelloService helloService;
@SuppressWarnings("static-access")
@Test
public void testSayHello()
{
System.out.println(((EchoService)helloService).$echo("aaaa"));
}
}
就是把你的service类强转成EchoService,至于为什么能强转,可以参见之前写的文章 reference bean发起调用
实现原理
直接看EchoFilter代码,很简单,不再多说。
与Spring Cloud对比
Spring Cloud貌似没有这个能力。
GenericFilter
作用
先讨论一个问题,rpc,最简单的场景是consumer端调用provider端的一个服务,这个服务双方都遵循一个接口实现,按最简单的dubbo的demo玩法,是需要consumer和provider两段都要有这个接口声明的(包括接口参数的类型的相关类),比如:
org.simonme.dubbo.demo.provider.service.UserService.queryUser(int) 这是一个查询用户的服务接口,但是,如果consumer端没有这个服务的接口声明及其相关联的bean类,也就是如果仅仅在provider端能找到这个接口类,在consumer工程里压根没有这个类,那是否还能进行调用?
dubbo是可以的。 就是通过在proivder侧用GenericFilter达成目的。
使用方式
需要在consumer端声明这个bean的时候,加上generic="true"
配置即可。
<dubbo:reference id="userService" interface="org.simonme.dubbo.demo.provider.service.UserService" generic="true">
</dubbo:reference>
generic还支持nativejava和bean两个可选的选项。nativejava对应byte[]类型的参数,bean对应com.alibaba.dubbo.common.beanutil.JavaBeanDescriptor类型的参数。
此外在使用这个服务时,形式略有不同:
@Autowired
private GenericService userService;
@SuppressWarnings("static-access")
@Test
public void testSayHello()
{
Object result = userService.$invoke("queryUser", new String[] { "int" }, new Object[]{100});
System.out.println(result);
System.out.println(result.getClass());
}
用GenericService
表示你这个未知的服务类型,用$invoke
这个特殊方法发起的你的服务方法调用。调用完之后,dubbo会把结果以hashmap的形式返回给你。比如,我这里是个User的bean,User有id和name字段,那么返回的map中就与id和value两个key,其对应的值就是bean的这两个字段的字段值。
为了便于理解,贴一下UserService的代码:
public interface UserService
{
public User queryUser(@Range(min=0,message="用户id值不能小于0")int id);
}
上面这个写法,解释了怎么在consumer端没有org.simonme.dubbo.demo.provider.service.UserService接口声明的时候,调用他的queryUser方法。provider端无需改动,和正常写rpc服务一样配置即可。
用法也可以参见官方文档,使用泛化调用。
实现原理
直接看GenericFilter代码,通过方法名(必须是$invoke),参数个数等判断是否命中,很简单,不再多说。
与Spring Cloud对比
Spring Cloud可以算有这个能力,因为Spring Cloud是http json的,http json天然不需要调用端有接口声明。
GenericImplFilter
作用
与 GenericFilter 类似,是一个相对的东西,他是用在provider端没有服务接口声明类时,使用的。使用方法可以参见官方文档实现泛化调用。不再多说。
TokenFilter
作用
官方文档说法是:
通过令牌验证在注册中心控制权限,以决定要不要下发令牌给消费者,可以防止消费者绕过注册中心访问提供者,另外通过注册中心可灵活改变授权方式,而不需修改或升级提供者。
使用方式
provider端:在provider上配置token值。 TokenFilter会在provider侧校验。
<dubbo:service interface="org.simonme.dubbo.demo.provider.service.UserService" ref="m00001.app001.xx.userService" timeout="600000" token="123456">
consumer端可以用编程的方式获取后塞,
RpcContext.getContext().setAttachment("token", "a37b6115-c171-43cd-b65c-38b636ee96cc");
或者通过配置parameter,
<dubbo:parameter key="token" value="123456" />
或者啥都不要处理
,默认consumer会从provider服务url中解析到。provider的url中会含有token字段。
实现原理
如果 token 配置的是true, 那么在provider export服务时,ServiceConfig
会生成UUID,这个其实不是由注册中心生成的。
当然token也支持配置固定密码。
比对过程:
if (!ConfigUtils.isEmpty(token)) {
if (ConfigUtils.isDefault(token)) {// 是 true或者default字段串就是表示默认
map.put(Constants.TOKEN_KEY, UUID.randomUUID().toString());
} else {
map.put(Constants.TOKEN_KEY, token);
}
}
consumer端如果没有通过上述代码的方式或者parameter配置的方式传送token,那么consumer端会在调用时,先将从注册中心拿到的provider端的url中部分参数转换成attachment给consumer端用,这个部分参数就包括token。具体代码在DubboInvoker
中,如下:
public DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers) {
super(serviceType, url, new String[]{Constants.INTERFACE_KEY, Constants.GROUP_KEY, Constants.TOKEN_KEY, Constants.TIMEOUT_KEY});// 此处会调用父类方法进行需要的参数从url转到attachment中
this.clients = clients;
// get version.
this.version = url.getParameter(Constants.VERSION_KEY, "0.0.0");
this.invokers = invokers;
}
当provider端使用注册中心,consumer试图不带token进行直接消费时,会被拒绝。 当consumer端也是连注册中心时,哪怕不送显式送token(实际上dubbo会自动送)也可以正常调用。但是如果consumer端用了注册中心,且显式送了token,那么就要送对。否则报错。
与Spring Cloud对比
Spring Cloud的注册中心eureka也是支持密码验证的。
AccessLogFilter
作用
用在provider端打印rpc请求日志,支持打到指定文件,支持异步。
使用方式
在provider侧配置名为accesslog的filter,若需要指定路径,则将accesslog参数设置成具体路径即可,默认需要将其配置成true。
<dubbo:service interface="org.simonme.dubbo.demo.provider.service.UserService" ref="m00001.app001.xx.userService" filter="accesslog"
timeout="600000" token="123456">
<dubbo:parameter key="accesslog" value="true" />
</dubbo:service>
实现原理
参见AccessLogFilter 代码,比较简单。
与Spring Cloud对比
zuul网关也支持。
zuul.debug.request=true #如果设置了这个,默认所有的请求都会debug
zuul.include-debug-header: true #打印头
未设置zuul.debug.request=true,可以用zuul_host:zuul_port/路径?debug=true debug你的指定请求
ActiveLimitFilter
作用
在consumer端实现并发数控制,能支持到方法级。
使用方式
在consumer侧配置
<dubbo:reference interface="com.foo.BarService">
<dubbo:method name="sayHello" actives="10" />
</dubbo:service>
实现原理
ConcurrentHashMap 配合 AtomicInteger AtomicLong完成。相关代码参见 ActiveLimitFilter 与 RpcStatus。
与Spring Cloud对比
Spring Cloud也支持。
ExecuteLimitFilter 与之类似,只是用在了provider端。
ClassLoaderFilter
作用
保持 调用下层invoker前后的ClassLoader一致
ContextFilter
作用
在provider端,对一些dubbo自己使用的保留key进行过滤,防止别人误传。
实现原理
参见代码即可,比较简单。
与Spring Cloud对比
N/A
dubbo rpc filter实现剖析(一)的更多相关文章
- dubbo rpc filter实现剖析(二)
2.6.3版本,之前读的是2.4.9版本 本篇主要阐述dubbo rpc的filter的实现,包括作用,用法,原理,与Spring Cloud在这些能力的对比. 整个filter列表的获取过程在 co ...
- dubbo 提示No such extension Filter for filter/com.alibaba.dubbo.rpc.Filter
配置时 <dubbo:provider filter="DubboExceptionFilter"></dubbo:provider> DubboExcep ...
- Dubbo的Filter链梳理---分组可见和顺序调整
前言: 刚刚写了篇博文: Dubbo透传traceId/logid的一种思路, 对dubbo的filter机制有了一个直观的理解. 同时对filter也多了一些好奇心, 好奇filter链是如何组织的 ...
- dubbo 自定义 Filter
通过自定义 Filter,可以在 dubbo 调用链中加入特定的逻辑,比如埋点分析调用链. 1. 新建 Filter 类 // @Activate(group = {Constants.CONSUME ...
- dubbo 之filter使用
1.继承接口com.alibaba.dubbo.rpc.Filter实现public Result invoke(Invoker<?> invoker, Invocation invoca ...
- Dubbo RPC源码解读
https://yq.aliyun.com/articles/272405#27 本文代码摘录的时候,将一些与本流程无关的内容去掉了,如有需要请看源码. 一.闲言碎语 使用rpc框架已经多年了,虽然之 ...
- dubbo 使用 filter 报错解决
dubbo可以用filter实现类似tomcat filter过滤器. 实现1.接口请求时间监控. 2.打印输入输出日志(输出日志有应用自己决定) 配置时出现报错. No such extension ...
- Dubbo自定义Filter统一处理异常
Dubbo版本:2.7 使用自定义Filter注意事项 1.自定义名称不能和默认Filter相同,否则可能不生效 2.只用定义Filter类和META-INF下的文本文件,不用添加配置,@Activa ...
- 报错 500 - Request processing failed; nested exception is com.alibaba.dubbo.rpc.RpcException的解决放案
版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/loveliness_peri/artic ...
随机推荐
- I/O操做总结(二)
文件的操作 这一节我们来讨论关于文件自身的操作 不浪费唾沫了,用代码说话…… 实例1:创建文件对象 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 ...
- swift基础-2
一.基本运算符 let a = 5 var b = 10 b = a if a = b{ swift 中赋值运算符,并不将自身作为一个值进行返回,所以编译不合法,帮开发者避免错误,很人性化的语言 } ...
- Git基础使用教程(仓库初始化,源码clone,源码push)
一.下载Git源码管理客户端 Git下载地址:https://git-scm.com/ 二.检查电脑是否已安装Git 1)已安装:输入git出现下图提示则代表已安装成功. 2)未安装情况下git会出现 ...
- 百度地图web 笔记
1.marker点击事件获取marker的title和lebal等信息 marker.setTitle(title); marker.setLabel(label); marker.addEventL ...
- 【Python图像特征的音乐序列生成】关于数据库到底在哪里下载
毕竟原网站一个是14年前的一个是16年前的…… 1,http://ifdo.ca/~seymour/nottingham/nottingham.html 这个网站可以下载zip包. 2,https:/ ...
- 使用ABAP代码返回S/4HANA Material上维护的Attachment明细
在事务码 MM02里为ID为16的material维护附件: 如何使用ABAP代码获得如下附件的名称和文件内容? REPORT zgos_api. DATA ls_appl_object TY ...
- thinkphp 的事务回滚处理 和 原始PHP的事务回滚实例
1. 要程序里面支持事务,首先连接的数据库和数据表必须支持事务 mysql 为例: 数据库InnoDB支持 transactions 数据表支持事务:InnoDB 支持transaction ...
- momentum公式
momentum对于w的更新公式: http://caffe.berkeleyvision.org/tutorial/solver.html
- java中求几个字符串的最大公共子串 使用了比较器Comparator
package com.swift; import java.util.ArrayList; import java.util.Collections; import java.util.Compar ...
- iOS中的数据存储方式_Preference(NSUserDefaults)
NSUserDefaults适合存储轻量级的本地数据,项目中,我会把一些简单的数据密码.网址.登陆状态BOOL.整型/浮点型数据等和用户有关的数据用它存储.但是它不能存储自定义的对象! 实例化一个 N ...