Dubbo學習-源碼學習
Dubbo概述
dubbo框架提供多協議遠程調用,服務提供方可以是分布式部署。dubbo框架可以很簡單的幫我們實現微服務。
此處援引官網上圖片
dubbo分為客戶端和服務提供方
服務方將服務註冊到註冊中心
客戶端從註冊中心獲取已註冊服務訪問方式
客戶端通過指定協議訪問服務提供方
根據dubbo架構,源碼分析我們主要切入點是:
- dubbo配置如何生效
- 客戶端如何調用服務
- 註冊的服務如何保證被調用到
- dubbo遠程調用的協議如何工作
針對以上我們來分析Dubbo源碼:
Dubbo源碼是maven管理,工程主要由dubbo-config,dubbo-container,dubbo-filter,dubbo-monitor,dubbo-registry,dubbo-remoting,dubbo-rpc,dubbo-serialization組成。
- dubbo-config :dubbo配置文件相關模塊
- dubbo-container :dubbo依賴的上下文
- dubbo-filter : dubbo支持的請求過濾器
- dubbo-monitor : dubbo監控管理模塊
- dubbo-registry : dubbo使用註冊中心相關的模塊
- dubbo-remoting : dubbo整個遠程交互框架,如何將通道、序列化、通信載體有機整合
- dubbo-rpc : dubbo遠程調用協議實現。
- dubbo-serialization : 遠程通信中序列化實現
dubbo配置加載
dubbo配置是以松耦合的方式嵌入到spring裏面,因此說到dubbo配置加載就必然和spring扯不開關系。
從dubbo配置文件的命名空間說起,我們通常在配置dubbo服務時要在spring配置基礎上添加dubbo的命名空間(xmlns)和模板:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:dubbo="http://code.alibabatech.com/schema/dubbo" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
xmlns:dubbo的值指定了dubbo配置命名空間。
spring中不同的命名空間對應不同的配置解析器,那麽dubbo的命名空間http://code.alibabatech.com/schema/dubbo也就對應著自己的解析器。
spring維護著命名空間和對應解析器的關系,這些對應關系包括spring內置的和第三方定制關系
spring命名空間
spring啟動中解析dubbo xml文件時首先獲取跟節點中的所有xmlns值,根據配置的命名空間從命名空間管理器中獲取該空間對應的解析器對象
命名空間管理器會從classpath下META-INF/下加載並解析所有的所有spring.handlers命名的文件,該文件以鍵值對配置了命名空間和對應解析器。
dubbo的命名spring.handler配置如下:
http\://code.alibabatech.com/schema/dubbo=com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler
路徑:META-INF/dubbo/spring.handler
如此,spring在加載dubbo配置時會調用dubbo命名空間解析器DubboNamespaceHandler來解析配置文件,dubbo的入口也就是DubboNamespaceHandler。
dubbo配置解析處理器(DubboNamespaceHandler)
dubbo配置解析處理器就是命名空間配置的com.alibaba.dubbo.config.spring.schema.DubboNamespaceHandler,該處理器作為dubbo對spring的擴展完成dubbo自定義的配置解析工作。spring的命名空間處理器擴展點是:org.springframework.beans.factory.xml.NamespaceHandlerSupport,DubboNamespaceHandler完成dubbo內部所有解析器初始和加載。解析器包括application節點解析、module節點解析、registry、monitor、provider、consumer、protocol、service、reference、annotation。
這些解析器初始化代碼如下:
1 public void init() { 2 registerBeanDefinitionParser("application", new DubboBeanDefinitionParser(ApplicationConfig.class, true)); 3 registerBeanDefinitionParser("module", new DubboBeanDefinitionParser(ModuleConfig.class, true)); 4 registerBeanDefinitionParser("registry", new DubboBeanDefinitionParser(RegistryConfig.class, true)); 5 registerBeanDefinitionParser("monitor", new DubboBeanDefinitionParser(MonitorConfig.class, true)); 6 registerBeanDefinitionParser("provider", new DubboBeanDefinitionParser(ProviderConfig.class, true)); 7 registerBeanDefinitionParser("consumer", new DubboBeanDefinitionParser(ConsumerConfig.class, true)); 8 registerBeanDefinitionParser("protocol", new DubboBeanDefinitionParser(ProtocolConfig.class, true)); 9 registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true)); 10 registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false)); 11 registerBeanDefinitionParser("annotation", new DubboBeanDefinitionParser(AnnotationBean.class, true)); 12 }
DubboNamespaceHandler做了什麽
spring加載配置文件過程中,讀取到一個xml節點時,會將節點信息傳遞給DubboNamespaceHandler,handler獲取節點的LocalName(在一個命名空間下去掉前綴剩下的節點名 稱),根據名稱從DubboNamespaceHandler獲取對應的Parser,如:LocalName是application,就會獲取到Handler初始化時註冊的application對應的 DubboBeanDefinitionParser對象,並調用該解析器的parse方法將節點轉換成spring的RootBeanDefinition對象,對象保存了創建一個bean對象所需的所有信 息。
Dubbo服務(ServiceBean)
通過registerBeanDefinitionParser("service", new DubboBeanDefinitionParser(ServiceBean.class, true));註冊的DubboBeanDefinitionParser
創建出的RootBeanDefinition最終被spring實例化成ServiceBean,ServiceBean即Dubbo實際的服務實例。實例化結束後,ServiceBean會從spring容器中獲取配置的Provider信 息、application、module、registry、monitor、protocol等配置信息並設置到ServiceBean,設置完成後將服務自身暴露出去,如果本次暴露失敗或者沒有暴露,則在spring容器啟 動完成後會再進行一次暴露。
服務暴露
服務暴露通過調用ServiceBean的export方法,通過註冊中心,最終將服務實例以指定的協議暴露給調用方。
準備暴露路徑
服務暴露前需要組織自己的訪問路徑和協議信息,只有有了這些信息,才能確定以何種協議被訪問,以及通過什麽路徑被訪問。組織訪問信息的方法是服務自身的doExportUrls
ServiceConfig.doExportUrls()
1 private void doExportUrls() { 2 List<URL> registryURLs = loadRegistries(true); 3 for (ProtocolConfig protocolConfig : protocols) { 4 doExportUrlsFor1Protocol(protocolConfig, registryURLs); 5 } 6 }
該方法調用了doExportUrlsFor1Protocol方法將協議和註冊路徑具體的組裝起來,形成dubbo的訪問路徑,將訪問路徑包裝秤可悲遠程調用的調用器invoker。
invoker的生成代碼如下:
1 //如果配置不是local則暴露為遠程服務.(配置為local,則表示只暴露本地服務) 2 if (! Constants.SCOPE_LOCAL.toString().equalsIgnoreCase(scope) ){ 3 if (logger.isInfoEnabled()) { 4 logger.info("Export dubbo service " + interfaceClass.getName() + " to url " + url); 5 } 6 if (registryURLs != null && registryURLs.size() > 0 7 && url.getParameter("register", true)) { 8 for (URL registryURL : registryURLs) { 9 url = url.addParameterIfAbsent("dynamic", registryURL.getParameter("dynamic")); 10 URL monitorUrl = loadMonitor(registryURL); 11 if (monitorUrl != null) { 12 url = url.addParameterAndEncoded(Constants.MONITOR_KEY, monitorUrl.toFullString()); 13 } 14 if (logger.isInfoEnabled()) { 15 logger.info("Register dubbo service " + interfaceClass.getName() + " url " + url + " to registry " + registryURL); 16 } 17 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); 18 19 Exporter<?> exporter = protocol.export(invoker); 20 exporters.add(exporter); 21 } 22 } else { 23 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); 24 25 Exporter<?> exporter = protocol.export(invoker); 26 exporters.add(exporter); 27 } 28 }
Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString()));
參數ref指向的是dubbo:service配置中ref中指定的服務對象,interfaceClass是該服務的接口,第三個參數則是該服務的訪問路徑。
Exporter<?> exporter = protocol.export(invoker); // 最終將invoker暴露
Dubbo學習-源碼學習