Dubbo的啟動(dòng)主要是發(fā)布服務(wù)的過程,起到核心作用的就是ServiceConfig(ServiceConfig就是我們在Dubbo的配置文件中配置的dubbo:service這些配置項(xiàng)對應(yīng)的實(shí)體類)。服務(wù)的啟動(dòng)初始位置也基本是在這里,下面我們來看看具體的實(shí)現(xiàn)內(nèi)容。
成都創(chuàng)新互聯(lián)公司10多年企業(yè)網(wǎng)站設(shè)計(jì)服務(wù);為您提供網(wǎng)站建設(shè),網(wǎng)站制作,網(wǎng)頁設(shè)計(jì)及高端網(wǎng)站定制服務(wù),企業(yè)網(wǎng)站設(shè)計(jì)及推廣,對石涼亭等多個(gè)領(lǐng)域擁有多年的網(wǎng)站推廣經(jīng)驗(yàn)的網(wǎng)站建設(shè)公司。
講基本內(nèi)容前首先理清楚幾個(gè)名詞概念:
Invoker:Invoker的概念我們在動(dòng)態(tài)代理的時(shí)候就接觸過,中文的意思大概是執(zhí)行者,這里其實(shí)可以理解為具體方法的執(zhí)行者。其核心內(nèi)容大致如下:
Class<T> getInterface();
Result invoke(Invocation invocation) throws RpcException;
URL getUrl();
通過以上的三個(gè)方法們就可以執(zhí)行到具體的方法并且獲得方法的執(zhí)行結(jié)果。通過getUrl獲得需要執(zhí)行的方法具體實(shí)現(xiàn)細(xì)節(jié),主要是獲得具體的ref;其次就是組裝方法的參數(shù)信息等等,這些信息在invocation里面都有封裝;最后通過執(zhí)行invoke方法觸發(fā)具體的方法并返回結(jié)果。從這里可以看出Invoker是具體方法執(zhí)行的最后一個(gè)守關(guān)者,獲得了Invoker,就獲得了具體接口的代碼,然后執(zhí)行代理就可以。
Invoker僅僅是作為一個(gè)代理的門面,其不僅可以代表本地執(zhí)行Duubo調(diào)用的代理,還可以充當(dāng)RPC時(shí)候的代理,更是可以將自己包裝成一個(gè)多個(gè)Invoker聚合而成的代理(主要是處理集群的一些策略,包括負(fù)載均衡和路由等)。
Exporter:服務(wù)暴露的過程中會將Invoker轉(zhuǎn)換成Exporter(暴露者),及Exporter其實(shí)包含了Invoker,主要是用于不同層面的服務(wù)發(fā)布。
其實(shí)Dubbo 還有一些比較重要的對象,像Protocol,Exchanger等等。我認(rèn)為在這里直接說明不太合適,所以等到我們用到之后再開始說明。
一些基本的屬性:group,version,interfaceName,interfaceClass,timeout等等。我們凡是可以在dubbo:service上配置的屬性都在ServiceConfig中可以找得到對應(yīng)的屬性;
//dubbo對應(yīng)的服務(wù)發(fā)布協(xié)議,這里可以清楚地看到Dubbo在這里使用的自己的spi機(jī)制,來保證靈活性。(至于SPI機(jī)制的具體實(shí)現(xiàn),之后有機(jī)會的話會講到,簡單理解就是通過getExtensionLoader獲得對應(yīng)類的擴(kuò)展類實(shí)現(xiàn)類)
private static final Protocol protocol = ExtensionLoader.getExtensionLoader(Protocol.class).getAdaptiveExtension();
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<?>>();
對于服務(wù)暴露來說,在ServiceConfig里面的初始方法就是export()方法了,下面我們從export方法開始來看看:
public synchronized void export() { if (provider != null) { //默認(rèn)取provider的配置 if (export == null) { export = provider.getExport(); } if (delay == null) { delay = provider.getDelay(); } } //如果export設(shè)置為false的話就直接返回 if (export != null && ! export.booleanValue()) { return; } //如果設(shè)置延遲時(shí)間的話就延遲指定時(shí)間然后進(jìn)行暴露 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);//將暴露接口的線程設(shè)置為守護(hù)線程 thread.setName("DelayExportServiceThread"); thread.start(); } else { doExport(); //一切暴露的核心還都是要看doExport方法。 } } protected synchronized void doExport() { // 防止服務(wù)多次暴露 // 設(shè)置默認(rèn)的基本屬性 // 針對泛化接口做單獨(dú)處理 if (ref instanceof GenericService) { interfaceClass = GenericService.class; if (StringUtils.isEmpty(generic)) { generic = Boolean.TRUE.toString(); } } else { try {//通過反射初始化接口(interfaceName是實(shí)現(xiàn)類的全稱) interfaceClass = Class.forName(interfaceName, true, Thread.currentThread() .getContextClassLoader()); } catch (ClassNotFoundException e) { throw new IllegalStateException(e.getMessage(), e); } //檢查定義的方法是否為接口中的方法 checkInterfaceAndMethods(interfaceClass, methods); //檢查引用不為空,并且引用必需實(shí)現(xiàn)接口 checkRef(); //如果到這一步的話說明類實(shí)現(xiàn)是自己定義的,所以設(shè)置generic為false generic = Boolean.FALSE.toString(); } // 處理Local和Stub代理處理 // 檢查Application,Registry,Protocol的配置情況 //將配置的屬性綁定到當(dāng)前對象 appendProperties(this); //針對Local,Stub和Mock進(jìn)行校驗(yàn) //上面的操作主要是做一些檢驗(yàn)和初始化的操作,沒有涉及到具體的暴露服務(wù)邏輯 doExportUrls(); } private void doExportUrls() { //取到注冊中心的URL List<URL> registryURLs = loadRegistries(true); for (ProtocolConfig protocolConfig : protocols) { //根據(jù)配置的通信協(xié)議將服務(wù)暴露到注冊中心 doExportUrlsFor1Protocol(protocolConfig, registryURLs); } } private void doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs) { // 默認(rèn)采用Dubbo協(xié)議 //之后的部分邏輯就是想盡一切辦法取到host //取到端口號(之后的部分關(guān)于端口的邏輯就是想盡一切辦法取端口號) // 這個(gè)map十分重要,它的意義在于所有存儲所有最終使用到的屬性,我們知道一個(gè)屬性例如timeout,可能在Application,provider,service中都有配置,具體以哪個(gè)為準(zhǔn),都是這個(gè)map處理的事情。 Map<String, String> map = new HashMap<String, String>(); if (anyhost) { //如果此時(shí)anyhost為true的話 map.put(Constants.ANYHOST_KEY, "true"); } // 存儲簡單的服務(wù)信息 //將application,module,provider,protocol和service的信息設(shè)置到map里面 //將應(yīng)用配置的樹勇按照層級存入map中。注意這里的層級關(guān)系,是一層層覆蓋的 即關(guān)系為:ServiceConfig->PrtocolConfig->ProviderConfig->ModuleConfig->ApplicaionConfig //單獨(dú)處理好method層級的參數(shù)關(guān)系 //判斷有沒有配置通配協(xié)議 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進(jìn)行包裝然后取得方法名字,對于wapper包裝器就是將不同的類統(tǒng)一化 //參考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的話就代表本地調(diào)用(本地調(diào)用還用Dubbo的話實(shí)在有點(diǎn)蛋疼) //所有的核心屬性最后都成了URL的拼接屬性,如果我們還記得map里面拼裝了多少屬性的話就知道這個(gè)URL內(nèi)容有多豐富 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,則表示只暴露遠(yuǎn)程服務(wù)) if (!Constants.SCOPE_REMOTE.toString().equalsIgnoreCase(scope)) { exportLocal(url); } //如果配置不是local則暴露為遠(yuǎn)程服務(wù).(配置為local,則表示只暴露遠(yuǎn)程服務(wù)) 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表示是否需要人工管理服務(wù)的上線下線(動(dòng)態(tài)管理模式) 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為接口實(shí)現(xiàn)類的引用) Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, registryURL.addParameterAndEncoded(Constants.EXPORT_KEY, url.toFullString())); //將invoker轉(zhuǎn)化為exporter對象 Exporter<?> exporter = protocol.export(invoker); exporters.add(exporter); //將exporter添加到需要暴露的列表中取 } ================================================================ } else { //如果找不到注冊中心的話就自己充當(dāng)自己的注冊中心吧 Invoker<?> invoker = proxyFactory.getInvoker(ref, (Class) interfaceClass, url); Exporter<?> exporter = protocol.export(invoker); exporters.add(exporter); } } } //多個(gè)協(xié)議就有多個(gè)url與其對應(yīng),所以要一一存儲。 this.urls.add(url); }
通過ServicConfig中的內(nèi)容分解,我們看出來里面主要做的內(nèi)容如下:
檢驗(yàn)所需參數(shù)的合法性
將多層的參數(shù)(可能重復(fù)配置)最終整理出最終的結(jié)果(map),然后根據(jù)參數(shù)拼接成暴露服務(wù)需用到的url。
處理generic,Stub,injvm等其他需要支持的內(nèi)容,補(bǔ)充dubbo的功能多樣性,但是都不涉及核心流程。
根據(jù)對應(yīng)的協(xié)議將服務(wù)進(jìn)行暴露(將提供的服務(wù)推送到注冊中心供服務(wù)調(diào)用者發(fā)現(xiàn)),默認(rèn)使用Dubbo協(xié)議。
網(wǎng)站名稱:DUBBO服務(wù)啟動(dòng)過程
文章來源:http://jinyejixie.com/article32/gcejsc.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站設(shè)計(jì)公司、App開發(fā)、電子商務(wù)、服務(wù)器托管、Google、云服務(wù)器
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)