dubbo源码阅读-服务暴露(七)之本地暴露(Injvm)
什么是本地暴露
具体可以参考:https://zhuanlan.zhihu.com/p/98423741
我们从上一篇的服务暴露本地暴露开始看起
https://www.cnblogs.com/LQBlog/p/12469007.html#autoid-6-13-0
com.alibaba.dubbo.config.ServiceConfig#exportLocal
@SuppressWarnings({"unchecked", "rawtypes"}) private void exportLocal(URL url) { //如果协议为不是injvm if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { //生成本地Url URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL)//injvm 注意这里把协议强制设置成了injvm .setHost(LOCALHOST) //127.0.0.1 .setPort(0); //service.classimpl 将本地暴露接口存入StaticContext map key为接口 value为实现类的名字 StaticContext.getContext(Constants.SERVICE_IMPL_CLASS).put(url.getServiceKey(), getServiceClass(ref)); /** * <1>使用 ProxyFactory 创建 Invoker 对象 根据url配置的proxy来作为spi的Key 缺省值是javasist 动态生成一个代理class 也可以使用jdk * private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension(); * 使用 Protocol 暴露 Invoker 对象 根据url的protcol 来作为SPIKey查找 缺省值duboo * * private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension(); */ Exporter<?> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local)); // 添加到 `exporters` exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry"); } }
类图
为什么会调用ProtocolListenerWrapper
按照https://www.cnblogs.com/LQBlog/p/12453900.html 理解 那么这里的protocol应该是InjvmProtocol
我们回到:https://www.cnblogs.com/LQBlog/p/12453900.html#autoid-8-11-0 注释处://当前类是否含有构造函数 并且为当前SPI接口 装饰者 具体可以参考
再回到:https://www.cnblogs.com/LQBlog/p/12453900.html#autoid-11-1-0 可可以发现动态代理都是调用的getExtention(name)方法
再看:https://www.cnblogs.com/LQBlog/p/12453900.html#autoid-4-3-0 18->19
//逐级生成代理对象返回 if (wrapperClasses != null && !wrapperClasses.isEmpty()) { for (Class<?> wrapperClass : wrapperClasses) { instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance)); } }
可以发现这里会对SPI配置的含有构造函数参数为SPI接口类型的 生成逐级代理
ProtocolFilterWrapper,QosProtocolWrapper,ProtocolListenerWrapper 这几个接口都是SPI代理接口 所以返回的这3个对象的代理对象
ProtocolFilterWrapper
<1>export
com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper#export
@Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { //registry 如果是registry 直接调用 代理抵消的export传入invoker invoke封装了代理ServiceImpl信息 以及registryUrl?export=暴露url信息 if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { return protocol.export(invoker); } // <2>调用buildInvokerChain 传入service.filter provider,<3>调用被代理对象的export //buildInvokerChain 后就是返回的代理对象的Invoker return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER)); }
<2>buildInvokerChain
com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper#export
->
com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper#buildInvokerChain
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) { Invoker<T> last = invoker; //获取激活对象 参数如:key service.filter gruop=provider //getActivateExtension内部实现可以看:https://www.cnblogs.com/LQBlog/p/12453900.html#autoid-6-0-0 List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group); if (!filters.isEmpty()) { //循环生成invoker调用链条 for (int i = filters.size() - 1; i >= 0; i--) { //逐级dialing final Filter filter = filters.get(i); final Invoker<T> next = last; last = new Invoker<T>() { @Override public Class<T> getInterface() { return invoker.getInterface(); } @Override public URL getUrl() { //调用被代理对象的url return invoker.getUrl(); } @Override public boolean isAvailable() { return invoker.isAvailable(); } @Override public Result invoke(Invocation invocation) throws RpcException { //代理 return filter.invoke(next, invocation); } @Override public void destroy() { invoker.destroy(); } @Override public String toString() { return invoker.toString(); } }; } } return last; }
QosProtocolWrapper
<3>export
QOS使用:https://www.jianshu.com/p/e117b580b996,https://segmentfault.com/a/1190000041003358
@Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { //如果协议是registry 则开启qos if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { //开启qos模块 具体可看http://dubbo.apache.org/zh-cn/docs/user/references/qos.html startQosServer(invoker.getUrl()); return protocol.export(invoker); } //<4>ProtocolListenerWrapper return protocol.export(invoker); }
ProtocolListenerWrapper
<4>export
/** * 用于给 Exporter 增加 ExporterListener ,监听 Exporter 暴露完成和取消暴露完成。 * @param invoker Service invoker * @param <T> * @return * @throws RpcException */ @Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) { return protocol.export(invoker); } //<5>InjvmProtocol Exporter<T> exporter=protocol.export(invoker); // SPI扩展点 返回监听器 Collections.unmodifiableList 返回的集合不可修改 List<ExporterListener> exporterListenerList=Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class) .getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)); //https://www.cnblogs.com/killbug/p/7341968.html 返回一个带监听功能的代理类 //代理类 在unexport和exported 会调用监听器的unexported,exported方法和 并传入invoker return new ListenerExporterWrapper<T>(exporter, exporterListenerList); }
InjvmProtocol
<5>export
com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol#export
@Override public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException { //创建一个InjvmExporter代理对象 内部保存了invoker以及所有的本地发布实例 key为 service com.alibaba.dubbo.demo.DemoService //<6>exporterMap为InJvmProtocol成员变量 构造函数内部会add key为Service全名称 return new InjvmExporter<T>(invoker, invoker.getUrl().getServiceKey(), exporterMap); }
InjvmExporter
<6>exporter构造函数
InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap) { super(invoker); //key为service全名称 this.key = key; //为协议成员变量的map this.exporterMap = exporterMap; //将当前export添加到map exporterMap.put(key, this); }