Dubbo中暴露服務(wù)的過程解析

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è)置延遲暴露熟屬性的過程為例。

簡易的暴露流程

  1. 首先將服務(wù)的實(shí)現(xiàn)封裝成一個(gè)Invoker,Invoker中封裝了服務(wù)的實(shí)現(xiàn)類。
  2. 將Invoker封裝成Exporter,并緩存起來,緩存里使用Invoker的url作為key。
  3. 服務(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的步驟簡介

  1. 首先會檢查各種配置信息,填充各種屬性,總之就是保證我在開始暴露服務(wù)之前,所有的東西都準(zhǔn)備好了,并且是正確的。
  2. 加載所有的注冊中心,因?yàn)槲覀儽┞斗?wù)需要注冊到注冊中心中去。
  3. 根據(jù)配置的所有協(xié)議和注冊中心url分別進(jìn)行導(dǎo)出。
  4. 進(jìn)行導(dǎo)出的時(shí)候,又是一波屬性的獲取設(shè)置檢查等操作。
  5. 如果配置的不是remote,則做本地導(dǎo)出。
  6. 如果配置的不是local,則暴露為遠(yuǎn)程服務(wù)。
  7. 不管是本地還是遠(yuǎn)程服務(wù)暴露,首先都會獲取Invoker。
  8. 獲取完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&registry=zookeeper&timestamp=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&timestamp=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&timestamp=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,大概的步驟是:

  1. 根據(jù)上面生成的proxyFactory方法調(diào)用具體的ProxyFactory實(shí)現(xiàn)類的getInvoker方法獲取Invoker。
  2. getInvoker的過程是,首先對實(shí)現(xiàn)類做一個(gè)包裝,生成一個(gè)包裝后的類。
  3. 然后新創(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&timestamp=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處理過程

大概的步驟是:

  1. 經(jīng)過兩個(gè)不用做任何處理的Wrapper類,然后到達(dá)RegistryProtocol中。
  2. 通過具體的協(xié)議導(dǎo)出Invoker為Exporter。
  3. 注冊服務(wù)到注冊中心。
  4. 訂閱注冊中心的服務(wù)。
  5. 生成一個(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&timestamp=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&timestamp=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&registry=zookeeper&timestamp=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&timestamp=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&timestamp=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&timestamp=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)行通信。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點(diǎn),簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

推薦閱讀更多精彩內(nèi)容