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方法。
@Override
public void multicastEvent(final ApplicationEvent event, ResolvableType eventType) {
//事件類型
ResolvableType type = (eventType != null ? eventType : resolveDefaultEventType(event));
//applicationListener
for (final ApplicationListener<?> listener : getApplicationListeners(event, type)) {
//異常執行
Executor executor = getTaskExecutor();
if (executor != null) {
executor.execute(new Runnable() {
@Override
public void run() {
invokeListener(listener, event);
}
});
}
else {
invokeListener(listener, event);
}
}
}
可以看到此方法會調用applicationListener的方法,對于Dubbo而言,就是ServiceBean.
怎么樣獲取到ServiceBean的呢?
ServiceBean實現了好幾個接口,其中有兩個接口ApplicationContextAware和ApplicationListener<ContextRefreshedEvent>,其中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組裝參數和對應的值,參數有ApplicationConfig和RegistryConfig對象的屬性以及path、dubbo、timestamp、pid、protocol、registry。
本示例applicationConfig是:
<dubbo:application name="demo-provider" qosPort="22222" id="demo-provider" />
registryURL
registryConfig是:
最終map組裝結果是:<dubbo:registry address="multicast://224.5.6.7:1234" id="org.apache.dubbo.config.RegistryConfig" />
最后得到registryURL是:
然后調用doExportUrlsFor1Protocol方法發布服務,此方法開始部分是構造發布的服務URL,然后再發布url。
服務URL
URL包括以下幾部分:服務端還是客戶端標識,Dubbo版本,時間戳,Pid,服務的方法名,token、ApplicationConfig,MoudleConfig,ProviderConfig,ProtocolConfig,*MethodConfig對象的相關屬性等。
例如本示例的url:
我們來著重看一下在構造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;
// 從環境變量從查找綁定的port
String 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:20880
final 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())) {
//本地服務url
URL 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是:
重點看這一句:
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<T> 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為發布的服務url
String key = getCacheKey(originInvoker);
//從map緩存中獲取
ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
//double check
if (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.
本示例中為:
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:20880
String 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默認為netty
String 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");
}
//如果編碼碼器沒有,則添加參數exchange
url = 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為
其中沒有transporter參數,所以就使用netty。然后dubbo就去查找netty對應的是哪個Transporter,結果找到是NettyTransporter。
package org.apache.dubbo.remoting.transport.netty4;
//省略導入部分
public class NettyTransporter implements Transporter {
public static final String NAME = "netty";
@Override
public Server bind(URL url, ChannelHandler listener) throws RemotingException {
return new NettyServer(url, listener);
}
@Override
public 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實例(此實例會將所有請求做為任務放入線程池中處理),在此實例基礎上又包裹了HeartbeatHandler和MultiMessageHandler。
NettyServer會將MultiMessageHandler層層往上傳到其父類AbstractPeer。
我們來回憶一下正向流程:
從ServiceConfig發布registryURL開始(見doExportUrlsFor1Protocol方法)
1.ServiceConfig生成服務實例的代理工廠類JavassistProxyFactory(ProxyFactory SPI默認代理工廠類)并包裹到DelegateProviderMetaDataInvoker(此類記錄代理工廠類和服務信息ServiceBean(<dubbo:service />標簽對應的類))
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。
用力不如用心!用心寫好每一篇文章!