在編寫好服務以後,dubbo會將服務export出去,這個時候就能夠編寫consumer來調用這個服務了。dubbo做爲一個rpc框架,使用者使用遠程服務和使用本地服務是相似的,不用關心遠程服務在哪裏,怎麼引用的,由於dubbo包含了自動發現和引用服務的功能。java
dubbo引用服務主要工做:spring
ReferenceConfig<TestDubboService> reference = new ReferenceConfig(); reference.setApplication(new ApplicationConfig(appName)); reference.setRegistry(new RegistryConfig(dubboRegistry)); reference.setInterface(TestDubboService.class); reference.setTimeout(timeout); TestDubboService service = (TestDubboService)reference.get();
使用Java代碼配置很明顯,直接使用ReferenceConfig.get獲取一個proxy緩存
<dubbo:reference interface="com.test.service.TestDubboService" id="testDubboService" />
app
spring解析該dubbo自定義標籤的時候(請提早學習spring如何解析自定義標籤),會初始化ReferenceBean,該bean是一個factoryBean而且繼承自ReferenceConfig,在getBean方法中調用了ReferenceConfig.get,接下來的方式就和上面「使用Java代碼引用」一致了。因此dubbo引用服務的工做就主要在於如何建立proxy。框架
ReferenceConfig的主要做用是配置並引用遠程服務,建立遠程服務的本地代理。ReferenceBean繼承自ReferenceConfig,ReferenceConfig是一個FactoryBean ,實現了getObject方法,在spring容器初始化完成的時候會初始化配置爲非lazyInit的bean,也就會調用ReferenceBean.getObject方法,裏面會調用ReferenceConfig.get方法,從而觸發ServiceConfig的初始化方法ServiceConfig.init。ide
inti方法的主要邏輯是:學習
因爲是遠程服務,consumer須要有一個代理來處理consumer發起的遠程過程調用。dubbo經過遠程調用的可執行體Invoker的代理來實現。接下來主要就是先建立Invoker,而後建立Invoker的proxy。this
建立Invoker調用堆棧以下url
createProxy的主要功能:代理
下面主要說說Invoker的建立過程:
這裏具體說明一下consumer訂閱,ZookeeperRegistry#doSubscribe中會將url中配置的category取出來拼接成registry的目錄節點形式,而後訂閱這些節點
// RegistryProtocol類 private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) { // ... 省略中間代碼 // 這裏調用的是RegistryDirectory.subscribe方法 // 在這裏將consumer端想要訂閱的category添加到url,包括providers,configurators,routers directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY, Constants.PROVIDERS_CATEGORY + "," + Constants.CONFIGURATORS_CATEGORY + "," + Constants.ROUTERS_CATEGORY)); return cluster.join(directory); } // ZookeeperRegistry在doSubscribe調用本身的下面這個方法,將URL中的category轉化爲registry中的目錄對應的url private String[] toCategoriesPath(URL url) { String[] categroies; if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) { // 若是配置的category是*,則取全部的category:providers,consumer,routers,configurators categroies = new String[] {Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY, Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY}; } else { categroies = url.getParameter(Constants.CATEGORY_KEY, new String[] {Constants.DEFAULT_CATEGORY}); } String[] paths = new String[categroies.length]; for (int i = 0; i < categroies.length; i ++) { // 將category拼接成registry中的目錄形式,相似:/dubbo/com.test.service.TestDubboService/providers paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categroies[i]; } return paths; } protected void doSubscribe(final URL url, final NotifyListener listener) { try { if (Constants.ANY_VALUE.equals(url.getServiceInterface())) { // ... 省略中間代碼 } else { List<URL> urls = new ArrayList<URL>(); for (String path : toCategoriesPath(url)) { ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url); if (listeners == null) { // 若是以前該路徑沒有添加過listener,則建立一個map來放置listener zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>()); listeners = zkListeners.get(url); } ChildListener zkListener = listeners.get(listener); if (zkListener == null) { // 若是沒有添加過對於子節點的listener,則建立 listeners.putIfAbsent(listener, new ChildListener() { public void childChanged(String parentPath, List<String> currentChilds) { ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds)); } }); zkListener = listeners.get(listener); } zkClient.create(path, false); // 添加listener到該目錄及其子節點 List<String> children = zkClient.addChildListener(path, zkListener); if (children != null) { urls.addAll(toUrlsWithEmpty(url, path, children)); } } // 這個方法自己會致使監聽的目錄及其子節點變化,直接調用notify notify(url, listener, urls); } } catch (Throwable e) { throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); } }
上面是consumer的訂閱部分的源碼,在consumer訂閱的時候會調用FailbackRegistry#notify,接下來就是將url轉換爲Invoker,接下來的調用鏈路能夠參考上面方法調用堆棧的圖,轉化的主要代碼爲:
com.alibaba.dubbo.registry.integration.RegistryDirectory#refreshInvoker com.alibaba.dubbo.registry.integration.RegistryDirectory#toInvokers
這兩個方法中的源代碼註釋較爲詳細了就再也不贅述了。
在toInvokers方法中會調用DubboProtocol#refer,在該方法中啓動NettyClient。
provider提供服務後,consumer端就能夠找到並引用該服務,接下來就能夠像使用本地服務同樣使用該服務了,發起遠程該過程調用。