dubbo暴露服務(wù)有兩種情況,一種是設(shè)置了延遲暴露(比如delay="5000"),另外一種是沒有設(shè)置延遲暴露或者延遲設(shè)置為-1(delay="-1"):
- 設(shè)置了延遲暴露,dubbo在Spring實(shí)例化bean(initializeBean)的時(shí)候會對實(shí)現(xiàn)了InitializingBean的類進(jìn)行回調(diào),回調(diào)方法是afterPropertySet(),如果設(shè)置了延遲暴露,dubbo在這個(gè)方法中進(jìn)行服務(wù)的發(fā)布。
- 沒有設(shè)置延遲或者延遲為-1,dubbo會在Spring實(shí)例化完bean之后,在刷新容器最后一步發(fā)布ContextRefreshEvent事件的時(shí)候,通知實(shí)現(xiàn)了ApplicationListener的類進(jìn)行回調(diào)onApplicationEvent,dubbo會在這個(gè)方法中發(fā)布服務(wù)。
但是不管延遲與否,都是使用ServiceConfig的export()方法進(jìn)行服務(wù)的暴露。使用export初始化的時(shí)候會將Bean對象轉(zhuǎn)換成URL格式,所有Bean屬性轉(zhuǎn)換成URL的參數(shù)。
以沒有設(shè)置延遲暴露熟屬性的過程為例。
簡易的暴露流程
- 首先將服務(wù)的實(shí)現(xiàn)封裝成一個(gè)Invoker,Invoker中封裝了服務(wù)的實(shí)現(xiàn)類。
- 將Invoker封裝成Exporter,并緩存起來,緩存里使用Invoker的url作為key。
- 服務(wù)端Server啟動(dòng),監(jiān)聽端口。(請求來到時(shí),根據(jù)請求信息生成key,到緩存查找Exporter,就找到了Invoker,就可以完成調(diào)用。)
Spring容器初始化調(diào)用
當(dāng)Spring容器實(shí)例化bean完成,走到最后一步發(fā)布ContextRefreshEvent事件的時(shí)候,ServiceBean會執(zhí)行onApplicationEvent方法,該方法調(diào)用ServiceConfig的export方法。
ServiceConfig初始化的時(shí)候,會先初始化靜態(tài)變量protocol和proxyFactory,這兩個(gè)變量初始化的結(jié)果是通過dubbo的spi擴(kuò)展機(jī)制得到的。
生成的protocol實(shí)例是:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol {
public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws java.lang.Class {
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);
}
public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
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();
//根據(jù)URL配置信息獲取Protocol協(xié)議,默認(rèn)是dubbo
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])");
//根據(jù)協(xié)議名,獲取Protocol的實(shí)現(xiàn)
//獲得Protocol的實(shí)現(xiàn)過程中,會對Protocol先進(jìn)行依賴注入,然后進(jìn)行Wrapper包裝,最后返回被修改過的Protocol
//包裝經(jīng)過了ProtocolFilterWrapper,ProtocolListenerWrapper,RegistryProtocol
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 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!");
}
}
生成的proxyFactory實(shí)例:
package com.alibaba.dubbo.rpc;
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class ProxyFactory$Adpative implements com.alibaba.dubbo.rpc.ProxyFactory {
public com.alibaba.dubbo.rpc.Invoker getInvoker(java.lang.Object arg0, java.lang.Class arg1, com.alibaba.dubbo.common.URL arg2) throws java.lang.Object {
if (arg2 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg2;
String extName = url.getParameter("proxy", "javassist");
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getInvoker(arg0, arg1, arg2);
}
public java.lang.Object getProxy(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.Invoker {
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.getParameter("proxy", "javassist");
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
com.alibaba.dubbo.rpc.ProxyFactory extension = (com.alibaba.dubbo.rpc.ProxyFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension(extName);
return extension.getProxy(arg0);
}
}
生成的代碼中可以看到,默認(rèn)的Protocol實(shí)現(xiàn)是dubbo,默認(rèn)的proxy是javassist。
ServiceConfig的export
export的步驟簡介
- 首先會檢查各種配置信息,填充各種屬性,總之就是保證我在開始暴露服務(wù)之前,所有的東西都準(zhǔn)備好了,并且是正確的。
- 加載所有的注冊中心,因?yàn)槲覀儽┞斗?wù)需要注冊到注冊中心中去。
- 根據(jù)配置的所有協(xié)議和注冊中心url分別進(jìn)行導(dǎo)出。
- 進(jìn)行導(dǎo)出的時(shí)候,又是一波屬性的獲取設(shè)置檢查等操作。
- 如果配置的不是remote,則做本地導(dǎo)出。
- 如果配置的不是local,則暴露為遠(yuǎn)程服務(wù)。
- 不管是本地還是遠(yuǎn)程服務(wù)暴露,首先都會獲取Invoker。
- 獲取完Invoker之后,轉(zhuǎn)換成對外的Exporter,緩存起來。
export方法先判斷是否需要延遲暴露(這里我們使用的是不延遲暴露),然后執(zhí)行doExport方法。
doExport方法先執(zhí)行一系列的檢查方法,然后調(diào)用doExportUrls方法。檢查方法會檢測dubbo的配置是否在Spring配置文件中聲明,沒有的話讀取properties文件初始化。
doExportUrls方法先調(diào)用loadRegistries獲取所有的注冊中心url,然后遍歷調(diào)用doExportUrlsFor1Protocol方法。對于在標(biāo)簽中指定了registry屬性的Bean,會在加載BeanDefinition的時(shí)候就加載了注冊中心。
獲取注冊中心url,會把注冊的信息都放在一個(gè)URL對象中,一個(gè)URL內(nèi)容如下:
registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?application=dubbo-provider&application.version=1.0&dubbo=2.5.3&environment=product&organization=china&owner=cheng.xi&pid=2939®istry=zookeeper×tamp=1488898049284
doExportUrlsFor1Protocol根據(jù)不同的協(xié)議將服務(wù)以URL形式暴露。如果scope配置為none則不暴露,如果服務(wù)未配置成remote,則本地暴露exportLocal,如果未配置成local,則注冊服務(wù)registryProcotol。
這里的URL是:
dubbo://192.168.1.100:20880/dubbo.common.hello.service.HelloService?anyhost=true&application=dubbo-provider&application.version=1.0&delay=5000&dubbo=2.5.3&environment=product&interface=dubbo.common.hello.service.HelloService&methods=sayHello&organization=china&owner=cheng.xi&pid=2939&side=provider×tamp=1488898464953
本地暴露
這時(shí)候會先做本地暴露,exportLocal(url);:
private void exportLocal(URL url) {
if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) {
//這時(shí)候轉(zhuǎn)成本地暴露的url:injvm://127.0.0.1/dubbo.common.hello.service.HelloService?anyhost=true&
//application=dubbo-provider&application.version=1.0&dubbo=2.5.3&environment=product&
//interface=dubbo.common.hello.service.HelloService&methods=sayHello&
//organization=china&owner=cheng.xi&pid=720&side=provider×tamp=1489716708276
URL local = URL.valueOf(url.toFullString())
.setProtocol(Constants.LOCAL_PROTOCOL)
.setHost(NetUtils.LOCALHOST)
.setPort(0);
//首先還是先獲得Invoker
//然后導(dǎo)出成Exporter,并緩存
//這里的proxyFactory實(shí)際是JavassistProxyFactory
//有關(guān)詳細(xì)的獲得Invoke以及exporter會在下面的流程解析,在本地暴露這個(gè)流程就不再說明。
Exporter<?> exporter = protocol.export(
proxyFactory.getInvoker(ref, (Class) interfaceClass, local));
exporters.add(exporter);
logger.info("Export dubbo service " + interfaceClass.getName() +" to local registry");
}
}
暴露為遠(yuǎn)程服務(wù)
接下來是暴露為遠(yuǎn)程服務(wù),跟本地暴露的流程一樣還是先獲取Invoker,然后導(dǎo)出成Exporter:
//根據(jù)服務(wù)具體實(shí)現(xiàn),實(shí)現(xiàn)接口,以及registryUrl通過ProxyFactory將HelloServiceImpl封裝成一個(gè)本地執(zhí)行的Invoker
//invoker是對具體實(shí)現(xiàn)的一種代理。
//這里proxyFactory是上面列出的生成的代碼
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
//使用Protocol將invoker導(dǎo)出成一個(gè)Exporter
//暴露封裝服務(wù)invoker
//調(diào)用Protocol生成的適配類的export方法
//這里的protocol是上面列出的生成的代碼
Exporter<?> exporter = protocol.export(invoker);
關(guān)于Invoker,Exporter等的解釋參見最下面的內(nèi)容。
暴露遠(yuǎn)程服務(wù)時(shí)的獲取Invoker過程
服務(wù)實(shí)現(xiàn)類轉(zhuǎn)換成Invoker,大概的步驟是:
- 根據(jù)上面生成的proxyFactory方法調(diào)用具體的ProxyFactory實(shí)現(xiàn)類的getInvoker方法獲取Invoker。
- getInvoker的過程是,首先對實(shí)現(xiàn)類做一個(gè)包裝,生成一個(gè)包裝后的類。
- 然后新創(chuàng)建一個(gè)Invoker實(shí)例,這個(gè)Invoker中包含著生成的Wrapper類,Wrapper類中有具體的實(shí)現(xiàn)類。
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
這行代碼中包含服務(wù)實(shí)現(xiàn)類轉(zhuǎn)換成Invoker的過程,其中proxyFactory是上面列出的動(dòng)態(tài)生成的代碼,其中g(shù)etInvoker的代碼為(做了精簡,把包都去掉了):
public Invoker getInvoker(Object arg0, Class arg1, URL arg2) throws Object {
if (arg2 == null) throw new IllegalArgumentException("url == null");
//傳進(jìn)來的url是dubbo://192.168.110.197:20880/dubbo.common.hello.service.HelloService?anyhost=true&application=dubbo-provider
//&application.version=1.0&dubbo=2.5.3&environment=product&interface=dubbo.common.hello.service.HelloService&methods=sayHello&organization=china&owner=cheng.xi
//&pid=28191&side=provider×tamp=1489027396094
URL url = arg2;
//沒有proxy參數(shù)配置,默認(rèn)使用javassist
String extName = url.getParameter("proxy", "javassist");
if(extName == null) throw new IllegalStateException("Fail to get extension(ProxyFactory) name from url(" + url.toString() + ") use keys([proxy])");
//這一步就使用javassist來獲取ProxyFactory的實(shí)現(xiàn)類JavassistProxyFactory
ProxyFactory extension = (ProxyFactory)ExtensionLoader.getExtensionLoader(ProxyFactory.class).getExtension(extName);
//JavassistProxyFactory的getInvoker方法
return extension.getInvoker(arg0, arg1, arg2);
}
使用JavassistProxyFactory獲取Invoker
JavassistProxyFactory的getInvoker方法:
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
// TODO Wrapper類不能正確處理帶$的類名
//第一步封裝一個(gè)Wrapper類
//該類是手動(dòng)生成的
//如果類是以$開頭,就使用接口類型獲取,其他的使用實(shí)現(xiàn)類獲取
final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf('$') < 0 ? proxy.getClass() : type);
//返回一個(gè)Invoker實(shí)例,doInvoke方法中直接返回上面wrapper的invokeMethod
//關(guān)于生成的wrapper,請看下面列出的生成的代碼,其中invokeMethod方法中就有實(shí)現(xiàn)類對實(shí)際方法的調(diào)用
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);
}
};
}
生成wrapper類的過程,首先看getWrapper方法:
public static Wrapper getWrapper(Class<?> c){
while( ClassGenerator.isDynamicClass(c) ) // can not wrapper on dynamic class.
c = c.getSuperclass();
//Object類型的
if( c == Object.class )
return OBJECT_WRAPPER;
//先去Wrapper緩存中查找
Wrapper ret = WRAPPER_MAP.get(c);
if( ret == null ) {
//緩存中不存在,生成Wrapper類,放到緩存
ret = makeWrapper(c);
WRAPPER_MAP.put(c,ret);
}
return ret;
}
makeWrapper方法代碼不在列出,太長了。就是生成一個(gè)繼承自Wrapper的類,最后的結(jié)果大概是:
public class Wrapper1 extends Wrapper {
public static String[] pns;
public static Map pts;
public static String[] mns; // all method name array.
public static String[] dmns;
public static Class[] mts0;
public String[] getPropertyNames() {
return pns;
}
public boolean hasProperty(String n) {
return pts.containsKey($1);
}
public Class getPropertyType(String n) {
return (Class) pts.get($1);
}
public String[] getMethodNames() {
return mns;
}
public String[] getDeclaredMethodNames() {
return dmns;
}
public void setPropertyValue(Object o, String n, Object v) {
dubbo.provider.hello.service.impl.HelloServiceImpl w;
try {
w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class dubbo.provider.hello.service.impl.HelloServiceImpl.");
}
public Object getPropertyValue(Object o, String n) {
dubbo.provider.hello.service.impl.HelloServiceImpl w;
try {
w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchPropertyException("Not found property \"" + $2 + "\" filed or setter method in class dubbo.provider.hello.service.impl.HelloServiceImpl.");
}
public Object invokeMethod(Object o, String n, Class[] p, Object[] v) throws java.lang.reflect.InvocationTargetException {
dubbo.provider.hello.service.impl.HelloServiceImpl w;
try {
w = ((dubbo.provider.hello.service.impl.HelloServiceImpl) $1);
} catch (Throwable e) {
throw new IllegalArgumentException(e);
}
try {
if ("sayHello".equals($2) && $3.length == 0) {
w.sayHello();
return null;
}
} catch (Throwable e) {
throw new java.lang.reflect.InvocationTargetException(e);
}
throw new com.alibaba.dubbo.common.bytecode.NoSuchMethodException("Not found method \"" + $2 + "\" in class dubbo.provider.hello.service.impl.HelloServiceImpl.");
}
}
生成完Wrapper以后,返回一個(gè)AbstractProxyInvoker實(shí)例。至此生成Invoker的步驟就完成了。可以看到Invoker執(zhí)行方法的時(shí)候,會調(diào)用Wrapper的invokeMethod,這個(gè)方法中會有真實(shí)的實(shí)現(xiàn)類調(diào)用真實(shí)方法的代碼。
使用JdkProxyFactory獲取invoker
JdkProxyFactory的getInvoker方法:
public <T> Invoker<T> getInvoker(T proxy, Class<T> type, URL url) {
return new AbstractProxyInvoker<T>(proxy, type, url) {
@Override
protected Object doInvoke(T proxy, String methodName,
Class<?>[] parameterTypes,
Object[] arguments) throws Throwable {
Method method = proxy.getClass().getMethod(methodName, parameterTypes);
return method.invoke(proxy, arguments);
}
};
}
直接返回一個(gè)AbstractProxyInvoker實(shí)例,沒有做處理,只是使用反射調(diào)用具體的方法。
JdkProxyFactory的getProxy方法:
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), interfaces, new InvokerInvocationHandler(invoker));
}
使用Java的反射機(jī)制生成一個(gè)代理類。
暴露遠(yuǎn)程服務(wù)時(shí)導(dǎo)出Invoker為Exporter
Invoker導(dǎo)出為Exporter分為兩種情況,第一種是Registry類型的Invoker,第二種是其他協(xié)議類型的Invoker,分開解析。
代碼入口:
Exporter<?> exporter = protocol.export(invoker);
Registry類型的Invoker處理過程
大概的步驟是:
- 經(jīng)過兩個(gè)不用做任何處理的Wrapper類,然后到達(dá)RegistryProtocol中。
- 通過具體的協(xié)議導(dǎo)出Invoker為Exporter。
- 注冊服務(wù)到注冊中心。
- 訂閱注冊中心的服務(wù)。
- 生成一個(gè)新的Exporter實(shí)例,將上面的Exporter進(jìn)行引入,然后返回。
protocol是上面列出的動(dòng)態(tài)生成的代碼,會先調(diào)用ProtocolListenerWrapper,這個(gè)Wrapper負(fù)責(zé)初始化暴露和引用服務(wù)的監(jiān)聽器。對于Registry類型的不做處理,代碼如下:
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//registry類型的Invoker,不需要做處理
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
//非Registry類型的Invoker,需要被監(jiān)聽器包裝
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
接著調(diào)用ProtocolFilterWrapper中的export方法,ProtocolFilterWrapper負(fù)責(zé)初始化invoker所有的Filter。代碼如下:
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//Registry類型的Invoker不做處理
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
//非Registry類型的Invoker需要先構(gòu)建調(diào)用鏈,然后再導(dǎo)出
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
這里我們先解析的是Registry類型的Invoker,接著就會調(diào)用RegistryProtocol的export方法,RegistryProtocol負(fù)責(zé)注冊服務(wù)到注冊中心和向注冊中心訂閱服務(wù)。代碼如下:
public <T> Exporter<T> export(final Invoker<T> originInvoker) throws RpcException {
//export invoker
//這里就交給了具體的協(xié)議去暴露服務(wù)(先不解析,留在后面,可以先去后面看下導(dǎo)出過程)
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
//registry provider
//根據(jù)invoker中的url獲取Registry實(shí)例
//并且連接到注冊中心
//此時(shí)提供者作為消費(fèi)者引用注冊中心核心服務(wù)RegistryService
final Registry registry = getRegistry(originInvoker);
//注冊到注冊中心的URL
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//調(diào)用遠(yuǎn)端注冊中心的register方法進(jìn)行服務(wù)注冊
//若有消費(fèi)者訂閱此服務(wù),則推送消息讓消費(fèi)者引用此服務(wù)。
//注冊中心緩存了所有提供者注冊的服務(wù)以供消費(fèi)者發(fā)現(xiàn)。
registry.register(registedProviderUrl);
// 訂閱override數(shù)據(jù)
// FIXME 提供者訂閱時(shí),會影響同一JVM即暴露服務(wù),又引用同一服務(wù)的的場景,因?yàn)閟ubscribed以服務(wù)名為緩存的key,導(dǎo)致訂閱信息覆蓋。
final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl);
overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
//提供者向注冊中心訂閱所有注冊服務(wù)的覆蓋配置
//當(dāng)注冊中心有此服務(wù)的覆蓋配置注冊進(jìn)來時(shí),推送消息給提供者,重新暴露服務(wù),這由管理頁面完成。
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//保證每次export都返回一個(gè)新的exporter實(shí)例
//返回暴露后的Exporter給上層ServiceConfig進(jìn)行緩存,便于后期撤銷暴露。
return new Exporter<T>() {
public Invoker<T> getInvoker() {
return exporter.getInvoker();
}
public void unexport() {
try {
exporter.unexport();
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
try {
registry.unregister(registedProviderUrl);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
try {
overrideListeners.remove(overrideSubscribeUrl);
registry.unsubscribe(overrideSubscribeUrl, overrideSubscribeListener);
} catch (Throwable t) {
logger.warn(t.getMessage(), t);
}
}
};
}
交給具體的協(xié)議去暴露服務(wù)
先不解析,留在后面,可以先去后面看下導(dǎo)出過程,然后再回來接著看注冊到注冊中心的過程。具體協(xié)議暴露服務(wù)主要是打開服務(wù)器和端口,進(jìn)行監(jiān)聽。
連接注冊中心并獲取Registry實(shí)例
具體的協(xié)議進(jìn)行暴露并且返回了一個(gè)ExporterChangeableWrapper之后,接下來看下一步連接注冊中心并注冊到注冊中心,代碼是在RegistryProtocol的export方法:
//先假裝此步已經(jīng)分析完
final ExporterChangeableWrapper<T> exporter = doLocalExport(originInvoker);
//得到具體的注冊中心,連接注冊中心,此時(shí)提供者作為消費(fèi)者引用注冊中心核心服務(wù)RegistryService
final Registry registry = getRegistry(originInvoker);
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//調(diào)用遠(yuǎn)端注冊中心的register方法進(jìn)行服務(wù)注冊
//若有消費(fèi)者訂閱此服務(wù),則推送消息讓消費(fèi)者引用此服務(wù)
registry.register(registedProviderUrl);
//提供者向注冊中心訂閱所有注冊服務(wù)的覆蓋配置
registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);
//返回暴露后的Exporter給上層ServiceConfig進(jìn)行緩存
return new Exporter<T>() {。。。}
getRegistry(originInvoker)方法:
//根據(jù)invoker的地址獲取registry實(shí)例
private Registry getRegistry(final Invoker<?> originInvoker){
//獲取invoker中的registryUrl
URL registryUrl = originInvoker.getUrl();
if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
//獲取registry的值,這里獲得是zookeeper,默認(rèn)值是dubbo
String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);
//這里獲取到的url為:
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?
//application=dubbo-provider&application.version=1.0&dubbo=2.5.3&
//environment=product&export=dubbo%3A%2F%2F192.168.1.100%3A20880%2F
//dubbo.common.hello.service.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-provider%26
//application.version%3D1.0%26dubbo%3D2.5.3%26environment%3Dproduct%26
//interface%3Ddubbo.common.hello.service.HelloService%26methods%3DsayHello%26
//organization%3Dchina%26owner%3Dcheng.xi%26pid%3D9457%26side%3Dprovider%26timestamp%3D1489807681627&organization=china&owner=cheng.xi&
//pid=9457×tamp=1489807680193
registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
}
//根據(jù)SPI機(jī)制獲取具體的Registry實(shí)例,這里獲取到的是ZookeeperRegistry
return registryFactory.getRegistry(registryUrl);
}
這里的registryFactory是動(dòng)態(tài)生成的代碼,如下:
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class RegistryFactory$Adpative implements com.alibaba.dubbo.registry.RegistryFactory {
public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
return extension.getRegistry(arg0);
}
}
所以這里registryFactory.getRegistry(registryUrl)用的是ZookeeperRegistryFactory。
先看下getRegistry方法,會發(fā)現(xiàn)該方法會在AbstractRegistryFactory中實(shí)現(xiàn):
public Registry getRegistry(URL url) {
url = url.setPath(RegistryService.class.getName())
.addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
.removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
//這里key為:
//zookeeper://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService
String key = url.toServiceString();
// 鎖定注冊中心獲取過程,保證注冊中心單一實(shí)例
LOCK.lock();
try {
//先從緩存中獲取Registry實(shí)例
Registry registry = REGISTRIES.get(key);
if (registry != null) {
return registry;
}
//創(chuàng)建registry,會直接new一個(gè)ZookeeperRegistry返回
//具體創(chuàng)建實(shí)例是子類來實(shí)現(xiàn)的
registry = createRegistry(url);
if (registry == null) {
throw new IllegalStateException("Can not create registry " + url);
}
//放到緩存中
REGISTRIES.put(key, registry);
return registry;
} finally {
// 釋放鎖
LOCK.unlock();
}
}
createRegistry(url);是在子類中實(shí)現(xiàn)的,這里是ZookeeperRegistry,首先需要經(jīng)過AbstractRegistry的構(gòu)造:
public AbstractRegistry(URL url) {
//url保存起來
setUrl(url);
// 啟動(dòng)文件保存定時(shí)器
//
syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
//保存的文件為:
///home/xxx/.dubbo/dubbo-registry-127.0.0.1.cache
String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
File file = null;
if (ConfigUtils.isNotEmpty(filename)) {
file = new File(filename);
if(! file.exists() && file.getParentFile() != null && ! file.getParentFile().exists()){
if(! file.getParentFile().mkdirs()){
throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
}
}
}
this.file = file;
//加載文件中的屬性
loadProperties();
//通知訂閱
notify(url.getBackupUrls());
}
獲取Registry時(shí)的訂閱
notify()方法:
protected void notify(List<URL> urls) {
if(urls == null || urls.isEmpty()) return;
//getSubscribed()方法獲取訂閱者列表
//訂閱者Entry里每個(gè)URL都對應(yīng)著n個(gè)NotifyListener
for (Map.Entry<URL, Set<NotifyListener>> entry : getSubscribed().entrySet()) {
URL url = entry.getKey();
if(! UrlUtils.isMatch(url, urls.get(0))) {
continue;
}
Set<NotifyListener> listeners = entry.getValue();
if (listeners != null) {
for (NotifyListener listener : listeners) {
try {
//通知每個(gè)監(jiān)聽器
notify(url, listener, filterEmpty(url, urls));
} catch (Throwable t) {}
}
}
}
}
notify(url, listener, filterEmpty(url, urls));
代碼:
protected void notify(URL url, NotifyListener listener, List<URL> urls) {
Map<String, List<URL>> result = new HashMap<String, List<URL>>();
for (URL u : urls) {
if (UrlUtils.isMatch(url, u)) {
//分類
String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
List<URL> categoryList = result.get(category);
if (categoryList == null) {
categoryList = new ArrayList<URL>();
result.put(category, categoryList);
}
categoryList.add(u);
}
}
if (result.size() == 0) {
return;
}
Map<String, List<URL>> categoryNotified = notified.get(url);
if (categoryNotified == null) {
notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
categoryNotified = notified.get(url);
}
for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
String category = entry.getKey();
List<URL> categoryList = entry.getValue();
categoryNotified.put(category, categoryList);
//保存到主目錄下的.dubbo目錄下
saveProperties(url);
//上面獲取到的監(jiān)聽器進(jìn)行通知
listener.notify(categoryList);
}
}
AbstractRegistry構(gòu)造器初始化完,接著調(diào)用FailbackRegistry構(gòu)造器初始化:
public FailbackRegistry(URL url) {
super(url);
//重試時(shí)間,默認(rèn)5000ms
int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);
//啟動(dòng)失敗重試定時(shí)器
this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
public void run() {
// 檢測并連接注冊中心
try {
//重試方法由每個(gè)具體子類實(shí)現(xiàn)
//獲取到注冊失敗的,然后嘗試注冊
retry();
} catch (Throwable t) { // 防御性容錯(cuò)}
}
}, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
}
最后回到ZookeeperRegistry的構(gòu)造初始化:
public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
super(url);
if (url.isAnyHost()) {
throw new IllegalStateException("registry address == null");
}
//獲得到注冊中心中的分組,默認(rèn)dubbo
String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
if (! group.startsWith(Constants.PATH_SEPARATOR)) {
group = Constants.PATH_SEPARATOR + group;
}
//注冊到注冊中心的節(jié)點(diǎn)
this.root = group;
//使用zookeeperTansporter去連接
//ZookeeperTransport這里是生成的自適應(yīng)實(shí)現(xiàn),默認(rèn)使用ZkClientZookeeperTransporter
//ZkClientZookeeperTransporter的connect去實(shí)例化一個(gè)ZkClient實(shí)例
//并且訂閱狀態(tài)變化的監(jiān)聽器subscribeStateChanges
//然后返回一個(gè)ZkClientZookeeperClient實(shí)例
zkClient = zookeeperTransporter.connect(url);
//ZkClientZookeeperClient添加狀態(tài)改變監(jiān)聽器
zkClient.addStateListener(new StateListener() {
public void stateChanged(int state) {
if (state == RECONNECTED) {
try {
recover();
} catch (Exception e) {
logger.error(e.getMessage(), e);
}
}
}
});
}
獲取注冊到注冊中心的url
獲取到了Registry,Registry實(shí)例中保存著連接到了zookeeper的zkClient實(shí)例之后,下一步獲取要注冊到注冊中心的url(在RegistryProtocol中)。
final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
//得到的URL是:
//dubbo://192.168.1.100:20880/dubbo.common.hello.service.HelloService?
//anyhost=true&application=dubbo-provider&application.version=1.0&dubbo=2.5.3&environment=product&
//interface=dubbo.common.hello.service.HelloService&methods=sayHello&
//organization=china&owner=cheng.xi&pid=9457&side=provider×tamp=1489807681627
注冊到注冊中心
然后調(diào)用registry.register(registedProviderUrl)
注冊到注冊中心(在RegistryProtocol中)。register方法的實(shí)現(xiàn)在FailbackRegistry中:
public void register(URL url) {
super.register(url);
failedRegistered.remove(url);
failedUnregistered.remove(url);
try {
// 向服務(wù)器端發(fā)送注冊請求
//調(diào)用子類具體實(shí)現(xiàn),發(fā)送注冊請求
doRegister(url);
} catch (Exception e) {
Throwable t = e;
// 如果開啟了啟動(dòng)時(shí)檢測,則直接拋出異常
boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
&& url.getParameter(Constants.CHECK_KEY, true)
&& ! Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
boolean skipFailback = t instanceof SkipFailbackWrapperException;
if (check || skipFailback) {
if(skipFailback) {
t = t.getCause();
}
throw 。。。
} else { }
// 將失敗的注冊請求記錄到失敗列表,定時(shí)重試
failedRegistered.add(url);
}
}
doRegister(url);
在這里是ZookeeperRegistry中具體實(shí)現(xiàn)的,這里將會注冊到注冊中心:
protected void doRegister(URL url) {
try {
//這里zkClient就是我們上面調(diào)用構(gòu)造的時(shí)候生成的
//ZkClientZookeeperClient
//保存著連接到Zookeeper的zkClient實(shí)例
//開始注冊,也就是在Zookeeper中創(chuàng)建節(jié)點(diǎn)
//這里toUrlPath獲取到的path為:
///dubbo/dubbo.common.hello.service.HelloService/providers/dubbo%3A%2F%2F192.168.1.100%3A20880%2F
//dubbo.common.hello.service.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-provider%26
//application.version%3D1.0%26dubbo%3D2.5.3%26environment%3Dproduct%26interface%3D
//dubbo.common.hello.service.HelloService%26methods%3DsayHello%26
//organization%3Dchina%26owner%3Dcheng.xi%26pid%3D8920%26side%3Dprovider%26timestamp%3D1489828029449
//默認(rèn)創(chuàng)建的節(jié)點(diǎn)是臨時(shí)節(jié)點(diǎn)
zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
} catch (Throwable e) { }
}
經(jīng)過這一步之后,Zookeeper中就有節(jié)點(diǎn)存在了,具體節(jié)點(diǎn)為:
/dubbo
dubbo.common.hello.service.HelloService
providers
/dubbo/dubbo.common.hello.service.HelloService/providers/
dubbo%3A%2F%2F192.168.1.100%3A20880%2Fdubbo.common.hello.service.HelloService%3F
anyhost%3Dtrue%26application%3Ddubbo-provider%26
application.version%3D1.0%26dubbo%3D2.5.3%26environment%3Dproduct%26
interface%3Ddubbo.common.hello.service.HelloService%26methods%3DsayHello%26
organization%3Dchina%26owner%3Dcheng.xi%26pid%3D13239%26side%3D
provider%26timestamp%3D1489829293525
訂閱注冊中心的服務(wù)
在注冊到注冊中心之后,registry會去訂閱覆蓋配置的服務(wù),這一步之后就會在/dubbo/dubbo.common.hello.service/HelloService
節(jié)點(diǎn)下多一個(gè)configurators節(jié)點(diǎn)。(具體過程暫先不解析)。
返回新Exporter實(shí)例
最后返回Exporter新實(shí)例,返回到ServiceConfig中。服務(wù)的發(fā)布就算完成了。
交給具體的協(xié)議進(jìn)行服務(wù)暴露
這里也就是非Registry類型的Invoker的導(dǎo)出過程。主要的步驟是將本地ip和20880端口打開,進(jìn)行監(jiān)聽。最后包裝成exporter返回。
doLocalExport(invoker):
private <T> ExporterChangeableWrapper<T> doLocalExport(final Invoker<T> originInvoker){
//原始的invoker中的url:
//registry://127.0.0.1:2181/com.alibaba.dubbo.registry.RegistryService?
//application=dubbo-provider&application.version=1.0&dubbo=2.5.3
//&environment=product&export=dubbo%3A%2F%2F10.42.0.1%3A20880%2F
//dubbo.common.hello.service.HelloService%3Fanyhost%3Dtrue%26application%3Ddubbo-provider%26
//application.version%3D1.0%26dubbo%3D2.5.3%26environment%3Dproduct%26
//interface%3Ddubbo.common.hello.service.HelloService%26methods%3DsayHello%26
//organization%3Dchina%26owner%3Dcheng.xi%26pid%3D7876%26side%3Dprovider%26timestamp%3D1489057305001&
//organization=china&owner=cheng.xi&pid=7876®istry=zookeeper×tamp=1489057304900
//從原始的invoker中得到的key:
//dubbo://10.42.0.1:20880/dubbo.common.hello.service.HelloService?anyhost=true&application=dubbo-provider&
//application.version=1.0&dubbo=2.5.3&environment=product&interface=dubbo.common.hello.service.HelloService&
//methods=sayHello&organization=china&owner=cheng.xi&pid=7876&side=provider×tamp=1489057305001
String key = getCacheKey(originInvoker);
ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
synchronized (bounds) {
exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
if (exporter == null) {
//得到一個(gè)Invoker代理,里面包含原來的Invoker
final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
//此處protocol還是最上面生成的代碼,調(diào)用代碼中的export方法,會根據(jù)協(xié)議名選擇調(diào)用具體的實(shí)現(xiàn)類
//這里我們需要調(diào)用DubboProtocol的export方法
//這里的使用具體協(xié)議進(jìn)行導(dǎo)出的invoker是個(gè)代理invoker
//導(dǎo)出完之后,返回一個(gè)新的ExporterChangeableWrapper實(shí)例
exporter = new ExporterChangeableWrapper<T>((Exporter<T>)protocol.export(invokerDelegete), originInvoker);
bounds.put(key, exporter);
}
}
}
return (ExporterChangeableWrapper<T>) exporter;
}
使用dubbo協(xié)議導(dǎo)出
這里protocol.export(invokerDelegete)
就要去具體的DubboProtocol中執(zhí)行了,DubboProtocol的外面包裹著ProtocolFilterWrapper,再外面還包裹著ProtocolListenerWrapper。會先經(jīng)過ProtocolListenerWrapper:
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//Registry類型的Invoker
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
//其他具體協(xié)議類型的Invoker
//先進(jìn)行導(dǎo)出protocol.export(invoker)
//然后獲取自適應(yīng)的監(jiān)聽器
//最后返回的是包裝了監(jiān)聽器的Exporter
//這里監(jiān)聽器的獲取是getActivateExtension,如果指定了listener就加載實(shí)現(xiàn),沒有指定就不加載
return new ListenerExporterWrapper<T>(protocol.export(invoker),
Collections.unmodifiableList(ExtensionLoader.getExtensionLoader(ExporterListener.class)
.getActivateExtension(invoker.getUrl(), Constants.EXPORTER_LISTENER_KEY)));
}
再經(jīng)過ProtocolFilterWrapper:
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//Registry類型的Invoker
if (Constants.REGISTRY_PROTOCOL.equals(invoker.getUrl().getProtocol())) {
return protocol.export(invoker);
}
//其他具體協(xié)議類型的Invoker
//先構(gòu)建Filter鏈,然后再導(dǎo)出
return protocol.export(buildInvokerChain(invoker, Constants.SERVICE_FILTER_KEY, Constants.PROVIDER));
}
查看下構(gòu)建Invoker鏈的方法:
private static <T> Invoker<T> buildInvokerChain(final Invoker<T> invoker, String key, String group) {
//我們要處理的那個(gè)Invoker作為處理鏈的最后一個(gè)
Invoker<T> last = invoker;
//根據(jù)key和group獲取自動(dòng)激活的Filter
List<Filter> filters = ExtensionLoader.getExtensionLoader(Filter.class).getActivateExtension(invoker.getUrl(), key, group);
if (filters.size() > 0) {
//把所有的過濾器都挨個(gè)連接起來,最后一個(gè)是我們真正的Invoker
for (int i = filters.size() - 1; i >= 0; i --) {
final Filter filter = filters.get(i);
final Invoker<T> next = last;
last = new Invoker<T>() {
public Class<T> getInterface() {
return invoker.getInterface();
}
public URL getUrl() {
return invoker.getUrl();
}
public boolean isAvailable() {
return invoker.isAvailable();
}
public Result invoke(Invocation invocation) throws RpcException {
return filter.invoke(next, invocation);
}
public void destroy() {
invoker.destroy();
}
@Override
public String toString() {
return invoker.toString();
}
};
}
}
return last;
}
接著就到了DubboProtocol的export方法,這里進(jìn)行暴露服務(wù):
public <T> Exporter<T> export(Invoker<T> invoker) throws RpcException {
//dubbo://10.42.0.1:20880/dubbo.common.hello.service.HelloService?
//anyhost=true&application=dubbo-provider&
//application.version=1.0&dubbo=2.5.3&environment=product&
//interface=dubbo.common.hello.service.HelloService&
//methods=sayHello&organization=china&owner=cheng.xi&
//pid=7876&side=provider×tamp=1489057305001
URL url = invoker.getUrl();
// export service.
//key由serviceName,port,version,group組成
//當(dāng)nio客戶端發(fā)起遠(yuǎn)程調(diào)用時(shí),nio服務(wù)端通過此key來決定調(diào)用哪個(gè)Exporter,也就是執(zhí)行的Invoker。
//dubbo.common.hello.service.HelloService:20880
String key = serviceKey(url);
//將Invoker轉(zhuǎn)換成Exporter
//直接new一個(gè)新實(shí)例
//沒做啥處理,就是做一些賦值操作
//這里的exporter就包含了invoker
DubboExporter<T> exporter = new DubboExporter<T>(invoker, key, exporterMap);
//緩存要暴露的服務(wù),key是上面生成的
exporterMap.put(key, exporter);
//export an stub service for dispaching event
//是否支持本地存根
//遠(yuǎn)程服務(wù)后,客戶端通常只剩下接口,而實(shí)現(xiàn)全在服務(wù)器端,
//但提供方有些時(shí)候想在客戶端也執(zhí)行部分邏輯,比如:做ThreadLocal緩存,
//提前驗(yàn)證參數(shù),調(diào)用失敗后偽造容錯(cuò)數(shù)據(jù)等等,此時(shí)就需要在API中帶上Stub,
//客戶端生成Proxy實(shí),會把Proxy通過構(gòu)造函數(shù)傳給Stub,
//然后把Stub暴露組給用戶,Stub可以決定要不要去調(diào)Proxy。
Boolean isStubSupportEvent = url.getParameter(Constants.STUB_EVENT_KEY,Constants.DEFAULT_STUB_EVENT);
Boolean isCallbackservice = url.getParameter(Constants.IS_CALLBACK_SERVICE, false);
if (isStubSupportEvent && !isCallbackservice){
String stubServiceMethods = url.getParameter(Constants.STUB_EVENT_METHODS_KEY);
if (stubServiceMethods == null || stubServiceMethods.length() == 0 ){
} else {
stubServiceMethodsMap.put(url.getServiceKey(), stubServiceMethods);
}
}
//根據(jù)URL綁定IP與端口,建立NIO框架的Server
openServer(url);
return exporter;
}
上面得到的Exporter會被放到緩存中去,key就是上面生成的,客戶端就可以發(fā)請求根據(jù)key找到Exporter,然后找到invoker進(jìn)行調(diào)用了。接下來是創(chuàng)建服務(wù)器并監(jiān)聽端口。
接著調(diào)用openServer方法創(chuàng)建NIO Server進(jìn)行監(jiān)聽:
private void openServer(URL url) {
// find server.
//key是IP:PORT
//192.168.110.197:20880
String key = url.getAddress();
//client 也可以暴露一個(gè)只有server可以調(diào)用的服務(wù)。
boolean isServer = url.getParameter(Constants.IS_SERVER_KEY,true);
if (isServer) {
ExchangeServer server = serverMap.get(key);
//同一JVM中,同協(xié)議的服務(wù),共享同一個(gè)Server,
//第一個(gè)暴露服務(wù)的時(shí)候創(chuàng)建server,
//以后相同協(xié)議的服務(wù)都使用同一個(gè)server
if (server == null) {
serverMap.put(key, createServer(url));
} else {
//同協(xié)議的服務(wù)后來暴露服務(wù)的則使用第一次創(chuàng)建的同一Server
//server支持reset,配合override功能使用
//accept、idleTimeout、threads、heartbeat參數(shù)的變化會引起Server的屬性發(fā)生變化
//這時(shí)需要重新設(shè)置Server
server.reset(url);
}
}
}
繼續(xù)看createServer方法:
//url為:
//dubbo://192.168.110.197:20880/dubbo.common.hello.service.HelloService?
//anyhost=true&application=dubbo-provider&
//application.version=1.0&dubbo=2.5.3&environment=product&
//interface=dubbo.common.hello.service.HelloService&
//methods=sayHello&organization=china&owner=cheng.xi&
//pid=720&side=provider×tamp=1489716708276
private ExchangeServer createServer(URL url) {
//默認(rèn)開啟server關(guān)閉時(shí)發(fā)送readonly事件
url = url.addParameterIfAbsent(Constants.CHANNEL_READONLYEVENT_SENT_KEY, Boolean.TRUE.toString());
//默認(rèn)開啟heartbeat
url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
//默認(rèn)使用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, Version.isCompatibleVersion() ? COMPATIBLE_CODEC_NAME : DubboCodec.NAME);
ExchangeServer server;
try {
//Exchangers是門面類,里面封裝的是Exchanger的邏輯。
//Exchanger默認(rèn)只有一個(gè)實(shí)現(xiàn)HeaderExchanger.
//Exchanger負(fù)責(zé)數(shù)據(jù)交換和網(wǎng)絡(luò)通信。
//從Protocol進(jìn)入Exchanger,標(biāo)志著程序進(jìn)入了remote層。
//這里requestHandler是ExchangeHandlerAdapter
server = Exchangers.bind(url, requestHandler);
} catch (RemotingException 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.bind方法:
public static ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
url = url.addParameterIfAbsent(Constants.CODEC_KEY, "exchange");
//getExchanger方法根據(jù)url獲取到一個(gè)默認(rèn)的實(shí)現(xiàn)HeaderExchanger
//調(diào)用HeaderExchanger的bind方法
return getExchanger(url).bind(url, handler);
}
HeaderExchanger的bind方法:
public ExchangeServer bind(URL url, ExchangeHandler handler) throws RemotingException {
//直接返回一個(gè)HeaderExchangeServer
//先創(chuàng)建一個(gè)HeaderExchangeHandler
//再創(chuàng)建一個(gè)DecodeHandler
//最后調(diào)用Transporters.bind
return new HeaderExchangeServer(Transporters.bind(url, new DecodeHandler(new HeaderExchangeHandler(handler))));
}
這里會先創(chuàng)建一個(gè)HeaderExchangerHandler,包含著ExchangeHandlerAdapter,接著創(chuàng)建一個(gè)DecodeHandler,會包含前面的handler,接下來調(diào)用Transporters的bind方法,返回一個(gè)Server,接著用HeaderExchangeServer包裝一下,就返回給Protocol層了。
在HeaderExchangerServer包裝的時(shí)候會啟動(dòng)心跳定時(shí)器startHeatbeatTimer();
,暫不解析。
Transports的bind方法:
public static Server bind(URL url, ChannelHandler... handlers) throws RemotingException {
ChannelHandler handler;
if (handlers.length == 1) {
handler = handlers[0];
} else {
//如果有多個(gè)handler的話,需要使用分發(fā)器包裝下
handler = new ChannelHandlerDispatcher(handlers);
}
//getTransporter()獲取一個(gè)Adaptive的Transporter
//然后調(diào)用bind方法(默認(rèn)是NettyTransporter的bind方法)
return getTransporter().bind(url, handler);
}
getTransporter()生成的Transporter的代碼如下:
import com.alibaba.dubbo.common.extension.ExtensionLoader;
public class Transporter$Adpative implements com.alibaba.dubbo.remoting.Transporter {
public com.alibaba.dubbo.remoting.Server bind(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
//Server默認(rèn)使用netty
String extName = url.getParameter("server", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([server, transporter])");
//獲取到一個(gè)NettyTransporter
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
//調(diào)用NettyTransporter的bind方法
return extension.bind(arg0, arg1);
}
public com.alibaba.dubbo.remoting.Client connect(com.alibaba.dubbo.common.URL arg0, com.alibaba.dubbo.remoting.ChannelHandler arg1) throws com.alibaba.dubbo.common.URL {
if (arg0 == null) throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg0;
String extName = url.getParameter("client", url.getParameter("transporter", "netty"));
if(extName == null) throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.Transporter) name from url(" + url.toString() + ") use keys([client, transporter])");
com.alibaba.dubbo.remoting.Transporter extension = (com.alibaba.dubbo.remoting.Transporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.Transporter.class).getExtension(extName);
return extension.connect(arg0, arg1);
}
}
NettyTransporter的bind方法:
public Server bind(URL url, ChannelHandler listener) throws RemotingException {
//創(chuàng)建一個(gè)Server
return new NettyServer(url, listener);
}
public NettyServer(URL url, ChannelHandler handler) throws RemotingException{
//handler先經(jīng)過ChannelHandlers的包裝方法
//然后再初始化
super(url, ChannelHandlers.wrap(handler, ExecutorUtil.setThreadName(url, SERVER_THREAD_POOL_NAME)));
}
ChannelHandlers.wrap方法中會根據(jù)SPI擴(kuò)展機(jī)制動(dòng)態(tài)生成Dispatcher的自適應(yīng)類,生成的代碼不在列出,默認(rèn)使用AllDispatcher處理,會返回一個(gè)AllChannelHandler,會把線程池和DataStore都初始化了。然后經(jīng)過HeartbeatHandler封裝,再經(jīng)過MultiMessageHandler封裝后返回。
NettyServer構(gòu)造,會依次經(jīng)過AbstractPeer,AbstractEndpoint,AbstractServer,NettyServer的初始化。重點(diǎn)看下AbstractServer的構(gòu)造方法:
public AbstractServer(URL url, ChannelHandler handler) throws RemotingException {
super(url, handler);
localAddress = getUrl().toInetSocketAddress();
String host = url.getParameter(Constants.ANYHOST_KEY, false)
|| NetUtils.isInvalidLocalHost(getUrl().getHost())
? NetUtils.ANYHOST : getUrl().getHost();
bindAddress = new InetSocketAddress(host, getUrl().getPort());
this.accepts = url.getParameter(Constants.ACCEPTS_KEY, Constants.DEFAULT_ACCEPTS);
this.idleTimeout = url.getParameter(Constants.IDLE_TIMEOUT_KEY, Constants.DEFAULT_IDLE_TIMEOUT);
try {
//初始化的時(shí)候會打開Server
//具體實(shí)現(xiàn)這里是NettyServer中
doOpen();
} catch (Throwable t) { }
if (handler instanceof WrappedChannelHandler ){
executor = ((WrappedChannelHandler)handler).getExecutor();
}
}
然后調(diào)用doOpen方法:
protected void doOpen() throws Throwable {
NettyHelper.setNettyLoggerFactory();
//boss線程池
ExecutorService boss = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerBoss", true));
//worker線程池
ExecutorService worker = Executors.newCachedThreadPool(new NamedThreadFactory("NettyServerWorker", true));
//ChannelFactory,沒有指定工作者線程數(shù)量,就使用cpu+1
ChannelFactory channelFactory = new NioServerSocketChannelFactory(boss, worker, getUrl().getPositiveParameter(Constants.IO_THREADS_KEY, Constants.DEFAULT_IO_THREADS));
bootstrap = new ServerBootstrap(channelFactory);
final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
channels = nettyHandler.getChannels();
bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
public ChannelPipeline getPipeline() {
NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec() ,getUrl(), NettyServer.this);
ChannelPipeline pipeline = Channels.pipeline();
pipeline.addLast("decoder", adapter.getDecoder());
pipeline.addLast("encoder", adapter.getEncoder());
pipeline.addLast("handler", nettyHandler);
return pipeline;
}
});
// bind之后返回一個(gè)Channel
channel = bootstrap.bind(getBindAddress());
}
doOpen方法創(chuàng)建Netty的Server端并打開,具體的事情就交給Netty去處理了,Netty的過程,原理,代碼有時(shí)間再另行研究。
- NIO框架接受到消息后,先由NettyCodecAdapter解碼,再由NettyHandler處理具體的業(yè)務(wù)邏輯,再由NettyCodecAdapter編碼后發(fā)送。
- NettyServer既是Server又是Handler。
- HeaderExchangerServer只是Server。
- MultiMessageHandler是多消息處理Handler。
- HeartbeatHandler是處理心跳事件的Handler。
- AllChannelHandler是消息派發(fā)器,負(fù)責(zé)將請求放入線程池,并執(zhí)行請求。
- DecodeHandler是編解碼Handler。
- HeaderExchangerHandler是信息交換Handler,將請求轉(zhuǎn)化成請求響應(yīng)模式與同步轉(zhuǎn)異步模式。
- RequestHandler是最后執(zhí)行的Handler,會在協(xié)議層選擇Exporter后選擇Invoker,進(jìn)而執(zhí)行Filter與Invoker,最終執(zhí)行請求服務(wù)實(shí)現(xiàn)類方法。
- Channel直接觸發(fā)事件并執(zhí)行Handler,Channel在有客戶端連接Server的時(shí)候觸發(fā)創(chuàng)建并封裝成NettyChannel,再由HeaderExchangerHandler創(chuàng)建HeaderExchangerChannel,負(fù)責(zé)請求響應(yīng)模式的處理。
- NettyChannel其實(shí)是個(gè)Handler,HeaderExchangerChannel是個(gè)Channel,
- 消息的序列化與反序列化工作在NettyCodecAdapter中發(fā)起完成。
當(dāng)有客戶端連接Server時(shí)的連接過程:
- NettyHandler.connected()
- NettyServer.connected()
- MultiMessageHandler.connected()
- HeartbeatHandler.connected()
- AllChannelHandler.connected()
- DecodeHandler.connected()
- HeaderExchangerHandler.connected()
- requestHandler.connected()
- 執(zhí)行服務(wù)的onconnect事件的監(jiān)聽方法
名詞解釋
Invoker
可執(zhí)行的對象,執(zhí)行具體的遠(yuǎn)程調(diào)用,能夠根據(jù)方法名稱,參數(shù)得到相應(yīng)的執(zhí)行結(jié)果。
Invocation,包含了需要執(zhí)行的方法,參數(shù)等信息。目前實(shí)現(xiàn)類只有RpcInvocation。
有三種類型的Invoker:
- 本地執(zhí)行類的Invoker。
- 遠(yuǎn)程通信執(zhí)行類的Invoker。
- 多個(gè)遠(yuǎn)程通信執(zhí)行類的Invoker聚合成集群版的Invoker。
以HelloService為例:
- 本地執(zhí)行類的Invoker:在Server端有HelloServiceImpl實(shí)現(xiàn),要執(zhí)行該接口,只需要通過反射執(zhí)行對應(yīng)的實(shí)現(xiàn)類即可。
- 遠(yuǎn)程通信執(zhí)行類的Invoker:在Client端要想執(zhí)行該接口的實(shí)現(xiàn)方法,需要先進(jìn)行遠(yuǎn)程通信,發(fā)送要執(zhí)行的參數(shù)信息給Server端,Server端利用本地執(zhí)行Invoker的方式執(zhí)行,最后將結(jié)果發(fā)送給Client。
- 集群版的Invoker:Client端使用的時(shí)候,通過集群版的Invoker操作,Invoker會挑選一個(gè)遠(yuǎn)程通信類型的Invoker來執(zhí)行。
提供者端的Invoker封裝了服務(wù)實(shí)現(xiàn)類,URL,Type,狀態(tài)都是只讀并且線程安全。通過發(fā)起invoke來具體調(diào)用服務(wù)類。
ProxyFactory
在服務(wù)提供者端,ProxyFactory主要服務(wù)的實(shí)現(xiàn)統(tǒng)一包裝成一個(gè)Invoker,Invoker通過反射來執(zhí)行具體的Service實(shí)現(xiàn)對象的方法。默認(rèn)的實(shí)現(xiàn)是JavassistProxyFactory,代碼如下:
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);
}
};
}
Protocol
服務(wù)地址的發(fā)布和訂閱。
Protocol是dubbo中的服務(wù)域,只在服務(wù)啟用時(shí)加載,無狀態(tài),線程安全,是實(shí)體域Invoker暴露和引用的主功能入口,負(fù)責(zé)Invoker的生命周期管理,是Dubbo中遠(yuǎn)程服務(wù)調(diào)用層。
Protocol根據(jù)指定協(xié)議對外公布服務(wù),當(dāng)客戶端根據(jù)協(xié)議調(diào)用這個(gè)服務(wù)時(shí),Protocol會將客戶端傳遞過來的Invocation參數(shù)交給Invoker去執(zhí)行。
Protocol加入了遠(yuǎn)程通信協(xié)議,會根據(jù)客戶端的請求來獲取參數(shù)Invocation。
@Extension("dubbo")
public interface Protocol {
int getDefaultPort();
//對于服務(wù)提供端,將本地執(zhí)行類的Invoker通過協(xié)議暴漏給外部
//外部可以通過協(xié)議發(fā)送執(zhí)行參數(shù)Invocation,然后交給本地Invoker來執(zhí)行
@Adaptive
<T> Exporter<T> export(Invoker<T> invoker) throws RpcException;
//這個(gè)是針對服務(wù)消費(fèi)端的,服務(wù)消費(fèi)者從注冊中心獲取服務(wù)提供者發(fā)布的服務(wù)信息
//通過服務(wù)信息得知服務(wù)提供者使用的協(xié)議,然后服務(wù)消費(fèi)者仍然使用該協(xié)議構(gòu)造一個(gè)Invoker。這個(gè)Invoker是遠(yuǎn)程通信類的Invoker。
//執(zhí)行時(shí),需要將執(zhí)行信息通過指定協(xié)議發(fā)送給服務(wù)提供者,服務(wù)提供者接收到參數(shù)Invocation,然后交給服務(wù)提供者的本地Invoker來執(zhí)行
@Adaptive
<T> Invoker<T> refer(Class<T> type, URL url) throws RpcException;
void destroy();
}
關(guān)于RegistryProtocol和DubboProtocol的疑惑
以下是官方文檔說明:
暴露服務(wù):
(1) 只暴露服務(wù)端口:
在沒有注冊中心,直接暴露提供者的情況下,即:
<dubbo:service regisrty="N/A" /> or <dubbo:registry address="N/A" />
ServiceConfig解析出的URL的格式為:
dubbo://service-host/com.foo.FooService?version=1.0.0
基于擴(kuò)展點(diǎn)的Adaptive機(jī)制,通過URL的"dubbo://"協(xié)議頭識別,直接調(diào)用DubboProtocol的export()方法,打開服務(wù)端口。
(2) 向注冊中心暴露服務(wù):
在有注冊中心,需要注冊提供者地址的情況下,即:
<dubbo:registry address="zookeeper://10.20.153.10:2181" />
ServiceConfig解析出的URL的格式為:
registry://registry-host/com.alibaba.dubbo.registry.RegistryService?export=URL.encode("dubbo://service-host/com.foo.FooService?version=1.0.0")
基于擴(kuò)展點(diǎn)的Adaptive機(jī)制,通過URL的"registry://"協(xié)議頭識別,就會調(diào)用RegistryProtocol的export()方法,將export參數(shù)中的提供者URL,先注冊到注冊中心,再重新傳給Protocol擴(kuò)展點(diǎn)進(jìn)行暴露:
dubbo://service-host/com.foo.FooService?version=1.0.0
基于擴(kuò)展點(diǎn)的Adaptive機(jī)制,通過提供者URL的"dubbo://"協(xié)議頭識別,就會調(diào)用DubboProtocol的export()方法,打開服務(wù)端口。
RegistryProtocol,注冊中心協(xié)議集成,裝飾真正暴露引用服務(wù)的協(xié)議,增強(qiáng)注冊發(fā)布功能。
ServiceConfig中的protocol是被多層裝飾的Protocol,是DubboProtocol+RegistryProtocol+ProtocolListenerWrapper+ProtocolFilterWrapper。
- ProtocolFilterWrapper負(fù)責(zé)初始化invoker所有的Filter。
- ProtocolListenerWrapper負(fù)責(zé)初始化暴露或引用服務(wù)的監(jiān)聽器。
- RegistryProtocol負(fù)責(zé)注冊服務(wù)到注冊中心和向注冊中心訂閱服務(wù)。
- DubboProtocol負(fù)責(zé)服務(wù)的具體暴露與引用,也負(fù)責(zé)網(wǎng)絡(luò)傳輸層,信息交換層的初始化,以及底層NIO框架的初始化。
Exporter
負(fù)責(zé)invoker的生命周期,包含一個(gè)Invoker對象,可以撤銷服務(wù)。
Exchanger
負(fù)責(zé)數(shù)據(jù)交換和網(wǎng)絡(luò)通信的組件。每個(gè)Invoker都維護(hù)了一個(gè)ExchangeClient的 引用,并通過它和遠(yuǎn)端server進(jìn)行通信。