总体流程以调试 om.alibaba.dubbo.demo.provider.DemoProvider来演示dubbo服务的发布流程。java
参照dubbo容器的启动, https://segmentfault.com/a/11... 文章描述spring
dubbo的xml自定义标签,都是基于spring提供NamespaceHandlerSupport机制来完成的。
ServiceBean加载和初始化segmentfault
<!-- 和本地bean同样实现服务 --> <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>
public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } }
xml文件->ServiceBean.class 这就是spring的NamespaceHandlerSupport作的事情。服务器
spring容器在初始化以后,会广播ContextRefreshedEvent事件,ServiceBean实现了ApplicationListener接口,在执行onApplicationEvent时,启动了export方法,开启 服务发布流程。网络
public void onApplicationEvent(ApplicationEvent event) { if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) { if (isDelay() && !isExported() && !isUnexported()) { if (logger.isInfoEnabled()) { logger.info("The service ready on spring started. service: " + getInterface()); } export(); } } }
--export()
-----doExport()
-------doExportUrls()
---------loadRegistries()加载注册中心,能够有多个
---------doExportUrlsFor1Protocol()
-----------exportLocal(url) 本地暴露服务
-----------protocol.export(invoker) 远程暴露服务app
假如服务没有配置了scope属性,或者配置了可是值不是”remote“,就会执行本地暴露。自同一个jvm内部,调用本jvm中存在的服务,就能够直接调用,而不须要走网络,减小响应时间。异步
private void exportLocal(URL url) { if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(NetUtils.LOCALHOST) .setPort(0); Exporter<?> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local) ); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry"); } }
第一步:设置URL中的参数,jvm
url的协议已经从dubbo变成了injvm
第二步:将ref【interfaceClass的实现类】包装成一个Wrapper,并返回一个Invoker
JavassistProxyFactory#getInvokeride
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) { // TODO Wrapper类不能正确处理带$的类名 final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type); return new AbstractProxyInvoker<T>(proxy, type, url) { @Override protected Object doInvoke(T proxy, String methodName, Class<?>[] parameterTypes, Object[] arguments) throws Throwable { return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); } }; }
第三步:根据injvm协议找到【这个是经过dubbo的Adaptive标签来动态决定的】InJvmProtocol,并执行他的export方法,装载Invoker。
第四步:将export放到该服务的exporters集合中。
本地暴露,不须要启动相似netty的服务器,也不须要注册zookeeper。函数
假如服务没有配置了scope属性,或者配置了可是值不是”local“,就会执行远程暴露。
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) { if (logger.isInfoEnabled()) { logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); } if (registryURLs != null && registryURLs.size() > 0 && url.getParameter("register", true)) { for (URL registryURL : registryURLs) { url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic")); URL monitorUrl = loadMonitor(registryURL); if (monitorUrl != null) { url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString()); } if (logger.isInfoEnabled()) { logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL); } Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); Exporter<?> exporter = protocol.export(invoker); exporters.add(exporter); } } else { Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); Exporter<?> exporter = protocol.export(invoker); exporters.add(exporter); }
下面,咱们来一步一步调试远程暴露都作了什么事情。
protocol是一个Protocol$Adaptie类,是dubbo动态生成的对象。
如何得到动态生成的类的源码:将日志级别调测DEBUG,在控制台中拷贝出打印的源码,而后new相同的package、相同的java文件,就能够进去调试了。
看一下经过前面一些列操做以后,组装的Invoker对象
当前协议值为
咱们再看一下export的值为
由于此时Invoker的协议为registry,因此会执行
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol { public void destroy() { throw new UnsupportedOperationException("method public abstract void com.alibaba.dubbo.rpc.Protocol.destroy() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public int getDefaultPort() { throw new UnsupportedOperationException("method public abstract int com.alibaba.dubbo.rpc.Protocol.getDefaultPort() of interface com.alibaba.dubbo.rpc.Protocol is not adaptive method!"); } public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { if (arg0 == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument == null"); if (arg0.getUrl() == null) throw new IllegalArgumentException("com.alibaba.dubbo.rpc.Invoker argument getUrl() == null"); com.alibaba.dubbo.common.URL url = arg0.getUrl(); String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.export(arg0); } public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { if (arg1 == null) throw new IllegalArgumentException("url == null"); com.alibaba.dubbo.common.URL url = arg1; String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol()); if (extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])"); com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName); return extension.refer(arg0, arg1); } }
从拿到的Extension的值,咱们就能验证,dubbo的AOP原理。请戳连接https://segmentfault.com/a/11... 这个里面讲了为何咱们拿的是 RegistryProtocol,可是实际上确的拿到的是通过两个wrapper包装的对象的缘由了?!
通过两次ProtocolListenerWrapper、ProtocolFilterWrapper的export方法以后,来到R
RegistryProtocol的export方法,这个是一个很是核心的方法。]
doLocalExport方法里面,将从Invoker里面的拿到了export的值,从新构造了一个InvokerDelegete对象,这个时候又会执行Protocol$Adpative#export方法,这个时候Invoker的Url属性的协议已是dubbo了。因此会拿到DubboProtocal,并执行export
DubboProtocol#export方法,发现有expoterMap属性的key是 com.alibaba.dubbo.demo.DemoService:20880 服务名:协议端口号。
从上图中,看到了openServer的方法了 <~.~> 。看一下入参URL以下,该值与上面一直图中的export的属性值如出一辙。
【忽略ip、pid、timestamp字段,由于不是同一次调试的。】
属性serverMap存储了相应的server,ip:prot为key。所以,假如咱们只配置一个协议端口,createServer只会执行一次。
执行Exchangers.bind(url, requestHandler) 就是为拿到 一个 ExchangeServer
]
在Exchangers里面, 执行getExchanger(url)拿到 HeaderExchanger执行bind方法
在HeaderExchanger里面,执行Transporters#bind也是为了拿到一个
在Transporters#bind里面,执行getTransporter()拿到默认的Transporter 即NettyTransporter。
在NettyTransporter启动一个NettyServer
netty服务器在父类的构造函数中被调用。
至此一个netty服务器就启动起来了。
大体完成的事情就是下图中红框框框起来的部分。
实现了全部服务接口的的透明化代理。有两个方法,
getInvoker 服务端使用,将实现类封装成一个Invoker。
getProxy 客户端使用,建立接口的代理对象。
封装了一个服务的相关信息,是一个服务可执行体。
是会话域,它持有调用过程当中的变量,好比方法名,参数等。
Protocol是一个服务域,他是Invoker引用和暴露的主要入口,它负责Invoker的生命周期管理。
具体执行Invoker的生命周期
封装请求响应模式,同步转异步
transport 网络传输层,抽象mina、netty的统一接口..]