Dubbo的服务发布逻辑是比较复杂的,我还是以Dubbo自带的示例讲解,这样更方便和容易理解。

Provider配置如下:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xmlns:dubbo="http://dubbo.apache.org/schema/dubbo"xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.3.xsd http://dubbo.apache.org/schema/dubbo http://dubbo.apache.org/schema/dubbo/dubbo.xsd"><!-- 提供方应用信息,用于计算依赖关系 --><dubbo:application name="hello-world-app"  /><!-- 使用multicast广播注册中心暴露服务地址 --><dubbo:registry address="multicast://224.5.6.7:1234" /><!-- 用dubbo协议在20880端口暴露服务 --><dubbo:protocol name="dubbo" port="20880" /><!-- 声明需要暴露的服务接口 --><dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService" /><!-- 和本地bean一样实现服务 --><bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl" />
</beans>

ApplicationContext

ClassPathXmlApplicationContext父类AbstractApplicationContext的方法refresh()在实例化bean之后的最后一步finishRefresh()中,此方法作用是发布相应的事件。

    protected void finishRefresh() {//省略LifeCycleProcessor刷新代码// Publish the final event.publishEvent(new ContextRefreshedEvent(this));// 省略注册到 LiveBeansView MBean代码}

可以看到发布了一个ContextRefreshedEvent事件。

    protected void publishEvent(Object event, ResolvableType eventType) {//省略部分代码getApplicationEventMulticaster().multicastEvent(applicationEvent, eventType);//省略部分代码

首先获取ApplicationEvent事件广播对象,然后广播事件。

ApplicationEvent事件广播对象默认是SimpleApplicationEventMulticaster,这个对象是在AbstractApplicationContext的方法initApplicationEventMulticaster()初始化的,如果需要自定义,可以实现接口ApplicationEventMulticaster,并将bean的名字命名为applicationEventMulticaster

接下来看看SimpleApplicationEventMulticaster类的multicastEvent方法。

    @Overridepublic void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {//事件类型ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));//applicationListenerfor (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {//异常执行Executor executor = getTaskExecutor();if (executor != null) {executor.execute(new Runnable() {@Overridepublic void run() {invokeListener(listener, event);}});}else {invokeListener(listener, event);}}}

可以看到此方法会调用applicationListener的方法,对于Dubbo而言,就是ServiceBean.

怎么样获取到ServiceBean的呢?

ServiceBean实现了好几个接口,其中有两个接口ApplicationContextAwareApplicationListener,其中ApplicationContextAware使ServiceBean具有获取ApplicationContext的能力(了解bean的生命周期),而ApplicationListener使ServiceBean具有响应事件响应的能力。dubbo实现ApplicationContextAware的目的是通过反射把自己添加到ApplicationContext的ApplicationListener列表中,即使不实现ApplicationContextAware接口,spring也会将实现了ApplicationListener接口的bean添加到其listener列表中的,dubbo这样做估计是向后兼容。

接着看invokeListener(listener, event);方法

protected void invokeListener(ApplicationListener<?> listener, ApplicationEvent event) {ErrorHandler errorHandler = getErrorHandler();if (errorHandler != null) {try {doInvokeListener(listener, event);}catch (Throwable err) {errorHandler.handleError(err);}}else {doInvokeListener(listener, event);}}private void doInvokeListener(ApplicationListener listener, ApplicationEvent event) {try {listener.onApplicationEvent(event);}catch (ClassCastException ex) {//省略异常处理}}

invokeListener方法内部调用了doInvokeListener方法,而doInvokeListener方法调用了listener(ServiceBean)的onApplicationEvent方法.

ServiceBean

    public void onApplicationEvent(ContextRefreshedEvent event) {if (isDelay() && !isExported() && !isUnexported()) {if (logger.isInfoEnabled()) {logger.info("The service ready on spring started. service: " + getInterface());}export();}}

onApplicationEvent方法调用了export方法,export方法首先判断是否已经发布了服务,发布了则直接返回,没有发布则会判断是否需要延迟发布,如果需要延迟,则将发布服务做为一个任务添加到ScheduledThreadPoolExecutor线程池中,如果不延迟,则调用doExport方法立即发布服务。
doExport方法中会获取application/registries/monitor/module/protocols,并做一些检查和属性填充,然后调用doExportUrls();发布服务。doExportUrls()首先调用loadRegistries方法得到要注册的url,然后发布相关Protocol的服务。

简单叙述一下获取url的过程,url通过map组装参数和对应的值,参数有ApplicationConfigRegistryConfig对象的属性以及pathdubbotimestamppidprotocolregistry

本示例applicationConfig是:

registryURL
registryConfig是:

最终map组装结果是:

最后得到registryURL是:

registry://224.5.6.7:1234/org.apache.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.2&pid=4892&qos.port=22222&registry=multicast&timestamp=1536112339884

然后调用doExportUrlsFor1Protocol方法发布服务,此方法开始部分是构造发布的服务URL,然后再发布url。

服务URL
URL包括以下几部分:服务端还是客户端标识Dubbo版本,时间戳,Pid,服务的方法名tokenApplicationConfig,MoudleConfig,ProviderConfig,ProtocolConfig,*MethodConfig对象的相关属性等。
例如本示例的url:

dubbo://192.168.124.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.124.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=8004&qos.port=22222&side=provider&timestamp=1536114090787

我们来着重看一下在构造URL过程中port的获取过程。

//protocolConfig是配置的<dubbo:protocol />生成的对象
//name是protocol的name,本示例为"dubbo"
//map保存了url的键值对
Integer port = this.findConfigedPorts(protocolConfig, name, map);

findConfigedPorts顾名思义是查找配置的port,从哪查呢,先从系统环境变量查,如果没找到,再查找名字为name的protocol协义。

    private Integer findConfigedPorts(ProtocolConfig protocolConfig, String name, Map<String, String> map) {Integer portToBind = null;// 从环境变量从查找绑定的portString port = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_BIND);portToBind = parsePort(port);// 如果没有从环境变量从查到,则从名称为name的protocol查找if (portToBind == null) {portToBind = protocolConfig.getPort();if (provider != null && (portToBind == null || portToBind == 0)) {portToBind = provider.getPort();}//这一句是关键,示例中name值是"dubbo",所以会实例化DubboProtocol,得到默认的port:20880final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();if (portToBind == null || portToBind == 0) {portToBind = defaultPort;}if (portToBind == null || portToBind <= 0) {portToBind = getRandomPort(name);if (portToBind == null || portToBind < 0) {portToBind = getAvailablePort(defaultPort);putRandomPort(name, portToBind);}logger.warn("Use random available port(" + portToBind + ") for protocol " + name);}}//保存port到map中,以便后面url使用map.put(Constants.BIND_PORT_KEY, String.valueOf(portToBind));// 从环境变量中查找注册的port,如果没有找到,则等于绑定的Port.String portToRegistryStr = getValueFromConfig(protocolConfig, Constants.DUBBO_PORT_TO_REGISTRY);Integer portToRegistry = parsePort(portToRegistryStr);if (portToRegistry == null) {portToRegistry = portToBind;}return portToRegistry;}

有人或许有疑问,ServiceConfig在实例化时,不是已经加载过Protocol了吗?为什么还要使用ExtensionLoader加载一次呢?

final int defaultPort =ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort();

答: ServiceConfig实例化时,加载的Protocol是自适应的Protocol,是动态生成的,类名是Protocol$Adaptive(见Dubbo源码分析-SPI的应用中有分析)。而这里获取Port时加载的也是Protocol类,但指名了具体加载的是哪个Protocol(本示例是名称为dubbo的Protocol,即DubboProtocol,此类默认的端口是20880)。

发布URL

发布本地服务

调用ServiceConfig类的exportLocal(URL url)发布本地服务。

    private void exportLocal(URL url) {if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {//本地服务urlURL local = URL.valueOf(url.toFullString()).setProtocol(Constants.LOCAL_PROTOCOL).setHost(LOCALHOST).setPort(0);ServiceClassHolder.getInstance().pushServiceClass(getServiceClass(ref));Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));exporters.add(exporter);logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry");}}

本示例的本地服务 url是:

injvm://127.0.0.1/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.124.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=3008&qos.port=22222&side=provider&timestamp=1536125473655

重点看这一句:

Exporter<?> exporter = protocol.export(proxyFactory.getInvoker(ref, (Class) interfaceClass, local));

其中涉及到ProxyFactory和Protocol,下面分别来看一看。

ProxyFactory

proxyFactory也是通过SPI加载的自适应类对象,类名为ProxyFactory$Adaptive,我们来看一下其class文件反编译后的源码。

package org.apache.dubbo.rpc;import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;public class ProxyFactory$Adaptive implements ProxyFactory {public ProxyFactory$Adaptive() {}public Invoker getInvoker(Object var1, Class var2, URL var3) throws RpcException {if (var3 == null) {throw new IllegalArgumentException("url == null");} else {String var5 = var3.getParameter("proxy", "javassist");if (var5 == null) {throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + var3.toString() + ") use keys([proxy])");} else {ProxyFactory var6 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(var5);return var6.getInvoker(var1, var2, var3);}}}public Object getProxy(Invoker var1, boolean var2) throws RpcException {if (var1 == null) {throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");} else if (var1.getUrl() == null) {throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");} else {URL var3 = var1.getUrl();String var4 = var3.getParameter("proxy", "javassist");if (var4 == null) {throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + var3.toString() + ") use keys([proxy])");} else {ProxyFactory var5 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(var4);return var5.getProxy(var1, var2);}}}public Object getProxy(Invoker var1) throws RpcException {if (var1 == null) {throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument == null");} else if (var1.getUrl() == null) {throw new IllegalArgumentException("org.apache.dubbo.rpc.Invoker argument getUrl() == null");} else {URL var2 = var1.getUrl();String var3 = var2.getParameter("proxy", "javassist");if (var3 == null) {throw new IllegalStateException("Fail to get extension(org.apache.dubbo.rpc.ProxyFactory) name from url(" + var2.toString() + ") use keys([proxy])");} else {ProxyFactory var4 = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(var3);return var4.getProxy(var1);}}}
}

其中有三个方法,两个获取代理,一个获取Invoker。我们来看其中的getInvoker方法,默认获取名称为javassist的ProxyFactory。
由于本地服务URL中没有proxy参数,所以会调用JavassistProxyFactory的getInvoker(T proxy, Class type, URL url)方法,返回AbstractProxyInvoker的匿名类对象,此对象代理了服务对象(本示例中为DemoServiceImpl对象)。

其实(ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension("javassist");获取到的并不是JavassistProxyFactory对象,而是StubProxyFactoryWrapper对象,为什么呢?我们可以看下ExtensionLoader的getExtension(String name)方法

   public T getExtension(String name) {//检查name是否合法if (name == null || name.length() == 0)throw new IllegalArgumentException("Extension name == null");//如果name等于true,则加载SPI的默认插件if ("true".equals(name)) {return getDefaultExtension();}//从当前插件类的缓存实例对象中获取Holder<Object> holder = cachedInstances.get(name);if (holder == null) {cachedInstances.putIfAbsent(name, new Holder<Object>());holder = cachedInstances.get(name);}Object instance = holder.get();if (instance == null) {synchronized (holder) {instance = holder.get();if (instance == null) {//创建插件实例instance = createExtension(name);holder.set(instance);}}}return (T) instance;}private T createExtension(String name) {//从文件目录中加载插件类Class<?> clazz = getExtensionClasses().get(name);if (clazz == null) {throw findException(name);}//从已加载的所有插件实例集合中获取try {T instance = (T) EXTENSION_INSTANCES.get(clazz);if (instance == null) {//实例化插件实例,并放入集合EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());instance = (T) EXTENSION_INSTANCES.get(clazz);}//注入属性injectExtension(instance);//插件的包裹类Set<Class<?>> wrapperClasses = cachedWrapperClasses;if (wrapperClasses != null && !wrapperClasses.isEmpty()) {for (Class<?> wrapperClass : wrapperClasses) {instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));}}return instance;} catch (Throwable t) {throw new IllegalStateException("Extension instance(name: " + name + ", class: " +type + ")  could not be instantiated: " + t.getMessage(), t);}}

重点的地方就在于插件的包裹类,StubProxyFactoryWrapper就是JavassistProxyFactory的包裹类,为什么这么说呢,因为StubProxyFactoryWrapper有一个带ProxyFactory参数的构造函数而且实现了ProxyFactory接口,具体可以看Extension的loadExtensionClasses方法源码(装饰者模式)。

Protocol

protocol对象也是一个自适应插件类,类名为Protocol$Adaptive,在上一篇文章中已有讲解。这个类会根据url的协义取得对应转义的插件类,没有的话,默认为dubbo协义,本地服务url协义为injvm,所以会加载InjvmProtocol,但是在加载InjvmProtocol并实例化后,发现InjvmProtocol还有对应的包裹类即(其实是所有Protocol的包裹类):ProtocolFilterWrapper和ProtocolListenerWrapper。ProtocolFilterWrapper类的作用是添加一些过滤器,ProtocolListenerWrapper的作用是添加ExporterListener。InjvmProtocol的export方法仅仅创建一个InjvmExporter实例,没有开启服务。

发布远程服务

如果注册url不为空,调用proxyFactory得到服务对象的代理类,然后使用protocol发布服务。由于注册url的协义是registry,在使用ExtensionLoader.getExtensionLoader(org.apache.dubbo.rpc.Protocol.class).getExtension("registry");会加载RegistryProtocol类并实例化,而且会添加其包裹类:ProtocolFilterWrapper和ProtocolListenerWrapper。而在这两个包裹类的export方法的首行,都会对registry协义进行单独处理。

RegistryProtocol
        if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {return protocol.export(invoker);}

经过这两个包裹类后,最终会调用RegistryProtocol的export方法。

    public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {//发布服务//originInvoker中包含了代理服务对象的代理类final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);//注册相关代码省略//订阅相关代码省略}private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker) {//key为发布的服务urlString key = getCacheKey(originInvoker);//从map缓存中获取ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);//double checkif (exporter == null) {synchronized (bounds) {exporter = (ExporterChangeableWrapper<T>) bounds.get(key);if (exporter == null) {final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);bounds.put(key, exporter);}}}return exporter;}

最重要的是这一句:

exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);

其中protocol也是Protocol$Adaptive对象,而invokerDelegete的URL是服务的url.

本示例中为:

dubbo://192.168.124.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.124.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=8468&qos.port=22222&side=provider&timestamp=1536138127517

DubboProtocol

Protocol$Adaptive在解析URL的时得到dubbo,所以会加载DubboProtocol并实例化(DubboProtocol实际在前面获取默认接口时已经实例化并缓存起来了,此处取的是缓存的实例),并调用了DubboProtocol的export方法(与上面一样,在得到DubboProtocol实例后,仍然会在外面包裹一下)。

    public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {URL url = invoker.getUrl();// 服务名:本例中为org.apache.dubbo.demo.DemoService:20880String key = serviceKey(url);//exporter 控制服务打开与关闭DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);exporterMap.put(key, exporter);//省略发布子服务的相关代码//打开服务openServer(url);//优化序列化处理optimizeSerialization(url);return exporter;}

经过层层探索,曲折迂回,终于到openServer了,进去看看。

    private void openServer(URL url) {// 服务ip:端口号String key = url.getAddress();boolean isServer = url.getParameter(Constants.IS_SERVER_KEY, true);if (isServer) {ExchangeServer server = serverMap.get(key);if (server == null) {synchronized (this) {server = serverMap.get(key);if (server == null) {serverMap.put(key, createServer(url));}}} else {// 服务支持重置server.reset(url);}}}

可以看到其中有一个重要方法createServer(url)。

    private ExchangeServer createServer(URL url) {// 当服务关闭时,默认启动发送只读事件url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());// 默认启动心跳url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));//str默认为nettyString str = url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_SERVER);if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str))throw new RpcException("Unsupported server type: " + str + ", url: " + url);//添加编解码器url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);ExchangeServer server;try {//启动服务,并传入请求处理器server = Exchangers.bind(url, requestHandler);} catch (RemotingException e) {throw new RpcException("Fail to start server(url: " + url + ") " + e.getMessage(), e);}//判断客户端使用的是网络传输层框架是否支持服务端的网络传输层。str = url.getParameter(Constants.CLIENT_KEY);if (str != null && str.length() > 0) {Set<String> supportedTypes = ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions();if (!supportedTypes.contains(str)) {throw new RpcException("Unsupported client type: " + str);}}return server;}
Exchangers

进入Exchangers.bind方法一探究竟。

    public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {if (url == null) {throw new IllegalArgumentException("url == null");}if (handler == null) {throw new IllegalArgumentException("handler == null");}//如果编码码器没有,则添加参数exchangeurl = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");return getExchanger(url).bind(url, handler);}

getExchanger(url)默认得到的是HeaderExchanger,可通过exchanger参数配置。
到HeaderExchanger中看看bind方法

    public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));}
Transporter

看下Transporters的bind方法。

    public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {if (url == null) {throw new IllegalArgumentException("url == null");}if (handlers == null || handlers.length == 0) {throw new IllegalArgumentException("handlers == null");}ChannelHandler handler;if (handlers.length == 1) {handler = handlers[0];} else {handler = new ChannelHandlerDispatcher(handlers);}return getTransporter().bind(url, handler);}

通过getTransporter方法获取一个自适应的Transporter,类名为Transporter$Adaptive,我们来看一下其源码:

package org.apache.dubbo.remoting;import org.apache.dubbo.common.URL;
import org.apache.dubbo.common.extension.ExtensionLoader;public class Transporter$Adaptive implements Transporter {public Transporter$Adaptive() {}public Client connect(URL var1, ChannelHandler var2) throws RemotingException {if (var1 == null) {throw new IllegalArgumentException("url == null");} else {String var4 = var1.getParameter("client", var1.getParameter("transporter", "netty"));if (var4 == null) {throw new IllegalStateException("Fail to get extension(org.apache.dubbo.remoting.Transporter) name from url(" + var1.toString() + ") use keys([client, transporter])");} else {Transporter var5 = (Transporter)ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(var4);return var5.connect(var1, var2);}}}public Server bind(URL var1, ChannelHandler var2) throws RemotingException {if (var1 == null) {throw new IllegalArgumentException("url == null");} else {String var4 = var1.getParameter("server", var1.getParameter("transporter", "netty"));if (var4 == null) {throw new IllegalStateException("Fail to get extension(org.apache.dubbo.remoting.Transporter) name from url(" + var1.toString() + ") use keys([server, transporter])");} else {Transporter var5 = (Transporter)ExtensionLoader.getExtensionLoader(Transporter.class).getExtension(var4);return var5.bind(var1, var2);}}}
}

可以看到Transporter$Adaptive通过判断URL中是否有transporter参数,如果没有,就默认为netty。

示例中服务的URL为

dubbo://192.168.124.1:20880/org.apache.dubbo.demo.DemoService?anyhost=true&application=demo-provider&bind.ip=192.168.124.1&bind.port=20880&dubbo=2.0.2&generic=false&interface=org.apache.dubbo.demo.DemoService&methods=sayHello&pid=8004&qos.port=22222&side=provider&timestamp=1536114090787

其中没有transporter参数,所以就使用netty。然后dubbo就去查找netty对应的是哪个Transporter,结果找到是NettyTransporter。

package org.apache.dubbo.remoting.transport.netty4;//省略导入部分public class NettyTransporter implements Transporter {public static final String NAME = "netty";@Overridepublic Server bind(URL url, ChannelHandler listener) throws RemotingException {return new NettyServer(url, listener);}@Overridepublic Client connect(URL url, ChannelHandler listener) throws RemotingException {return new NettyClient(url, listener);}}

NettyTransporter很简单,只有两个方法,一个用于开启服务,一个用于连接服务。到这里已经明白了Dubbo是如何发布一个服务的。

我们再进一步看下NettyServer的构造函数

    public NettyServer(URL url, ChannelHandler handler) throws RemotingException {super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));}

可以看出其调用父类的构造函数,并传入url和handler的包裹类。handler的包裹类有哪些呢,进去看一看。

    public static ChannelHandler wrap(ChannelHandler handler, URL url) {return ChannelHandlers.getInstance().wrapInternal(handler, url);}protected ChannelHandler wrapInternal(ChannelHandler handler, URL url) {return new MultiMessageHandler(new HeartbeatHandler(ExtensionLoader.getExtensionLoader(Dispatcher.class).getAdaptiveExtension().dispatch(handler, url)));}

注意到有一个接口Dispatcher,其自适应插件类是AllDispatcher,AllDispatcher的dispatch方法返回AllChannelHandler实例(此实例会将所有请求做为任务放入线程池中处理),在此实例基础上又包裹了HeartbeatHandlerMultiMessageHandler
NettyServer会将MultiMessageHandler层层往上传到其父类AbstractPeer

我们来回忆一下正向流程

从ServiceConfig发布registryURL开始(见doExportUrlsFor1Protocol方法)
1.ServiceConfig生成服务实例的代理工厂类JavassistProxyFactory(ProxyFactory SPI默认代理工厂类)并包裹到DelegateProviderMetaDataInvoker(此类记录代理工厂类和服务信息ServiceBean(标签对应的类))
2.由于registryURL的protocol协义是registry,所以会加载RegistryProtocol(Protocol类的外面都包裹了ProtocolFilterWrapper和ProtocolListenerWrapper,下面不再特殊说明),并传入上一步的invoker。
3.RegistryProtocol又找到DubboProtocol,也会带上Invoker(此时的Invoker包含上一次的Invoker并带有服务地址(dubbo://IP:端口/服务接口全称?参数=xxx))。

所以requestHandler又会调用正向传过来的Invoker,经过ProtocolFilterWrapper和ProtocolListenerWrapper,最终调用到服务实现类相应的方法。

最后以一张图总结:

标识为SPI的类,是可以动态加载的。图片看不清楚的话,请查看原图。

再简单说下接收到请求后的处理流程:NettyServer接收到请求后,交给NettyServerHandler处理,NettyServerHandler转交给NettyServer的父类AbstractPeer处理,AbstractPeer又交给MultiMessageHandler处理,这样就开始了handler链的处理,handler的终点是HeaderExchangerHandler,HeaderExchangerHandler调用DubboProtocol传过来的成员变量requestHandler调用相应的服务类方法,然后得到结果,调用NettyServerHandler传过来的NettyChannel发送结果到Client。

转载于:https://www.cnblogs.com/mycodingworld/p/dubbo_publishservice.html

Dubbo2.7源码分析-如何发布服务相关推荐

  1. Netty源码分析系列之服务端Channel的端口绑定

    扫描下方二维码或者微信搜索公众号菜鸟飞呀飞,即可关注微信公众号,Spring源码分析和Java并发编程文章. 微信公众号 问题 本文内容是接着前两篇文章写的,有兴趣的朋友可以先去阅读下两篇文章: Ne ...

  2. 云客Drupal源码分析之twig服务

    以下内容仅是一个预览,完整内容请见文尾: 全文目录(全文11509字): 服务定义及调用入口 对原生twig所做变动 为twig提供的drupal_core扩展 添加的模板函数 添加的过滤器 复数翻译 ...

  3. android6.0源码分析之AMS服务源码分析

    activitymanagerservice服务源码分析 1.ActivityManagerService概述 ActivityManagerService(以下简称AMS)作为Android中最核心 ...

  4. dubbo源码分析7 之 服务本地暴露

    在上一篇文章我们分析了一下 dubbo 在服务暴露发生了哪些事,今天我们就来分析一下整个服务暴露中的本地暴露.(PS:其实我感觉本地暴露蛮鸡肋的).本地暴露需要服务提供方与服务消费方在同一个 JVM. ...

  5. motan源码分析一:服务发布及注册

    motan是新浪微博开源的服务治理框架,具体介绍请看:http://tech.sina.com.cn/i/2016-05-10/doc-ifxryhhh1869879.shtml. 本系列的文章将分析 ...

  6. dubbo源码分析二:服务发布

    本文将深入分析dubbo的服务发布涉及到的流程及主要类的代码.首先,我们先从类的关系图来看一下dubbo发布服务涉及到的相关类. 1.类图 上图展示了部分服务发布过程中需要使用到的类和接口,其中: s ...

  7. Dubbo2.7源码分析-SPI的应用

    SPI简介 SPI是Service Provider Interface的缩写,即服务提供接口(翻译出来好绕口,还是不翻译的好),实质上是接口,作用是对外提供服务. SPI是Java的一种插件机制,可 ...

  8. redis 发布订阅实际案例_Redis源码分析之发布订阅+慢查询+排序以及监视器

    发布订阅 发布订阅就是一个经典的观察者模式,其中通道是指channel字符串本身,而模式是指正则表达式,进行匹配.结合Redis设计与实现一书 数据结构 基本数据结构 在client对象中,分别记录了 ...

  9. 应用挂载beegfs指定目录_BeeGFS源码分析1-元数据服务概要分析

    元数据服务是BeeGFS中用来维护文件和目录关系及其属性配置的服务,其多线程epoll设计实现非常高效,主要流程如下: ConnAcceptor(PThread)类(一个线程)负责监听端口,并接受客户 ...

  10. Dubbo源码分析之QoS服务(一)

    目录 一.概述 二.QoS配置 三.连接 1.telnet连接 2.http连接 四.命令 1.help帮助菜单 2.ls列出所有服务 3.online上线服务 4.offline下线服务 5.qui ...

最新文章

  1. git的author和commiter的修改
  2. TESTb需要向至少十几家应用商店提交上线审核,且每个应用商店要求的资料可能都不一样,异常繁琐
  3. Linux下没有包含头文件(不知是哪个)导致编译无法通过的解决心得
  4. c语言中x的n次方怎么表示_线性代数的本质及其在人工智能中的应用
  5. mysql sillyr x.so_mysql2.so:libmysqlclient_r.so.15:无法打开共享对象文件:没有这样的文件或目录...
  6. maven+mvc设计模式
  7. 04-02 接口协议分析工具
  8. GetModuleFileNameA函数与GetCurrentDirectoryA函数
  9. 机械臂动力学建模(2)- Kane凯恩算法
  10. 视频去水印工具-视频去水印哪个软件好用免费
  11. SpringCloud使用Feign调用第三方接口
  12. 必须知道的python专属骚技巧25例
  13. 如何判断一个点在三角形内部
  14. 【Matlab】mod函数和rem函数的区别
  15. 网页HTML5制作flex布局骰子,css利用flex布局画骰子的六个面
  16. 举例说明专用计算机和通用计算机的区别,计算机原理答案
  17. 7-4 使用递归输出杨辉三角形 (18 分)输出杨辉三角形,用户输入一个正整数n,输出n行的直角三角形的杨辉三角。要求使用递归函数实现
  18. 万事无忧之SEO GOOGLE优化秘诀
  19. python第一弹 爬虫淘女郎图片
  20. “制服大姨妈”对症调理

热门文章

  1. poj 1695 动态规划
  2. 【OpenCV学习笔记】【错误处理】计算机缺失MSVCP120D.dll
  3. Node安装模块命令
  4. Java设计模式笔记--------工厂模式------抽象工厂模式
  5. Java并发编程实战读书笔记之死锁
  6. Java并发编程实战读书笔记5 ---Executor在android中的应用
  7. arduino 可以用c_lt;开源项目gt;Arduino+STM32+SIM868 用一天创造一款可以定位的GPRS无线开关...
  8. 30万奖金等你拿!Apache Flink 极客挑战赛入门指南(附Demo)
  9. 我给曾经暗恋的高中女同学,用Python实现了她飞机上刷抖音
  10. path和classpath的区别