dubbo Protocol實現剖析


title: dubbo Protocol實現剖析
date: 2018-09-09 19:10:07
tags:
---
2.6.3版本,以前讀的是2.4.9版本
本篇主要闡述dubbo rpc的com.alibaba.dubbo.rpc.Protocol的實現,包括做用,用法,原理等等。html

類與接口關係以及其裝配

根據類與接口關係文檔能夠看到Protocol實現關係以下:
264 Protocol
--264.1 QosProtocolWrapper
--264.2 RegistryProtocol
--264.3 AbstractProtocol
----264.3.1 AbstractProxyProtocol
------264.3.1.1 HessianProtocol
------264.3.1.2 HttpProtocol
------264.3.1.3 RestProtocol
------264.3.1.4 RmiProtocol
------264.3.1.5 WebServiceProtocol
----264.3.2 DubboProtocol
----264.3.3 InjvmProtocol
----264.3.4 MemcachedProtocol
----264.3.5 RedisProtocol
----264.3.6 ThriftProtocol
----264.3.7 MockProtocol
--264.4 InjvmProtocol
--264.5 ProtocolFilterWrapper
--264.6 ProtocolListenerWrapper
默認會用到 QosProtocolWrapper RegistryProtocol(若是有註冊中心) DubboProtocol InjvmProtocol ProtocolFilterWrapper ProtocolListenerWrapper。
java

在META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol中作了裝配
filter=com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
listener=com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper
mock=com.alibaba.dubbo.rpc.support.MockProtocol
dubbo=com.alibaba.dubbo.rpc.protocol.dubbo.DubboProtocol
injvm=com.alibaba.dubbo.rpc.protocol.injvm.InjvmProtocol
rmi=com.alibaba.dubbo.rpc.protocol.rmi.RmiProtocol
hessian=com.alibaba.dubbo.rpc.protocol.hessian.HessianProtocol
com.alibaba.dubbo.rpc.protocol.http.HttpProtocol
com.alibaba.dubbo.rpc.protocol.webservice.WebServiceProtocol
thrift=com.alibaba.dubbo.rpc.protocol.thrift.ThriftProtocol
memcached=com.alibaba.dubbo.rpc.protocol.memcached.MemcachedProtocol
redis=com.alibaba.dubbo.rpc.protocol.redis.RedisProtocol
rest=com.alibaba.dubbo.rpc.protocol.rest.RestProtocol
registry=com.alibaba.dubbo.registry.integration.RegistryProtocol
qos=com.alibaba.dubbo.qos.protocol.QosProtocolWrapperweb

擴展點機制createAdaptiveExtensionClass

要看懂protocol的實例化與export過程最好先了解下擴展點機制,前面有文章寫過。 此處要須要注意ExtensionLoader類的下面代碼:redis

private Class<?> createAdaptiveExtensionClass() {
    String code = createAdaptiveExtensionClassCode();
    ClassLoader classLoader = findClassLoader();
    com.alibaba.dubbo.common.compiler.Compiler compiler = ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.common.compiler.Compiler.class).getAdaptiveExtension();
    return compiler.compile(code, classLoader);
}

再協議初始化與export時會動態生成的兩個類的代碼: Protocol$Adaptive,ProxyFactory$Adaptive,看上面code變量就是生成的具體代碼了。緩存

protocol及其wrap之間的關係

在擴展點機制com.alibaba.dubbo.common.extension.ExtensionLoader.createExtension(String)中:
在建立擴展實例時,會同時對他的wrap類的實例進行建立,代碼細節以下:app

T instance = (T) EXTENSION_INSTANCES.get(clazz);
if (instance == null) {
    EXTENSION_INSTANCES.putIfAbsent(clazz, clazz.newInstance());
    instance = (T) EXTENSION_INSTANCES.get(clazz);
}
injectExtension(instance);// 此時是DubboProtocol示例
Set<Class<?>> wrapperClasses = cachedWrapperClasses;
if (wrapperClasses != null && !wrapperClasses.isEmpty()) {
    for (Class<?> wrapperClass : wrapperClasses) {
        instance = injectExtension((T) wrapperClass.getConstructor(type).newInstance(instance));// 此處建立wrap實例
    }
}

實際應用中,以dubbo協議這個擴展爲例,當創上述代碼建了DubboProtocol協議的instance,此時是DubboProtocol實例,接下來對cachedWrapperClasses進行for循環,建立對應的wrap類,這些wrap類都有一個以Protocol類型的參數,那麼建立wrap實例的時候,會把以前產生的Protocol實例傳遞給他。
此例,cachedWrapperClasses裏的數據來自於META-INF/dubbo/internal/com.alibaba.dubbo.rpc.Protocol文件中配置的是wrap的擴展,
此處是:
class com.alibaba.dubbo.rpc.protocol.ProtocolFilterWrapper
class com.alibaba.dubbo.rpc.protocol.ProtocolListenerWrapper,
class com.alibaba.dubbo.qos.protocol.QosProtocolWrapper,
(怎麼從配置文件中識別是不是wrap類?答案是 只要這個QosProtocolWrapper類有類型爲Protocol的構造函數存在,就當QosProtocolWrapper是wrap類。)
那麼此處便會依次構造這三個類。 這個三個類的構造順序不固定,由於用的上面createExtension代碼中cachedWrapperClasses是ConcurrentHashSet維護的。負載均衡

export機制

總體構造與export順序即觸發時機的解釋

此處是以provider端爲例
DubboProtocol. () line: 162 // final int defaultPort = ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(name).getDefaultPort(); ServiceConfig取默認端口值 ,用在沒有對dubbo:protocol標籤配置端口時 dom

ProtocolListenerWrapper. (Protocol) line: 41 // 上面實例化DubboProtocol實例時,會級聯實例化其關聯的wrap類,級聯的wrap類是怎麼獲取的 參見 級聯的wrap類是怎麼獲取部分
QosProtocolWrapper. (Protocol) line: 39 // 上面實例化DubboProtocol實例時,會級聯實例化其關聯的wrap類
ProtocolFilterWrapper. (Protocol) line: 40 // 上面實例化DubboProtocol實例時,會級聯實例化其關聯的wrap類
jvm

ProtocolListenerWrapper. (Protocol) line: 41 // ServiceConfig 對本端須要暴露的服務 作exportLocal(url) 時,會export injvm協議,那麼這時又會級聯實例化 其關聯的wrap類,一樣是3個 :
QosProtocolWrapper. (Protocol) line: 39 // ServiceConfig 作exportLocal(url) 時,會export injvm協議,級聯實例化,同上
ProtocolFilterWrapper. (Protocol) line: 40 // ServiceConfig 作exportLocal(url) 時,會export injvm協議,級聯實例化,同上
分佈式

ProtocolFilterWrapper.export(Invoker ) line: 97 // 對上面作exportLocal時級聯建立出來的wrap類調用export方法 ,而這三個wrap類又會對他內部的protocol作export調用 就是下面這幾個
QosProtocolWrapper.export(Invoker ) line: 52 // 被上行這個級聯export的
ProtocolListenerWrapper.export(Invoker ) line: 54 // 被上行這個級聯export的
InjvmProtocol.export(Invoker ) line: 86 // 被上行這個級聯export的 結果是建立一個InjvmExporter實例, 也便是說export多是調本身級聯的protocol,也多是建立一個exporter

RegistryProtocol. () line: 73
//

Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
DelegateProviderMetaDataInvoker wrapperInvoker = new DelegateProviderMetaDataInvoker(invoker, this);

Exporter<?> exporter = protocol.export(wrapperInvoker);

針對 registryURL 先建立invoker,再用這個invoker建立wrapperInvoker,再用wrapperInvoker作exporter。

ProtocolListenerWrapper. (Protocol) line: 41 // 被RegistryProtocol級聯建立
QosProtocolWrapper. (Protocol) line: 39 // 被RegistryProtocol級聯建立
ProtocolFilterWrapper. (Protocol) line: 40 // 被RegistryProtocol級聯建立
----
ProtocolFilterWrapper.export(Invoker ) line: 97 // RegistryProtocol作export時先級聯的wrap實例進行export 這三個wrap中 對REGISTRY_PROTOCOL作了特殊邏輯處理。 詳細請參見代碼
QosProtocolWrapper.export(Invoker ) line: 52 // 同上
ProtocolListenerWrapper.export(Invoker ) line: 54 // 同上
RegistryProtocol.export(Invoker ) line: 132 // 同上 最終export出了DestroyableExporter 就是對應Registry協議的exporter
ProtocolFilterWrapper.export(Invoker ) line: 97 // 上行RegistryProtocol export時先作doLocalExport,doLocalExport時把provider的url與其對應的exporter作一個緩存(若是緩存中沒有,此時要export,並把對provider export出來的結果作ExporterChangeableWrapper的包裝,最後返回ExporterChangeableWrapper實例。這行的這個export就是被這個觸發的,詳細的能夠參見他代碼)

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) {
            final Invoker<?> invokerDelegete = new InvokerDelegete<T>(originInvoker, getProviderUrl(originInvoker));
            exporter = new ExporterChangeableWrapper<T>((Exporter<T>) protocol.export(invokerDelegete), originInvoker);
            bounds.put(key, exporter);
        }
    }
}

QosProtocolWrapper.export(Invoker ) line: 52 // 上一行級聯觸發
ProtocolListenerWrapper.export(Invoker ) line: 54 // 上上一行級聯觸發
DubboProtocol.export(Invoker ) line: 229 // 上上上一行級聯觸發

ProtocolFilterWrapper

rpc過濾器鏈的組織建立者。

若是是註冊中心 級聯export的,直接級聯wrap的protocol進行export,不作其餘處理。
若是是非註冊中心 級聯export的,好比injvm dubbo這種協議級聯的,作buildInvokerChain 構建調用中過濾器鏈的處理。 就是前面博客介紹的rpc過濾器com.alibaba.dubbo.rpc.Filter的實現實例串起來。經過循環加內部類訪問外圍變量的方式組成的鏈。貼下關鍵代碼:

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 Result invoke(Invocation invocation) throws RpcException {
            return filter.invoke(next, invocation);
        }
// ......
    };
}

next與last配合使用造成了鏈。

QosProtocolWrapper

若是是非註冊中心 級聯export的,直接級聯wrap的protocol進行export,不作其餘處理
若是是註冊中心 級聯export的,啓動QoS server。參見com.alibaba.dubbo.qos.server.Server。

ProtocolListenerWrapper

用於服務export時候監聽機制的插入。

若是是註冊中心 級聯export的,直接級聯wrap的protocol進行export,不作其餘處理。
若是是非註冊中心 級聯export的,好比injvm dubbo這種協議級聯的,先級聯wrap的protocol進行export,再建立ListenerExporterWrapper實例。 主要是爲了讓ExporterListener監聽器插入進來。

RegistryProtocol

對應註冊中心協議

先作本地的export
而後根據配置將 registry://127.0.0.1:2174/com.alibaba.dubbo.registry.RegistryService?application=hello-...置換成具體的註冊中心協議地址,如: zookeeper://127.0.0.1:2174/com.alibaba.dubbo.registry.RegistryService?application=hello-world-...
根據註冊中心協議地址 或者Registry-->ZookeeperRegistry的實例。
判斷是不是非延遲暴露的,就開始註冊,比較簡單:

zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));

能夠經過其父類FailbackRegistry看到,支持失敗重試,由一個定時調度線程重試。

this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
    @Override
    public void run() {
        // Check and connect to the registry
        try {
            retry();
        } catch (Throwable t) { // Defensive fault tolerance
            logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
        }
    }
}, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);

Failback是一種容錯,詳細的能夠參見 常見容錯機制。
註冊以後是對OverrideListener 的處理。
該export返回的是DestroyableExporter實例,顧名思義是能夠銷燬的exoprter,在其unexport裏作了文章,移除註冊,移除export。

ref機制

先看下consumer端發起調用時的鏈路流程:

+---------------------------+            +---------------------------+            +---------------------------+              
 |      helloService         |            |      proxy                |            |  InvokerInvocationHandler |              
 |      sayHello             +----------> |      sayHello             +----------> |  invoke                   |              
 |                           |            |                           |            |  proxy method args        |              
 +---------------------------+            +---------------------------+            +-------------+-------------+              
                                                                                                 |                            
                                                                                                 |                            
                                                                                  +---------------------------------+         
                                                                                  |              |                  |         
                                                                                  | +------------v--------------+   |         
                                                                                  | |  MockClusterInvoker       |   |         
                                                                                  | |  invoke                   |   |         
                                                                                  | |                           |   |         
                                                                                  | +------------+--------------+   |         
                                                                                  |              |                  |         
                                                                                  |              |                  |         
                                                                                  |              |                  |         
 +---------------------------+            +---------------------------+           | +------------v--------------+   |         
 | Router                    |            | RegistryDirectory         |           | |  FailoverClusterInvoker   |   |         
 | route                     | <----------+ list                      | <-----------+  invoke                   |   |         
 | MockInVokersSelector      |            | INVOCATION-->List INVOKER |           | |                           |   |         
 +------------+--------------+            +---------------------------+           | +---------------------------+   |         
              |                                                                   |                                 |         
              |                                                                   +---------------------------------+         
              |                                                                 cluster invoke,分佈式調用容錯機制也是在這作                      
              |                                                                                                               
              |                                                                                                               
              |                                                                                                               
              |                                                                                                               
              |                                                                                                               
+-------------v-------------+             +---------------------------+             +---------------------------+             
|  RandomLoadBalance        |             |InvokerDelegate            |             | ListenerInvokerWrap       |             
|  select                   +-----------> |invoke                     +-----------> | invoke                    |             
|  List INVOKER-->INVOKER   |             |                           |             |                           |             
+---------------------------+             +---------------------------+             +---------------------------+

RegistryProtocol 註冊中心協議 對接口協議接口約束,不論何種註冊中心都走這個實現。
RegistryDirectory 提供由服務目錄查找出真正能提供服務的全部提供方
Registry 各個註冊中心的實現的接口約束,具體實現有 RedisRegistry ZookeeperRegistry MulticastRegistry DubboRegistry。 主要完成註冊這個邏輯。 就是把本身的信息寫入註冊中心。
Cluster 多服務提供方的一個封裝 。他們負責建立多服務調用者實例。 好比FailoverCluster負責建立FailoverClusterInvoker實例。Cluster的實現是以分佈式容錯機制Failover,Failfast,Failback等這些維度進行。 對應invoker也是按照這個維度劃分,好比FailbackClusterInvoker,FailfastClusterInvoker,BroadcastClusterInvoker等等。
Router 路由機制
LoadBalance 負載均衡機制
Invoker 調用者。 按分佈式容錯機制的維度進行劃分了多個實現,與 Cluster實現一一對應。
基本上描述了 由consumer的ref類-->proxy-->InvokerInvocationHandler-->clusterinvoker-->服務目錄查找-->路由機制-->負載均衡機制-->調用委託-->調用監聽器。

Invoker的實現結構:
--61.1 Invoker
----61.1.1 DelegateProviderMetaDataInvoker
----61.1.2 ConsumerInvokerWrapper
----61.1.3 ProviderInvokerWrapper
----61.1.4 AbstractClusterInvoker
------61.1.4.1 AvailableClusterInvoker
------61.1.4.2 BroadcastClusterInvoker
------61.1.4.3 FailbackClusterInvoker
------61.1.4.4 FailfastClusterInvoker
------61.1.4.5 FailoverClusterInvoker
------61.1.4.6 FailsafeClusterInvoker
------61.1.4.7 ForkingClusterInvoker
----61.1.5 MergeableClusterInvoker
----61.1.6 MockClusterInvoker
----61.1.7 ListenerInvokerWrapper
----61.1.8 AbstractInvoker
------61.1.8.1 ChannelWrappedInvoker
------61.1.8.2 DubboInvoker
------61.1.8.3 InjvmInvoker
------61.1.8.4 ThriftInvoker
----61.1.9 InvokerWrapper
----61.1.10 AbstractProxyInvoker
----61.1.11 DelegateInvoker
----61.1.12 MockInvoker

RegistryProtocol

ref完成的邏輯是:
建立服務目錄實例 RegistryDirectory
建立ClusterInvoker,默認是FailoverClusterInvoker..

其餘

ProtocolFilterWrapper, QosProtocolWrapper, ProtocolListenerWrapper的ref邏輯與export基本相同,再也不贅述。

相關文章
相關標籤/搜索