在编写好服务以后,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端就能够找到并引用该服务,接下来就能够像使用本地服务同样使用该服务了,发起远程该过程调用。