摘要:整體流程以調試來演示服務的發布流程。暴露遠程服務假如服務沒有配置了屬性,或者配置了但是值不是,就會執行遠程暴露。封裝了一個服務的相關信息,是一個服務可執行體。是一個服務域,他是引用和暴露的主要入口,它負責的生命周期管理。
整體流程以調試 om.alibaba.dubbo.demo.provider.DemoProvider來演示dubbo服務的發布流程。
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實現了ApplicationListener接口,在執行onApplicationEvent時,啟動了export方法,開啟 服務發布流程。
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) 本地暴露服務
-----------protocol.export(invoker) 遠程暴露服務
假如服務沒有配置了scope屬性,或者配置了但是值不是”remote“,就會執行本地暴露。自同一個jvm內部,調用本jvm中存在的服務,就可以直接調用,而不需要走網絡,減少響應時間。
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"); } }
第一步:設置URL中的參數,
protocal:injvm
host:127.0.0.1
port:0
url的協議已經從dubbo變成了injvm
第二步:將ref【interfaceClass的實現類】包裝成一個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); } }; }
第三步:根據injvm協議找到【這個是通過dubbo的Adaptive標簽來動態決定的】InJvmProtocol,并執行他的export方法,裝載Invoker。
第四步:將export放到該服務的exporters集合中。
本地暴露,不需要啟動類似netty的服務器,也不需要注冊zookeeper。
假如服務沒有配置了scope屬性,或者配置了但是值不是”local“,就會執行遠程暴露。
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); }
下面,我們來一步一步調試遠程暴露都做了什么事情。
通過代理拿到Invoker,這一步就不細說了,和injvm的協議是一樣的。
核心看著段代碼:Exporter> exporter = protocol.export(invoker);
protocol是一個Protocol$Adaptie類,是dubbo動態生成的對象。
如何獲得動態生成的類的源碼:將日志級別調測DEBUG,在控制臺中拷貝出打印的源碼,然后new相同的package、相同的java文件,就可以進去調試了。
看一下通過前面一些列操作之后,組裝的Invoker對象
當前協議值為
我們再看一下export的值為
因為此時Invoker的協議為registry,所以會執行
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,但是實際上確的拿到的是經過兩個wrapper包裝的對象的原因了?!
經過兩次ProtocolListenerWrapper、ProtocolFilterWrapper的export方法之后,來到R
RegistryProtocol的export方法,這個是一個非常核心的方法。
]
doLocalExport方法里面,將從Invoker里面的拿到了export的值,重新構造了一個InvokerDelegete對象,這個時候又會執行Protocol$Adpative#export方法,這個時候Invoker的Url屬性的協議已經是dubbo了。所以會拿到DubboProtocal,并執行export
DubboProtocol#export方法,發現有expoterMap屬性的key是 com.alibaba.dubbo.demo.DemoService:20880 服務名:協議端口號。
從上圖中,看到了openServer的方法了 <~.~> 。看一下入參URL如下,該值與上面一直圖中的export的屬性值一模一樣。
【忽略ip、pid、timestamp字段,因為不是同一次調試的。】
屬性serverMap存儲了相應的server,ip:prot為key。因此,假如我們只配置一個協議端口,createServer只會執行一次。
執行Exchangers.bind(url, requestHandler) 就是為拿到 一個 ExchangeServer
]
在Exchangers里面, 執行getExchanger(url)拿到 HeaderExchanger執行bind方法
在HeaderExchanger里面,執行Transporters#bind也是為了拿到一個
在Transporters#bind里面,執行getTransporter()拿到默認的Transporter 即NettyTransporter。
在NettyTransporter啟動一個NettyServer
netty服務器在父類的構造函數中被調用。
至此一個netty服務器就啟動起來了。
大致完成的事情就是下圖中紅框框框起來的部分。
實現了所有服務接口的的透明化代理。有兩個方法,
getInvoker 服務端使用,將實現類封裝成一個Invoker。
getProxy 客戶端使用,創建接口的代理對象。
封裝了一個服務的相關信息,是一個服務可執行體。
4.2、Invocation是會話域,它持有調用過程中的變量,比如方法名,參數等。
4.3、ProtocolProtocol是一個服務域,他是Invoker引用和暴露的主要入口,它負責Invoker的生命周期管理。
4.4、Exporter具體執行Invoker的生命周期
4.5、Exchange封裝請求響應模式,同步轉異步
4.6、Transporttransport 網絡傳輸層,抽象mina、netty的統一接口
..]
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69175.html
摘要:根據的值,進行服務暴露。如果配置為則不暴露,如果服務未配置成,則本地暴露如果未配置成,則暴露遠程服務。提供者向注冊中心訂閱所有注冊服務當注冊中心有此服務的覆蓋配置注冊進來時,推送消息給提供者,重新暴露服務,這由管理頁面完成。 概覽 dubbo暴露服務有兩種情況,一種是設置了延遲暴露(比如delay=5000),另外一種是沒有設置延遲暴露或者延遲設置為-1(delay=-1): 設置了...
摘要:將標記為服務,使用對象來提供具體的服務。這整個過程算是該類的典型的執行過程。從上面得知服務發布的第一二個過程獲取注冊中心信息和協議信息。對于端來說,上述服務發布的第步中要解決的問題是根據指定協議向注冊中心注冊服務。 showImg(https://segmentfault.com/img/remote/1460000015274828?w=646&h=413); 上圖是服務提供者暴露服...
摘要:啟動容器,加載,運行服務提供者。服務提供者在啟動時,在注冊中心發布注冊自己提供的服務。注冊中心返回服務提供者地址列表給消費者,如果有變更,注冊中心將基于長連接推送變更數據給消費者。 一 為什么需要 dubbo 很多時候,其實我們使用這個技術的時候,可能都是因為項目需要,所以,我們就用了,但是,至于為什么我們需要用到這個技術,可能自身并不是很了解的,但是,其實了解技術的來由及背景知識,對...
摘要:如果你想查看運行時模塊的加載過程輸出結果表示為模塊,由于我限制了不再往下輸出了,而我們模塊又沒有別的額外依賴,所以僅有這行輸出。 jdk9模塊快速入門 列出自帶模塊:java --list-modulesmac多版本jdk共存:http://adolphor.com/blog/2016...模塊規則示意圖:showImg(https://segmentfault.com/img/bVb...
摘要:都是持久化節點,服務信息是零時節點,主要是為了監聽服務提供方的是否斷開連接,做出相應處理。訂閱節點紅框框里面主要做的時候就是創建服務對應的節點創建節點持久型節點并監聽如下節點創建一個的監聽器對服務提供者的節點配置監聽器。 接著上一篇繼續看。上一篇服務暴露已經講完RegistryProtocol#doLocalExport的方法了。下面將服務是如何被寫到zookeeper上的。showI...
閱讀 3480·2021-11-19 09:40
閱讀 1496·2021-10-13 09:41
閱讀 2671·2021-09-29 09:35
閱讀 2715·2021-09-23 11:21
閱讀 1703·2021-09-09 11:56
閱讀 836·2019-08-30 15:53
閱讀 848·2019-08-30 15:52
閱讀 604·2019-08-30 12:47