dubbo系列之服务引用-原理

前言

前面几篇关于dubbo的文章一直都是介绍服务暴露,这篇文章就来说说服务的引用。

源码构成

先来一张官方文档上的图

服务消费者消费一个服务的详细过程

看代码最重要的一步就是找到代码的起点,服务消费的起点就是dubbo-demo-cosumer.xml文件

1
2
<!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
<dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>

通过spring的schemas标签引用服务,和服务暴露的时候一样,我们找到DubboNameSpaceHandler文件,即dubbo的命名空间处理器,找到dubbo:reference标签解析。

DubboNamespaceHandler类
1
2
3
4
public void init() {
//......
registerBeanDefinitionParser("reference", new DubboBeanDefinitionParser(ReferenceBean.class, false));
}
ReferenceBean类

观察到ReferenceBean类继承了FactoryBean类,而spring中的FactoryBean类就是用于获取动态代理对象的。所以我们就dubug进入ReferenceBean继承FactoryBean实现的getObject()方法里面看看。

1
2
3
public Object getObject() throws Exception {
return get();//实现spring的FactoryBean,为了获取动态代理对象
}
ReferenceConfig类
get()
1
2
3
4
5
6
7
8
9
public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
}
init()
–>createProxy()
1
2
// 通过refer创建invoker对象
invoker = refprotocol.refer(interfaceClass, urls.get(0));

–>ExtensionLoader.getExtensionLoader(Protocol.class).getExtension(“registry”);

–>extension.refer(arg0, arg1);
​ –>ProtocolFilterWrapper.refer
​ –>RegistryProtocol.refer ——建立zk连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
Registry registry = registryFactory.getRegistry(url);//建立zk的连接,和服务发布流程一样
if (RegistryService.class.equals(type)) {
return proxyFactory.getInvoker((T) registry, type, url);
}

// group="a,b" or group="*"
Map<String, String> qs = StringUtils.parseQueryString(url.getParameterAndDecoded(Constants.REFER_KEY));
String group = qs.get(Constants.GROUP_KEY);
if (group != null && group.length() > 0) {
if ((Constants.COMMA_SPLIT_PATTERN.split(group)).length > 1
|| "*".equals(group)) {
return doRefer(getMergeableCluster(), registry, type, url);
}
}
return doRefer(cluster, registry, type, url);
}
doRefer()——创建、订阅节点
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
directory.setRegistry(registry);
directory.setProtocol(protocol);
URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, NetUtils.getLocalHost(), 0, type.getName(), directory.getUrl().getParameters());
if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
&& url.getParameter(Constants.REGISTER_KEY, true)) {
//创建节点
registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
Constants.CHECK_KEY, String.valueOf(false)));
}
//订阅节点
directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
Constants.PROVIDERS_CATEGORY
+ "," + Constants.CONFIGURATORS_CATEGORY
+ "," + Constants.ROUTERS_CATEGORY));
//加入集群路由(下一篇博客再做详解)
return cluster.join(directory);
}

registry.register//创建zk的节点,和服务端发布一样(省略代码)。节点名为:dubbo/com.alibaba.dubbo.demo.DemoService/consumers

registry.subscribe//订阅zk的节点,和服务端发布一样(省略代码)。 /dubbo/com.alibaba.dubbo.demo.DemoService/providers,

/dubbo/com.alibaba.dubbo.demo.DemoService/configurators, /dubbo/com.alibaba.dubbo.demo.DemoService/routers]

当然这里说到了服务的发布和引用在建立zk连接,创建节点以及订阅节点都是一样的。有没有不同之处呢?还是有的,就是在订阅结束之后的通知环节。

zookeeperRegistry().doSubscribe()

–>FailbackRegistry.notify

-->doNotify(url, listener, urls);

        -->AbstractRegistry.notify

​ –>saveProperties(url);//把服务端的注册url信息更新到C:\Users\bobo.dubbo\dubbo-registry-192.168.48.117.cache
​ –>registryCacheExecutor.execute(new SaveProperties(version));//采用线程池来处理
​ –>listener.notify(categoryList)
以上的通知流程都是和服务发布的时候是一样的,直到进入

RegistryDirectory.notify
1
refreshInvoker(invokerUrls)//刷新缓存中的invoker列表

refreshInvoker的最终目的:刷新Map<String, Invoker> urlInvokerMap 对象,这也就是服务发布和引用的时候通知的最大区别,服务发布的时候是没有刷新的。

ok,上面的所有代码就完成了scheemas标签信息到invoker的转换,下面就来将invoker到ref的转换过程——就是创建代理。

再回到createProxy()
1
2
// 创建服务代理
return (T) proxyFactory.getProxy(invoker)

–>ProxyFactory$Adpative.getProxy

-->ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.ProxyFactory.class).getExtension("javassist");
  -->StubProxyFactoryWrapper.getProxy
    -->proxyFactory.getProxy(invoker)
     -->AbstractProxyFactory.getProxy

       -->getProxy(invoker, interfaces)
JavassistProxyFactory类
1
2
3
4
public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
return (T) Proxy.getProxy(interfaces).newInstance(
new InvokerInvocationHandler(invoker));
}

Proxy.getProxy(interfaces)//目前代理对象interface com.alibaba.dubbo.demo.DemoService, interface com.alibaba.dubbo.rpc.service.EchoService
InvokerInvocationHandler// 采用jdk自带的InvocationHandler,创建InvokerInvocationHandler对象。

最后最后,再来一张服务引用的总结图

服务引用流程图