Dubbo扩展点SPI实践
UniJoy 人气:0正文
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 { // ... }
加载全部内容