Dubbo的啓動主要是發佈服務的過程,起到核心做用的就是ServiceConfig(ServiceConfig就是咱們在Dubbo的配置文件中配置的dubbo:service這些配置項對應的實體類)。服務的啓動初始位置也基本是在這裏,下面咱們來看看具體的實現內容。app
講基本內容前首先理清楚幾個名詞概念:
Invoker:Invoker的概念咱們在動態代理的時候就接觸過,中文的意思大概是執行者,這裏其實能夠理解爲具體方法的執行者。其核心內容大體以下:負載均衡
經過以上的三個方法們就能夠執行到具體的方法而且得到方法的執行結果。經過getUrl得到須要執行的方法具體實現細節,主要是得到具體的ref;其次就是組裝方法的參數信息等等,這些信息在invocation裏面都有封裝;最後經過執行invoke方法觸發具體的方法並返回結果。從這裏能夠看出Invoker是具體方法執行的最後一個守關者,得到了Invoker,就得到了具體接口的代碼,而後執行代理就能夠。jvm
Invoker僅僅是做爲一個代理的門面,其不只能夠表明本地執行Duubo調用的代理,還能夠充當RPC時候的代理,更是能夠將本身包裝成一個多個Invoker聚合而成的代理(主要是處理集羣的一些策略,包括負載均衡和路由等)。ide
Exporter:服務暴露的過程當中會將Invoker轉換成Exporter(暴露者),及Exporter其實包含了Invoker,主要是用於不一樣層面的服務發佈。
其實Dubbo 還有一些比較重要的對象,像Protocol,Exchanger等等。我認爲在這裏直接說明不太合適,因此等到咱們用到以後再開始說明。this
一些基本的屬性:group,version,interfaceName,interfaceClass,timeout等等。咱們凡是能夠在dubbo:service上配置的屬性都在ServiceConfig中能夠找獲得對應的屬性;url
//dubbo對應的服務發佈協議,這裏能夠清楚地看到Dubbo在這裏使用的本身的spi機制,來保證靈活性。(至於SPI機制的具體實現,以後有機會的話會講到,簡單理解就是經過getExtensionLoader得到對應類的擴展類實現類)spa
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();.net
private static final ProxyFactory proxyFactory = ExtensionLoader.getExtensionLoader(ProxyFactory.class).getAdaptiveExtension();線程
private final List<URL> urls = new ArrayList<URL>();
private final List<Exporter<?>> exporters = new ArrayList<Exporter<?>>();代理
對於服務暴露來講,在ServiceConfig裏面的初始方法就是export()方法了,下面咱們從export方法開始來看看:
public synchronized void export() { if (provider != null) { //默認取provider的配置 if (export == null) { export = provider.getExport(); } if (delay == null) { delay = provider.getDelay(); } } //若是export設置爲false的話就直接返回 if (export != null && ! export.booleanValue()) { return; } //若是設置延遲時間的話就延遲指定時間而後進行暴露 if (delay != null && delay > 0) { Thread thread = new Thread(new Runnable() { public void run() { try { Thread.sleep(delay); } catch (Throwable e) { } doExport(); } }); thread.setDaemon(true);//將暴露接口的線程設置爲守護線程 thread.setName("DelayExportServiceThread"); thread.start(); } else { doExport(); //一切暴露的核心還都是要看doExport方法。 } } protected synchronized void doExport() { // 防止服務屢次暴露 // 設置默認的基本屬性 // 針對泛化接口作單獨處理 if (ref instanceof GenericService) { interfaceClass = GenericService.class; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } } else { try {//經過反射初始化接口(interfaceName是實現類的全稱) interfaceClass = Class.forName(interfaceName, true, Thread.currentThread() .getContextClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } //檢查定義的方法是否爲接口中的方法 checkInterfaceAndMethods(interfaceClass, methods); //檢查引用不爲空,而且引用必需實現接口 checkRef(); //若是到這一步的話說明類實現是本身定義的,因此設置generic爲false generic = Boolean.FALSE.toString(); } // 處理Local和Stub代理處理 // 檢查Application,Registry,Protocol的配置狀況 //將配置的屬性綁定到當前對象 appendProperties(this); //針對Local,Stub和Mock進行校驗 //上面的操做主要是作一些檢驗和初始化的操做,沒有涉及到具體的暴露服務邏輯 doExportUrls(); } private void doExportUrls() { //取到註冊中心的URL List<URL> registryURLs = loadRegistries(true); for (ProtocolConfig protocolConfig : protocols) { //根據配置的通訊協議將服務暴露到註冊中心 doExportUrlsFor1Protocol(protocolConfig, registryURLs); } } private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { // 默認採用Dubbo協議 //以後的部分邏輯就是想盡一切辦法取到host //取到端口號(以後的部分關於端口的邏輯就是想盡一切辦法取端口號) // 這個map十分重要,它的意義在於全部存儲全部最終使用到的屬性,咱們知道一個屬性例如timeout,可能在Application,provider,service中都有配置,具體以哪一個爲準,都是這個map處理的事情。 Map<String, String> map = new HashMap<String, String>(); if (anyhost) { //若是此時anyhost爲true的話 map.put(Constants.ANYHOST_KEY, "true"); } // 存儲簡單的服務信息 //將application,module,provider,protocol和service的信息設置到map裏面 //將應用配置的樹勇按照層級存入map中。注意這裏的層級關係,是一層層覆蓋的 即關係爲:ServiceConfig->PrtocolConfig->ProviderConfig->ModuleConfig->ApplicaionConfig //單獨處理好method層級的參數關係 //判斷有沒有配置通配協議 if (ProtocolUtils.isGeneric(generic)) { map.put("generic", generic); map.put("methods", Constants.ANY_VALUE); } else { String revision = Version.getVersion(interfaceClass, version); if (revision != null && revision.length() > 0) { map.put("revision", revision); } //經過包裝類將interfaceClass進行包裝而後取得方法名字,對於wapper包裝器就是將不一樣的類統一化 //參考http://blog.csdn.net/quhongwei_zhanqiu/article/details/41597261理解Wapper String[] methods = Wrapper.getWrapper(interfaceClass).getMethodNames(); if(methods.length == 0) { logger.warn("NO method found in service interface " + interfaceClass.getName()); map.put("methods", Constants.ANY_VALUE); } else { //將全部的方法拼接成以逗號爲分隔符的字符串 map.put("methods", StringUtils.join(new HashSet<String>(Arrays.asList(methods)), ",")); } } //處理token屬性 //若是配置的injvm的話就表明本地調用(本地調用還用Dubbo的話實在有點蛋疼) //全部的核心屬性最後都成了URL的拼接屬性,若是咱們還記得map裏面拼裝了多少屬性的話就知道這個URL內容有多豐富 URL url = new URL(name, host, port, (contextPath == null || contextPath.length() == 0 ? "" : contextPath + "/") + path, map); // 下面是核心暴露過程,將不會省略源碼 String scope = url.getParameter(Constants.SCOPE_KEY); //配置爲none不暴露 if (! Constants.SCOPE_NONE.toString().equalsIgnoreCase(scope)) { //配置不是remote的狀況下作本地暴露 (配置爲remote,則表示只暴露遠程服務) if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) { exportLocal(url); } //若是配置不是local則暴露爲遠程服務.(配置爲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) { //dynamic表示是否須要人工管理服務的上線下線(動態管理模式) 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對象(ref爲接口實現類的引用) Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); //將invoker轉化爲exporter對象 Exporter<?> exporter = protocol.export(invoker); exporters.add(exporter); //將exporter添加到須要暴露的列表中取 } ================================================================ } else { //若是找不到註冊中心的話就本身充當本身的註冊中心吧 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); Exporter<?> exporter = protocol.export(invoker); exporters.add(exporter); } } } //多個協議就有多個url與其對應,因此要一一存儲。 this.urls.add(url); }
檢驗所需參數的合法性
將多層的參數(可能重複配置)最終整理出最終的結果(map),而後根據參數拼接成暴露服務需用到的url。
處理generic,Stub,injvm等其餘須要支持的內容,補充dubbo的功能多樣性,可是都不涉及核心流程。
根據對應的協議將服務進行暴露(將提供的服務推送到註冊中心供服務調用者發現),默認使用Dubbo協議。