Dubbo扩展点SPI实践示例解析
作者:UniJoy 发布时间:2021-12-14 12:56:45
Dubbo的扩展点加载从JDK标准的SPI(Service Provider Interface)扩展点发现机制加强而来。Dubbo改进了JDK标准的SPI的以下问题:
JDK标准的SPI会一次性实例化扩展点所有实现,如果有扩展实现初始化很耗时,但如果没用上也加载,会很浪费资源。如果扩展点加载失败,连扩展点的名称都拿不到了。
Dubbo增加了对扩展点IoC和AOP的支持,一个扩展点可以直接setter注入其它扩展点。 比如:Filter是Dubbo的集合类扩展点的一种,还有其他如InvokerListener, ExportListener, TelnetHandler, StatusChecker等扩展,这个有时间可以都看看(搜索Dubbo开发者指南:SPI参考手册)
扩展点配置:
src\
|-main\
|-java\
|-com\
|-xxx\
|-XxxFilter.java (实现Filter接口)\
|-resources\
|-META-INF\
|-dubbo\
|-com.alibaba.dubbo.rpc.Filter (纯文本文件,内容为:xxx=com.xxx.XxxFilter)
扩展实现类:
@Activate
public class AuthorityFilter implements Filter {
private static Logger logger = LoggerFactory.getLogger(AuthorityFilter.class);
List<String> ipWhiteList = AuthorityUtils.getAccessListByKey("1");
@Override
public Result invoke(Invoker<?> invoker, Invocation invocation) throws RpcException {
[logger.info](http://logger.info/)("########go into filtersuccess !!########");
String clientIp = RpcContext.getContext().getRemoteHost();
String host = IpParseUtils.getHostByIp(clientIp);
if (ipWhiteList.contains(host))
Unknown macro: { [logger.info](http://logger.info/)("########白名单校验通过!########"); return invoker.invoke(invocation); }
else
Unknown macro: { [logger.info](http://logger.info/)("########白名单校验未通过!########"); return new RpcResult(); }
}
}
拦截配置文件:
在扩展类的jar包内,放置扩展点配置文件:META-INF/dubbo/接口全限定名,内容为:配置名=扩展实现类全限定名,多个实现类用换行符分隔。(注意:这里的配置文件是放在你自己的jar包内,不是dubbo本身的jar包内,Dubbo会全ClassPath扫描所有jar包内同名的这个文件,然后进行合并)
注意这个xxx=左侧的名字跟你扩展类同名哦(这是个坑儿,别问我怎么知道的)!
AuthorityFilter=your.extend.dubbo.async.provider.AuthorityFilter
调用拦截扩展:
<dubbo:provider filter="AuthorityFilter"></dubbo:provider>
拦截扩展说明:
服务提供方和服务消费方调用过程拦截,Dubbo本身的大多功能均基于此扩展点实现,每次远程方法执行,该拦截都会被执行,请注意对性能的影响。\
常用约定:
用户自定义filter默认在内置filter之后。
特殊值default,表示缺省扩展点插入的位置。 比如:filter="xxx,default,yyy",表示xxx在缺省filter之前,yyy在缺省filter之后。\
特殊符号-,表示剔除。
比如:filter="-foo1",剔除添加缺省扩展点foo1。
比如:filter="-default",剔除添加所有缺省扩展点。\provider和service同时配置的filter时,累加所有filter,而不是覆盖。
比如:<dubbo:provider filter="xxx,yyy"/>和<dubbo:service filter="aaa,bbb" />,则xxx,yyy,aaa,bbb均会生效。
如果要覆盖,需配置:<dubbo:service filter="-xxx,-yyy,aaa,bbb" />\
实现细节:
解析服务
基于dubbo.jar内的META-INF/spring.handlers配置,Spring在遇到dubbo名称空间时,会回调DubboNamespaceHandler。
所有dubbo的标签,都统一用DubboBeanDefinitionParser进行解析,基于一对一属性映射,将XML标签解析为Bean对象。
在ServiceConfig.export()或ReferenceConfig.get()初始化时,将Bean对象转换URL格式,所有Bean属性转成URL的参数。
然后将URL传给Protocol扩展点,基于扩展点的Adaptive机制,根据URL的协议头,进行不同协议的服务暴露或引用。
拦截服务
基于扩展点的Wrapper机制,所有的Protocol扩展点都会自动套上Wrapper类。
基于ProtocolFilterWrapper类,将所有Filter组装成链,在链的最后一节调用真实的引用。
基于ProtocolListenerWrapper类,将所有InvokerListener和ExporterListener组装集合,在暴露和引用前后,进行回调。
包括监控在内,所有附加功能,全部通过Filter拦截实现。
还有暴露服务和引用服务,个人觉得看这上面这两个对写程序相对更有用些!
扩展点的几个特点:
扩展点自动包装
自动Wrap扩展点的Wrapper类
ExtensionLoader会把加载扩展点时(通过扩展点配置文件中内容),如果该实现有拷贝构造函数,则判定为扩展点Wrapper类。Wrapper类同样实现了扩展点接口。
扩展点自动装配
加载扩展点时,自动注入依赖的扩展点
加载扩展点时,扩展点实现类的成员如果为其它扩展点类型,ExtensionLoader在会自动注入依赖的扩展点。
ExtensionLoader通过扫描扩展点实现类的所有set方法来判定其成员。即ExtensionLoader会执行扩展点的拼装操作。
ExtensionLoader加载CarMaker的扩展点实现RaceCar时,setWheelMaker方法的WheelMaker也是扩展点则会注入WheelMaker的实现。
扩展点自适应
扩展点的Adaptive实例
ExtensionLoader注入的依赖扩展点是一个Adaptive实例,直到扩展点方法执行时才决定调用是一个扩展点实现。
Dubbo使用URL对象(包含了Key-Value)传递配置信息
扩展点方法调用会有URL参数(或是参数有URL成员)
这样依赖的扩展点也可以从URL拿到配置信息,所有的扩展点自己定好配置的Key后,配置信息从URL上从最外层传入。URL在配置传递上即是一条总线。
示例:有两个为扩展点CarMaker(造车者)、wheelMaker(造轮者)
扩展点自动激活
对于集合类扩展点,比如:Filter, InvokerListener, ExportListener, TelnetHandler, StatusChecker等,可以同时加载多个实现,此时,可以用自动激活来简化配置,如:
import com.alibaba.dubbo.common.extension.Activate;
import com.alibaba.dubbo.rpc.Filter;
@Activate // 无条件自动激活
public class XxxFilter implements Filter {
// ...
}
来源:https://juejin.cn/post/7155508816207937573


猜你喜欢
- MyBatis注解实现动态SQL在 Mybatis 中,使用注解可以很方便的进行sql操作,但很多动态 SQL 都是由 xml 配置实现的。
- 在之前文章的铺垫下,再为大家分享一篇:Android手势密码,附源码下载,不要错过。源码下载:http://xiazai.jb51.net/
- 本文实例为大家分享了Android实现竖直跑马灯效果的具体代码,供大家参考,具体内容如下首先给出跑马灯效果图中间的色块是因为视频转成GIF造
- 我在5月份的时候就申请了洞态IAST企业版内测,算是比较早的一批用户了。聊聊几个我比较在意的问题,比如API接口覆盖率、第三方开源组件检测以
- Java对自动登陆功能的简单实现,仅用到了servlet和一个filter。第一次写博文,如有不足的地方,敬请指正,谢谢。登陆界面<%
- 前言在开发过程中经常会遇到比较排序的问题,比如说对集合数组的排序等情况,基本类型都提供了默认的比较算法,如string提供了按字母进行排序,
- 有时候可能需要将手机上的一些操作投影出来,比如一些App Demo的展示等。其实,有专门的硬件设备能干这件事儿,但没必要专门为展示个Demo
- 1 常量定义在程序中存在大量的数据来代表程序的状态,其中有些数据在程序运行过程中值不能发生改变,这些数据在程序中被叫做常量。2 常量语法命名
- 假设有两个线程在并发运行,一个线程执行的代码中含有一个死循环如:while(true)....当该线程在执行while(true)中代码时,
- 本文实例讲述了C#数据结构之单链表(LinkList)实现方法。分享给大家供大家参考,具体如下:这里我们来看下“单链表(LinkList)”
- 本文实例讲述了Java单例模式。分享给大家供大家参考,具体如下:在实际开发的时候会有一些需求,在某个类中只能允许同时存在一个对象。这时就需要
- 使用Task类解决线程的等待问题在任何的编程语言中,面对耗时任务时,我们都会有这样的需求:让任务执行一定时间,主任务进行等待,如果到时仍然完
- 前言TensorFlow是Google开源的一款人工智能学习系统。为什么叫这个名字呢?Tensor的意思是张量,代表N维数组;Flow的意思
- 大家好,今天尝试用swing技术写一个贪吃蛇大作战小游戏,供大家参考。 效果展示效果展示一、游戏界面二、得分情况&nb
- 前言在web开发过程中涉及到表格时,例如dataTable,就会产生分页的需求,通常我们将分页方式分为两种:前端分页和后端分页。前端分页一次
- 前言:从MVC到WebApi,路由机制一直是伴随着这些技术的一个重要组成部分。它可以很简单:如果你仅仅只需要会用一些简单的路由,如/Home
- 解决C#中WebBrowser的DocumentCompleted事件不执行的实现方法 :使用WebBrowser的ProgressChan
- 使用redis scan方法无法获取connection,导致线程锁死。0、关键字redisspringbootredistemplates
- 支付宝上有一个咻一咻的功能,就是点击图片后四周有水波纹的这种效果,今天也写一个类似的功能。效果如下所示:思路:就是几个圆的半径不断在变大,这
- C# 的类型转换有显式转型 和 隐式转型 两种方式。显式转型:有可能引发异常、精确度丢失及其他问题的转换方式。需要使用手段进行转换操作。隐式