摘要:整體流程以調(diào)試來演示服務(wù)的發(fā)布流程。暴露遠程服務(wù)假如服務(wù)沒有配置了屬性,或者配置了但是值不是,就會執(zhí)行遠程暴露。封裝了一個服務(wù)的相關(guān)信息,是一個服務(wù)可執(zhí)行體。是一個服務(wù)域,他是引用和暴露的主要入口,它負責(zé)的生命周期管理。
整體流程以調(diào)試 om.alibaba.dubbo.demo.provider.DemoProvider來演示dubbo服務(wù)的發(fā)布流程。
1、啟動Spring容器參照dubbo容器的啟動, https://segmentfault.com/a/11... 文章描述
1.1、解析xml文件dubbo的xml自定義標簽,都是基于spring提供NamespaceHandlerSupport機制來完成的。
ServiceBean加載和初始化
public class DubboNamespaceHandler extends NamespaceHandlerSupport { static { Version.checkDuplicate(DubboNamespaceHandler.class); } public void init() { registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); } }
xml文件->ServiceBean.class 這就是spring的NamespaceHandlerSupport做的事情。
spring容器在初始化之后,會廣播ContextRefreshedEvent事件,ServiceBean實現(xiàn)了ApplicationListener接口,在執(zhí)行onApplicationEvent時,啟動了export方法,開啟 服務(wù)發(fā)布流程。
public void onApplicationEvent(ApplicationEvent event) { if (ContextRefreshedEvent.class.getName().equals(event.getClass().getName())) { if (isDelay() && !isExported() && !isUnexported()) { if (logger.isInfoEnabled()) { logger.info("The service ready on spring started. service: " + getInterface()); } export(); } } }
--export()
-----doExport()
-------doExportUrls()
---------loadRegistries()加載注冊中心,可以有多個
---------doExportUrlsFor1Protocol()
-----------exportLocal(url) 本地暴露服務(wù)
-----------protocol.export(invoker) 遠程暴露服務(wù)
假如服務(wù)沒有配置了scope屬性,或者配置了但是值不是”remote“,就會執(zhí)行本地暴露。自同一個jvm內(nèi)部,調(diào)用本jvm中存在的服務(wù),就可以直接調(diào)用,而不需要走網(wǎng)絡(luò),減少響應(yīng)時間。
private void exportLocal(URL url) { if (!Constants.LOCAL_PROTOCOL.equalsIgnoreCase(url.getProtocol())) { URL local = URL.valueOf(url.toFullString()) .setProtocol(Constants.LOCAL_PROTOCOL) .setHost(NetUtils.LOCALHOST) .setPort(0); Exporter> exporter = protocol.export( proxyFactory.getInvoker(ref, (Class) interfaceClass, local) ); exporters.add(exporter); logger.info("Export dubbo service " + interfaceClass.getName() + " to local registry"); } }
第一步:設(shè)置URL中的參數(shù),
protocal:injvm
host:127.0.0.1
port:0
url的協(xié)議已經(jīng)從dubbo變成了injvm
第二步:將ref【interfaceClass的實現(xiàn)類】包裝成一個Wrapper,并返回一個Invoker
JavassistProxyFactory#getInvoker
publicInvoker getInvoker(T proxy, Class type, URL url) { // TODO Wrapper類不能正確處理帶$的類名 final Wrapper wrapper = Wrapper.getWrapper(proxy.getClass().getName().indexOf("$") < 0 ? proxy.getClass() : type); return new AbstractProxyInvoker (proxy, type, url) { @Override protected Object doInvoke(T proxy, String methodName, Class>[] parameterTypes, Object[] arguments) throws Throwable { return wrapper.invokeMethod(proxy, methodName, parameterTypes, arguments); } }; }
第三步:根據(jù)injvm協(xié)議找到【這個是通過dubbo的Adaptive標簽來動態(tài)決定的】InJvmProtocol,并執(zhí)行他的export方法,裝載Invoker。
第四步:將export放到該服務(wù)的exporters集合中。
本地暴露,不需要啟動類似netty的服務(wù)器,也不需要注冊zookeeper。
假如服務(wù)沒有配置了scope屬性,或者配置了但是值不是”local“,就會執(zhí)行遠程暴露。
if (!Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope)) { if (logger.isInfoEnabled()) { logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); } if (registryURLs != null && registryURLs.size() > 0 && url.getParameter("register", true)) { for (URL registryURL : registryURLs) { url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic")); URL monitorUrl = loadMonitor(registryURL); if (monitorUrl != null) { url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString()); } if (logger.isInfoEnabled()) { logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL); } Invoker> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); Exporter> exporter = protocol.export(invoker); exporters.add(exporter); } } else { Invoker> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); Exporter> exporter = protocol.export(invoker); exporters.add(exporter); }
下面,我們來一步一步調(diào)試遠程暴露都做了什么事情。
通過代理拿到Invoker,這一步就不細說了,和injvm的協(xié)議是一樣的。
核心看著段代碼:Exporter> exporter = protocol.export(invoker);
protocol是一個Protocol$Adaptie類,是dubbo動態(tài)生成的對象。
如何獲得動態(tài)生成的類的源碼:將日志級別調(diào)測DEBUG,在控制臺中拷貝出打印的源碼,然后new相同的package、相同的java文件,就可以進去調(diào)試了。
看一下通過前面一些列操作之后,組裝的Invoker對象
當前協(xié)議值為
我們再看一下export的值為
因為此時Invoker的協(xié)議為registry,所以會執(zhí)行
package com.alibaba.dubbo.rpc; import com.alibaba.dubbo.common.extension.ExtensionLoader; public class Protocol$Adpative implements com.alibaba.dubbo.rpc.Protocol { 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!"); } public com.alibaba.dubbo.rpc.Exporter export(com.alibaba.dubbo.rpc.Invoker arg0) throws com.alibaba.dubbo.rpc.RpcException { 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.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.export(arg0); } public com.alibaba.dubbo.rpc.Invoker refer(java.lang.Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException { 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); } }
從拿到的Extension的值,我們就能驗證,dubbo的AOP原理。請戳鏈接https://segmentfault.com/a/11... 這個里面講了為什么我們拿的是 RegistryProtocol,但是實際上確的拿到的是經(jīng)過兩個wrapper包裝的對象的原因了?!
經(jīng)過兩次ProtocolListenerWrapper、ProtocolFilterWrapper的export方法之后,來到R
RegistryProtocol的export方法,這個是一個非常核心的方法。
]
doLocalExport方法里面,將從Invoker里面的拿到了export的值,重新構(gòu)造了一個InvokerDelegete對象,這個時候又會執(zhí)行Protocol$Adpative#export方法,這個時候Invoker的Url屬性的協(xié)議已經(jīng)是dubbo了。所以會拿到DubboProtocal,并執(zhí)行export
DubboProtocol#export方法,發(fā)現(xiàn)有expoterMap屬性的key是 com.alibaba.dubbo.demo.DemoService:20880 服務(wù)名:協(xié)議端口號。
從上圖中,看到了openServer的方法了 <~.~> ??匆幌氯?yún)RL如下,該值與上面一直圖中的export的屬性值一模一樣。
【忽略ip、pid、timestamp字段,因為不是同一次調(diào)試的?!?br>
屬性serverMap存儲了相應(yīng)的server,ip:prot為key。因此,假如我們只配置一個協(xié)議端口,createServer只會執(zhí)行一次。
執(zhí)行Exchangers.bind(url, requestHandler) 就是為拿到 一個 ExchangeServer
]
在Exchangers里面, 執(zhí)行g(shù)etExchanger(url)拿到 HeaderExchanger執(zhí)行bind方法
在HeaderExchanger里面,執(zhí)行Transporters#bind也是為了拿到一個
在Transporters#bind里面,執(zhí)行g(shù)etTransporter()拿到默認的Transporter 即NettyTransporter。
在NettyTransporter啟動一個NettyServer
netty服務(wù)器在父類的構(gòu)造函數(shù)中被調(diào)用。
至此一個netty服務(wù)器就啟動起來了。
大致完成的事情就是下圖中紅框框框起來的部分。
實現(xiàn)了所有服務(wù)接口的的透明化代理。有兩個方法,
getInvoker 服務(wù)端使用,將實現(xiàn)類封裝成一個Invoker。
getProxy 客戶端使用,創(chuàng)建接口的代理對象。
封裝了一個服務(wù)的相關(guān)信息,是一個服務(wù)可執(zhí)行體。
4.2、Invocation是會話域,它持有調(diào)用過程中的變量,比如方法名,參數(shù)等。
4.3、ProtocolProtocol是一個服務(wù)域,他是Invoker引用和暴露的主要入口,它負責(zé)Invoker的生命周期管理。
4.4、Exporter具體執(zhí)行Invoker的生命周期
4.5、Exchange封裝請求響應(yīng)模式,同步轉(zhuǎn)異步
4.6、Transporttransport 網(wǎng)絡(luò)傳輸層,抽象mina、netty的統(tǒng)一接口
..]
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://systransis.cn/yun/69175.html
摘要:根據(jù)的值,進行服務(wù)暴露。如果配置為則不暴露,如果服務(wù)未配置成,則本地暴露如果未配置成,則暴露遠程服務(wù)。提供者向注冊中心訂閱所有注冊服務(wù)當注冊中心有此服務(wù)的覆蓋配置注冊進來時,推送消息給提供者,重新暴露服務(wù),這由管理頁面完成。 概覽 dubbo暴露服務(wù)有兩種情況,一種是設(shè)置了延遲暴露(比如delay=5000),另外一種是沒有設(shè)置延遲暴露或者延遲設(shè)置為-1(delay=-1): 設(shè)置了...
摘要:將標記為服務(wù),使用對象來提供具體的服務(wù)。這整個過程算是該類的典型的執(zhí)行過程。從上面得知服務(wù)發(fā)布的第一二個過程獲取注冊中心信息和協(xié)議信息。對于端來說,上述服務(wù)發(fā)布的第步中要解決的問題是根據(jù)指定協(xié)議向注冊中心注冊服務(wù)。 showImg(https://segmentfault.com/img/remote/1460000015274828?w=646&h=413); 上圖是服務(wù)提供者暴露服...
摘要:啟動容器,加載,運行服務(wù)提供者。服務(wù)提供者在啟動時,在注冊中心發(fā)布注冊自己提供的服務(wù)。注冊中心返回服務(wù)提供者地址列表給消費者,如果有變更,注冊中心將基于長連接推送變更數(shù)據(jù)給消費者。 一 為什么需要 dubbo 很多時候,其實我們使用這個技術(shù)的時候,可能都是因為項目需要,所以,我們就用了,但是,至于為什么我們需要用到這個技術(shù),可能自身并不是很了解的,但是,其實了解技術(shù)的來由及背景知識,對...
摘要:如果你想查看運行時模塊的加載過程輸出結(jié)果表示為模塊,由于我限制了不再往下輸出了,而我們模塊又沒有別的額外依賴,所以僅有這行輸出。 jdk9模塊快速入門 列出自帶模塊:java --list-modulesmac多版本jdk共存:http://adolphor.com/blog/2016...模塊規(guī)則示意圖:showImg(https://segmentfault.com/img/bVb...
摘要:都是持久化節(jié)點,服務(wù)信息是零時節(jié)點,主要是為了監(jiān)聽服務(wù)提供方的是否斷開連接,做出相應(yīng)處理。訂閱節(jié)點紅框框里面主要做的時候就是創(chuàng)建服務(wù)對應(yīng)的節(jié)點創(chuàng)建節(jié)點持久型節(jié)點并監(jiān)聽如下節(jié)點創(chuàng)建一個的監(jiān)聽器對服務(wù)提供者的節(jié)點配置監(jiān)聽器。 接著上一篇繼續(xù)看。上一篇服務(wù)暴露已經(jīng)講完RegistryProtocol#doLocalExport的方法了。下面將服務(wù)是如何被寫到zookeeper上的。showI...
閱讀 3490·2021-11-19 09:40
閱讀 1506·2021-10-13 09:41
閱讀 2682·2021-09-29 09:35
閱讀 2728·2021-09-23 11:21
閱讀 1722·2021-09-09 11:56
閱讀 846·2019-08-30 15:53
閱讀 854·2019-08-30 15:52
閱讀 609·2019-08-30 12:47