7.6 服务远程暴露 - 注册服务到zookeeper

为了安全:服务启动的ip全部使用10.10.10.10

远程服务的暴露总体步骤:

  • 将ref封装为invoker
  • 将invoker转换为exporter
  • 启动netty
  • 注册服务到zookeeper
  • 订阅
  • 返回新的exporter实例

7.4 服务远程暴露 - 创建Exporter与启动netty服务端中,实现了前三步,本节实现第四步:注册服务到zk。总体代码如下:RegistryProtocol.export(final Invoker<T> originInvoker)

1         final Registry registry = getRegistry(originInvoker);//创建ZookeeperRegistry实例:创建CuratorClient,并启动会话。
2 final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);//获取真正要注册在zk上的url
3 registry.register(registedProviderUrl);//创建节点(即注册服务到zk上)

说明:

  • 第一句代码用来创建ZookeeperRegistry实例:创建CuratorClient,并启动会话。
  • 第二句代码获取真正要注册在zk上的url
  • 第三句代码实现创建节点(即注册服务到zk上)

一  创建ZookeeperRegistry实例

1  RegistryProtocol.getRegistry(final Invoker<?> originInvoker)

 1     /**
2 * 根据invoker的地址获取registry实例
3 */
4 private Registry getRegistry(final Invoker<?> originInvoker) {
5 URL registryUrl = originInvoker.getUrl();
6 if (Constants.REGISTRY_PROTOCOL.equals(registryUrl.getProtocol())) {
7 String protocol = registryUrl.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_DIRECTORY);//zookeeper
8 registryUrl = registryUrl.setProtocol(protocol).removeParameter(Constants.REGISTRY_KEY);
9 }
10 return registryFactory.getRegistry(registryUrl);
11 }

首先对originInvoker中的url进行处理:

  • 将协议换成zookeeper
  • 去掉registry=zookeeper的参数

来看一下originInvoker的url:(解码后的)

registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&export=dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2791&side=provider&timestamp=1507262031554&pid=2791&registry=zookeeper&timestamp=1507262031521

说明:

  • 第一个红色部分代表协议:zookeeper
  • 第二个红色部分是export参数
  • 第三个红色部分是registry=zookeeper

经过处理之后的registryUrl为:

zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&export=dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=2791&side=provider&timestamp=1507262031554&pid=2791&timestamp=1507262031521

之后使用注册工厂来创建注册中心。

2  RegistryFactory$Adaptive.getRegistry(com.alibaba.dubbo.common.URL registryUrl)

 1 public class RegistryFactory$Adaptive implements com.alibaba.dubbo.registry.RegistryFactory {
2 public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
3 if (arg0 == null)
4 throw new IllegalArgumentException("url == null");
5 com.alibaba.dubbo.common.URL url = arg0;
6 String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );//zookeeper
7 if(extName == null)
8 throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
9 com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
10 return extension.getRegistry(arg0);
11 }
12 }

这里获取到的extension是ZookeeperRegistryFactory,之后,使用ZookeeperRegistryFactory进行Registry的创建。首先来看一下ZookeeperRegistryFactory的继承图:

getRegistry方法在ZookeeperRegistryFactory的父类AbstractRegistryFactory中。

3  AbstractRegistryFactory.getRegistry(URL registryUrl)

 1     public Registry getRegistry(URL url) {
2 url = url.setPath(RegistryService.class.getName())
3 .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
4 .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
5 String key = url.toServiceString();
6 // 锁定注册中心获取过程,保证注册中心单一实例
7 LOCK.lock();
8 try {
9 Registry registry = REGISTRIES.get(key);
10 if (registry != null) {
11 return registry;
12 }
13 registry = createRegistry(url);
14 if (registry == null) {
15 throw new IllegalStateException("Can not create registry " + url);
16 }
17 REGISTRIES.put(key, registry);
18 return registry;
19 } finally {
20 // 释放锁
21 LOCK.unlock();
22 }
23 }

流程:

  • 先处理url,之后获取Registry的key,然后根据该key从Map<String, Registry> REGISTRIES注册中心集合缓存中获取Registry,如果有,直接返回,如果没有,创建Registry,之后存入缓存,最后返回。

首先处理传入的registryUrl:

  • 设置:path=com.alibaba.dubbo.registry.RegistryService
  • 添加参数:interface=com.alibaba.dubbo.registry.RegistryService
  • 去除export参数

最终得到的registryUrl如下:

zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=2791&timestamp=1507262031521

之后,很具上述的registryUrl创建Registry的key,该{ key : Registry }最终会被存储在Map<String, Registry> REGISTRIES注册中心集合(该属性是ZookeeperRegistryFactory父类AbstractRegistryFactory的一个属性)中。

根据registryUrl创建Registry的key:url.toServiceString()

 1     public String toServiceString() {
2 return buildString(true, false, true, true);
3 }
4
5 private String buildString(boolean appendUser, boolean appendParameter, boolean useIP, boolean useService, String... parameters) {
6 StringBuilder buf = new StringBuilder();
7 if (protocol != null && protocol.length() > 0) { //protocol://
8 buf.append(protocol);
9 buf.append("://");
10 }
11 if (appendUser && username != null && username.length() > 0) { //protocol://username:password@host:port/group/interface{path}:version/parameters
12 buf.append(username);
13 if (password != null && password.length() > 0) {
14 buf.append(":");
15 buf.append(password);
16 }
17 buf.append("@");
18 }
19 String host;
20 if (useIP) {
21 host = getIp();
22 } else {
23 host = getHost();
24 }
25 if (host != null && host.length() > 0) {
26 buf.append(host);
27 if (port > 0) {
28 buf.append(":");
29 buf.append(port);
30 }
31 }
32 String path;
33 if (useService) {
34 path = getServiceKey();
35 } else {
36 path = getPath();
37 }
38 if (path != null && path.length() > 0) {
39 buf.append("/");
40 buf.append(path);
41 }
42 if (appendParameter) {
43 buildParameters(buf, true, parameters);
44 }
45 return buf.toString();
46 }
47
48 public String getServiceKey() {
49 String inf = getServiceInterface();//先获取interface参数,如果没有的话,取path的值,这里都是com.alibaba.dubbo.registry.RegistryService
50 if (inf == null) return null;
51 StringBuilder buf = new StringBuilder();
52 String group = getParameter(Constants.GROUP_KEY);
53 if (group != null && group.length() > 0) {
54 buf.append(group).append("/"); //interfacegroup
55 }
56 buf.append(inf);
57 String version = getParameter(Constants.VERSION_KEY);
58 if (version != null && version.length() > 0) {
59 buf.append(":").append(version);
60 }
61 return buf.toString();
62 }

最终得到的应该是这样的形式:protocol://username:password@host:port/group/interface{path}:version?key1=value1&key2=value2...。

这里key=zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService

之后来到了真正创建Registry的地方。

 1 public class ZookeeperRegistryFactory extends AbstractRegistryFactory {
2 private ZookeeperTransporter zookeeperTransporter;
3
4 public void setZookeeperTransporter(ZookeeperTransporter zookeeperTransporter) {
5 this.zookeeperTransporter = zookeeperTransporter;
6 }
7
8 public Registry createRegistry(URL url) {
9 return new ZookeeperRegistry(url, zookeeperTransporter);
10 }
11 }

这里的zookeeperTransporter对象是一个com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter$Adaptive对象。

在创建ZookeeperRegistry之前来看一下其继承图:

new ZookeeperRegistry(registryUrl, ZookeeperTransporter$Adaptive对象)

 1     private final static int DEFAULT_ZOOKEEPER_PORT = 2181;
2 private final static String DEFAULT_ROOT = "dubbo";
3 private final String root;
4 private final Set<String> anyServices = new ConcurrentHashSet<String>();
5 private final ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners = new ConcurrentHashMap<URL, ConcurrentMap<NotifyListener, ChildListener>>();
6 private final ZookeeperClient zkClient;
7
8 public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
9 super(url);
10 if (url.isAnyHost()) {
11 throw new IllegalStateException("registry address == null");
12 }
13 String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);//dubbo
14 if (!group.startsWith(Constants.PATH_SEPARATOR)) {
15 group = Constants.PATH_SEPARATOR + group;
16 }
17 this.root = group;// /dubbo
18 zkClient = zookeeperTransporter.connect(url);//创建zk客户端,启动会话
19 zkClient.addStateListener(new StateListener() {//监听重新连接成功事件,重新连接成功后,之前已经完成注册和订阅的url要重新进行注册和订阅(因为临时节点可能已经跪了)
20 public void stateChanged(int state) {
21 if (state == RECONNECTED) {
22 try {
23 recover();
24 } catch (Exception e) {
25 logger.error(e.getMessage(), e);
26 }
27 }
28 }
29 });
30 }

new FailbackRegistry(registryUrl)

 1     private final ScheduledExecutorService retryExecutor = Executors.newScheduledThreadPool(1, new NamedThreadFactory("DubboRegistryFailedRetryTimer", true));
2 // 失败重试定时器,定时检查是否有请求失败,如有,无限次重试
3 private final ScheduledFuture<?> retryFuture;
4 private final Set<URL> failedRegistered = new ConcurrentHashSet<URL>();
5 private final Set<URL> failedUnregistered = new ConcurrentHashSet<URL>();
6 private final ConcurrentMap<URL, Set<NotifyListener>> failedSubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
7 private final ConcurrentMap<URL, Set<NotifyListener>> failedUnsubscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();
8 private final ConcurrentMap<URL, Map<NotifyListener, List<URL>>> failedNotified = new ConcurrentHashMap<URL, Map<NotifyListener, List<URL>>>();
9 private AtomicBoolean destroyed = new AtomicBoolean(false);
10
11 public FailbackRegistry(URL url) {
12 super(url);
13 int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD);//5*1000
14 this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() {
15 public void run() {
16 // 检测并连接注册中心
17 try {
18 retry();
19 } catch (Throwable t) { // 防御性容错
20 logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t);
21 }
22 }
23 }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);
24 }

new AbstractRegistry(registryUrl)

 1     // URL地址分隔符,用于文件缓存中,服务提供者URL分隔
2 private static final char URL_SEPARATOR = ' ';
3 // URL地址分隔正则表达式,用于解析文件缓存中服务提供者URL列表
4 private static final String URL_SPLIT = "\\s+";
5 // 本地磁盘缓存,其中特殊的key值.registies记录注册中心列表,其它均为notified服务提供者列表
6 private final Properties properties = new Properties();
7 // 文件缓存定时写入
8 private final ExecutorService registryCacheExecutor = Executors.newFixedThreadPool(1, new NamedThreadFactory("DubboSaveRegistryCache", true));
9 //是否是同步保存文件
10 private final boolean syncSaveFile;
11 // 本地磁盘缓存文件
12 private File file;
13 private final AtomicLong lastCacheChanged = new AtomicLong();
14 private final Set<URL> registered = new ConcurrentHashSet<URL>();//已经注册的url集合
15 private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();//已经订阅的<URL, Set<NotifyListener>>
16 private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<URL, Map<String, List<URL>>>();//已经通知的<URL, Map<String, List<URL>>>
17 private URL registryUrl;//注册url
18 private AtomicBoolean destroyed = new AtomicBoolean(false);
19
20 public AbstractRegistry(URL url) {
21 setUrl(url);
22 // 启动文件保存定时器
23 syncSaveFile = url.getParameter(Constants.REGISTRY_FILESAVE_SYNC_KEY, false);
24 String filename = url.getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/.dubbo/dubbo-registry-" + url.getHost() + ".cache");
25 File file = null;
26 if (ConfigUtils.isNotEmpty(filename)) {
27 file = new File(filename);
28 if (!file.exists() && file.getParentFile() != null && !file.getParentFile().exists()) {
29 if (!file.getParentFile().mkdirs()) {//创建文件所在的文件夹 /Users/jigangzhao/.dubbo/
30 throw new IllegalArgumentException("Invalid registry store file " + file + ", cause: Failed to create directory " + file.getParentFile() + "!");
31 }
32 }
33 }
34 this.file = file;
35 loadProperties();
36 notify(url.getBackupUrls());
37 }

先简单的总结一下:父子三代分别做的事情:

  • AbstractRegistry主要用来维护缓存文件。
  • FailbackRegistry主要用来做失败重试操作(包括:注册失败/反注册失败/订阅失败/反订阅失败/通知失败的重试);也提供了供ZookeeperRegistry使用的zk重连后的恢复工作的方法。
  • ZookeeperRegistry创建zk客户端,启动会话;并且调用FailbackRegistry实现zk重连后的恢复工作。

先看AbstractRegistry

  • 设置属性registryUrl=url:zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=4685&timestamp=1507286468150
  • 创建文件/Users/jigangzhao/.dubbo/dubbo-registry-10.211.55.5.cache的文件夹/Users/jigangzhao/.dubbo
  • 设置属性file:/Users/jigangzhao/.dubbo/dubbo-registry-10.211.55.5.cache文件,该文件存储信息将是这样的:

    com.alibaba.dubbo.demo.DemoService=empty\://10.10.10.10\:20880/com.alibaba.dubbo.demo.DemoService?anyhost\=true&application\=demo-provider&category\=configurators&check\=false&dubbo\=2.0.0&generic\=false&interface\=com.alibaba.dubbo.demo.DemoService&methods\=sayHello&pid\=5259&side\=provider&timestamp\=1507294508053

  • 如果file存在,将file中的内容写入properties属性;既然有读file,那么是什么时候写入file的呢?AbstractRegistry创建了一个含有一个名字为DubboSaveRegistryCache的后台线程的FixedThreadPool,只在在notify(URL url, NotifyListener listener, List<URL> urls)方法中会被调用,我们此处由于ConcurrentMap<URL, Set<NotifyListener>> subscribed为空,所以AbstractRegistry(URL url)中的notify(url.getBackupUrls())不会执行,此处也不会创建文件。
  • 最后是notify(url.getBackupUrls())(TODO 这里后续会写)

再来看FailbackRegistry:

只做了一件事,启动了一个含有一个名为DubboRegistryFailedRetryTimer的后台线程的ScheduledThreadPool,线程创建5s后开始第一次执行retry(),之后每隔5s执行一次。来看一下retry()

  1     /**
2 * 将所有注册失败的url(failedRegistered中的url)进行注册,之后从failedRegistered进行移除;
3 * 将所有反注册失败的url(failedUnregistered中的url)进行反注册,之后从failedUnregistered进行移除;
4 * 将所有订阅失败的url(failedSubscribed中的url)进行重新订阅,之后从failedSubscribed进行移除;
5 * 将所有反订阅失败的url(failedUnsubscribed中的url)进行反订阅,之后从failedUnsubscribed进行移除;
6 * 将所有通知失败的url(failedNotified中的url)进行通知,之后从failedNotified进行移除;
7 */
8 protected void retry() {
9 if (!failedRegistered.isEmpty()) {
10 Set<URL> failed = new HashSet<URL>(failedRegistered);
11 if (failed.size() > 0) {
12 if (logger.isInfoEnabled()) {
13 logger.info("Retry register " + failed);
14 }
15 try {
16 for (URL url : failed) {
17 try {
18 doRegister(url);
19 failedRegistered.remove(url);
20 } catch (Throwable t) { // 忽略所有异常,等待下次重试
21 logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
22 }
23 }
24 } catch (Throwable t) { // 忽略所有异常,等待下次重试
25 logger.warn("Failed to retry register " + failed + ", waiting for again, cause: " + t.getMessage(), t);
26 }
27 }
28 }
29 if (!failedUnregistered.isEmpty()) {
30 Set<URL> failed = new HashSet<URL>(failedUnregistered);
31 if (failed.size() > 0) {
32 if (logger.isInfoEnabled()) {
33 logger.info("Retry unregister " + failed);
34 }
35 try {
36 for (URL url : failed) {
37 try {
38 doUnregister(url);
39 failedUnregistered.remove(url);
40 } catch (Throwable t) { // 忽略所有异常,等待下次重试
41 logger.warn("Failed to retry unregister " + failed + ", waiting for again, cause: " + t.getMessage(), t);
42 }
43 }
44 } catch (Throwable t) { // 忽略所有异常,等待下次重试
45 logger.warn("Failed to retry unregister " + failed + ", waiting for again, cause: " + t.getMessage(), t);
46 }
47 }
48 }
49 if (!failedSubscribed.isEmpty()) {
50 Map<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedSubscribed);
51 for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(failed).entrySet()) {
52 if (entry.getValue() == null || entry.getValue().size() == 0) {
53 failed.remove(entry.getKey());
54 }
55 }
56 if (failed.size() > 0) {
57 if (logger.isInfoEnabled()) {
58 logger.info("Retry subscribe " + failed);
59 }
60 try {
61 for (Map.Entry<URL, Set<NotifyListener>> entry : failed.entrySet()) {
62 URL url = entry.getKey();
63 Set<NotifyListener> listeners = entry.getValue();
64 for (NotifyListener listener : listeners) {
65 try {
66 doSubscribe(url, listener);//listener需要一个一个订阅,每订阅一个,就将该listener从当前的url监听列表中移除
67 listeners.remove(listener);
68 } catch (Throwable t) { // 忽略所有异常,等待下次重试
69 logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
70 }
71 }
72 }
73 } catch (Throwable t) { // 忽略所有异常,等待下次重试
74 logger.warn("Failed to retry subscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
75 }
76 }
77 }
78 if (!failedUnsubscribed.isEmpty()) {
79 Map<URL, Set<NotifyListener>> failed = new HashMap<URL, Set<NotifyListener>>(failedUnsubscribed);
80 for (Map.Entry<URL, Set<NotifyListener>> entry : new HashMap<URL, Set<NotifyListener>>(failed).entrySet()) {
81 if (entry.getValue() == null || entry.getValue().size() == 0) {
82 failed.remove(entry.getKey());
83 }
84 }
85 if (failed.size() > 0) {
86 if (logger.isInfoEnabled()) {
87 logger.info("Retry unsubscribe " + failed);
88 }
89 try {
90 for (Map.Entry<URL, Set<NotifyListener>> entry : failed.entrySet()) {
91 URL url = entry.getKey();
92 Set<NotifyListener> listeners = entry.getValue();
93 for (NotifyListener listener : listeners) {
94 try {
95 doUnsubscribe(url, listener);//listener需要一个一个反订阅,每反订阅一个,就将该listener从当前的url监听列表中移除
96 listeners.remove(listener);
97 } catch (Throwable t) { // 忽略所有异常,等待下次重试
98 logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
99 }
100 }
101 }
102 } catch (Throwable t) { // 忽略所有异常,等待下次重试
103 logger.warn("Failed to retry unsubscribe " + failed + ", waiting for again, cause: " + t.getMessage(), t);
104 }
105 }
106 }
107 if (!failedNotified.isEmpty()) {
108 Map<URL, Map<NotifyListener, List<URL>>> failed = new HashMap<URL, Map<NotifyListener, List<URL>>>(failedNotified);
109 for (Map.Entry<URL, Map<NotifyListener, List<URL>>> entry : new HashMap<URL, Map<NotifyListener, List<URL>>>(failed).entrySet()) {
110 if (entry.getValue() == null || entry.getValue().size() == 0) {
111 failed.remove(entry.getKey());
112 }
113 }
114 if (failed.size() > 0) {
115 if (logger.isInfoEnabled()) {
116 logger.info("Retry notify " + failed);
117 }
118 try {
119 for (Map<NotifyListener, List<URL>> values : failed.values()) {
120 for (Map.Entry<NotifyListener, List<URL>> entry : values.entrySet()) {
121 try {
122 NotifyListener listener = entry.getKey();
123 List<URL> urls = entry.getValue();
124 listener.notify(urls);
125 values.remove(listener);
126 } catch (Throwable t) { // 忽略所有异常,等待下次重试
127 logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
128 }
129 }
130 }
131 } catch (Throwable t) { // 忽略所有异常,等待下次重试
132 logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t);
133 }
134 }
135 }
136 }

最后回到我们的主角:ZookeeperRegistry

首先是为属性设置root=/dubbo,之后创建zk客户端,启动会话,最后创建了一个StateListener监听器,监听重新连接成功事件,重新连接成功后,之前已经完成注册和订阅的url要重新进行注册和订阅(因为临时节点可能已经跪了)。

来看创建zk客户端,启动会话的代码,这是此处最核心的部分:

ZookeeperTransporter$Adaptive.connect(com.alibaba.dubbo.common.URL registryUrl)

 1     public com.alibaba.dubbo.remoting.zookeeper.ZookeeperClient connect(com.alibaba.dubbo.common.URL arg0) {
2 if (arg0 == null)
3 throw new IllegalArgumentException("url == null");
4 com.alibaba.dubbo.common.URL url = arg0;
5 String extName = url.getParameter("client", url.getParameter("transporter", "zkclient"));//curator
6 if(extName == null)
7 throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter) name from url(" + url.toString() + ") use keys([client, transporter])");
8 com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter extension = (com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.remoting.zookeeper.ZookeeperTransporter.class).getExtension(extName);
9 return extension.connect(arg0);
10 }

这里创建的extension是CuratorZookeeperTransporter实例。

1 public class CuratorZookeeperTransporter implements ZookeeperTransporter {
2 public ZookeeperClient connect(URL url) {
3 return new CuratorZookeeperClient(url);
4 }
5 }

new CuratorZookeeperClient(registryUrl)

 1     private final CuratorFramework client;
2
3 public CuratorZookeeperClient(URL url) {
4 super(url);
5 try {
6 CuratorFrameworkFactory.Builder builder = CuratorFrameworkFactory.builder()
7 .connectString(url.getBackupAddress())
8 .retryPolicy(new RetryNTimes(Integer.MAX_VALUE, 1000))
9 .connectionTimeoutMs(5000);
10 String authority = url.getAuthority();
11 if (authority != null && authority.length() > 0) {
12 builder = builder.authorization("digest", authority.getBytes());
13 }
14 client = builder.build();
15 client.getConnectionStateListenable().addListener(new ConnectionStateListener() {
16 public void stateChanged(CuratorFramework client, ConnectionState state) {
17 if (state == ConnectionState.LOST) {
18 CuratorZookeeperClient.this.stateChanged(StateListener.DISCONNECTED);
19 } else if (state == ConnectionState.CONNECTED) {
20 CuratorZookeeperClient.this.stateChanged(StateListener.CONNECTED);
21 } else if (state == ConnectionState.RECONNECTED) {
22 CuratorZookeeperClient.this.stateChanged(StateListener.RECONNECTED);
23 }
24 }
25 });
26 client.start();
27 } catch (Exception e) {
28 throw new IllegalStateException(e.getMessage(), e);
29 }
30 }

这里首先执行父类AbstractZookeeperClient的构造器来初始化一些参数,之后创建CuratorFramework客户端,然后添加了ConnectionStateListener监听器,监听连接断开/连接成功/重新连接成功事件,之后作出相应的操作(实际上这里只有重新连接成功事件会被处理,而处理器实际上就是ZookeeperRegistry构造器中的那个执行recover()的StateListener),

    protected void stateChanged(int state) {
for (StateListener sessionListener : getSessionListeners()) {
sessionListener.stateChanged(state);//此处查找实现类,只有ZookeeperRegistry构造器中的那个StateListener
}
}

最后阻塞,直到创建会话完成。

来看一下父类AbstractZookeeperClient

1     private final URL url;
2 private final Set<StateListener> stateListeners = new CopyOnWriteArraySet<StateListener>();
3 private final ConcurrentMap<String, ConcurrentMap<ChildListener, TargetChildListener>> childListeners = new ConcurrentHashMap<String, ConcurrentMap<ChildListener, TargetChildListener>>();
4 private volatile boolean closed = false;
5
6 public AbstractZookeeperClient(URL url) {
7 this.url = url;
8 }

说明:

  • 设置属性url=registryUrl:zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=4685&timestamp=1507286468150
  • 创建了一个Set<StateListener> stateListeners,ZookeeperRegistry构造器中的那个执行recover()的StateListener就将会放在这里

至此,一个完整的ZookeeperRegistry实例就创建完成了,来看一下属性:

  • ZookeeperClient zkClient = CuratorZookeeperClient实例

    • CuratorFramework client:CuratorFrameworkImpl实例
    • String url:zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=4685&timestamp=1507286468150
    • Set<StateListener> stateListeners:{ 监听了重连成功事件的执行recover()的StateListener }
  • String root="/dubbo"
  • URL registryUrl = zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=4685&timestamp=1507286468150
  • Set<URL> registered:0//已经注册的url集合,此处为空
  • ConcurrentMap<URL, Set<NotifyListener>> subscribed:0//已经订阅的<URL, Set<NotifyListener>>
  • ConcurrentMap<URL, Map<String, List<URL>>> notified:0//已经通知的<URL, Map<String, List<URL>>>
  • Set<URL> failedRegistered:0//注册失败的url
  • Set<URL> failedUnregistered:0//反注册失败的url
  • ConcurrentMap<URL, Set<NotifyListener>> failedSubscribed:0//订阅失败的url
  • ConcurrentMap<URL, Set<NotifyListener>> failedUnsubscribed:0//反订阅失败的url
  • ConcurrentMap<URL, Map<NotifyListener, List<URL>>> failedNotified:0//通知失败的url
  • ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners:0

还有一个定时线程:DubboRegistryFailedRetryTimer每隔5s执行一次retry(),进行失败重试。

最后,该ZookeeperRegistry会存储在ZookeeperRegistry的父类的static属性Map<String, Registry> REGISTRIES中:

Map<String, Registry> REGISTRIES:{ "zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService" : ZookeeperRegistry实例 }

二  获取真正要注册到zk的节点url

1 final URL registedProviderUrl = getRegistedProviderUrl(originInvoker);
 1     /**
2 * 1 获取originInvoker的export参数值:就是providerUrl
3 * 2 去除providerUrl中所有参数名是"."开头的,然后去除参数monitor
4 */
5 private URL getRegistedProviderUrl(final Invoker<?> originInvoker) {
6 URL providerUrl = getProviderUrl(originInvoker);
7 //注册中心看到的地址
8 final URL registedProviderUrl = providerUrl.removeParameters(getFilteredKeys(providerUrl)).removeParameter(Constants.MONITOR_KEY);
9 return registedProviderUrl;
10 }
11
12 /**
13 * 从invoker的URL中的Map<String, String> parameters中获取key为export的地址providerUrl:
14 */
15 private URL getProviderUrl(final Invoker<?> origininvoker) {
16 String export = origininvoker.getUrl().getParameterAndDecoded(Constants.EXPORT_KEY);
17 if (export == null || export.length() == 0) {
18 throw new IllegalArgumentException("The registry export url is null! registry: " + origininvoker.getUrl());
19 }
20 URL providerUrl = URL.valueOf(export);
21 return providerUrl;
22 }
23
24 //过滤URL中不需要输出的参数(以点号开头的)
25 private static String[] getFilteredKeys(URL url) {
26 Map<String, String> params = url.getParameters();
27 if (params != null && !params.isEmpty()) {
28 List<String> filteredKeys = new ArrayList<String>();
29 for (Map.Entry<String, String> entry : params.entrySet()) {
30 if (entry != null && entry.getKey() != null && entry.getKey().startsWith(Constants.HIDE_KEY_PREFIX)) {
31 filteredKeys.add(entry.getKey());
32 }
33 }
34 return filteredKeys.toArray(new String[filteredKeys.size()]);
35 } else {
36 return new String[]{};
37 }
38 }

最后得到的registedProviderUrl是:

  • dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=4758&side=provider&timestamp=1507289961588

三  注册服务到zk

registry.register(registedProviderUrl);//创建节点(即注册服务到zk上)

这里的registry是ZookeeperRegistry。register(registedProviderUrl)方法在ZookeeperRegistry的父类FailbackRegistry中实现。

1  FailbackRegistry.register(registedProviderUrl)

 1     @Override
2 public void register(URL url) {
3 if (destroyed.get()){
4 return;
5 }
6 super.register(url);
7 failedRegistered.remove(url);
8 failedUnregistered.remove(url);
9 try {
10 // 向服务器端发送注册请求
11 doRegister(url);
12 } catch (Exception e) {
13 Throwable t = e;
14 // 如果开启了启动时检测check=true,则直接抛出异常,不会加入到failedRegistered中
15 boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
16 && url.getParameter(Constants.CHECK_KEY, true)
17 && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
18 boolean skipFailback = t instanceof SkipFailbackWrapperException;
19 if (check || skipFailback) {
20 if (skipFailback) {
21 t = t.getCause();
22 }
23 throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
24 } else {
25 logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
26 }
27 // 将失败的注册请求记录到失败列表,定时重试
28 failedRegistered.add(url);
29 }
30 }

首先调用父类AbstractRegistry的register(registedProviderUrl)将当前的registeredProviderUrl放到Set<URL> registered属性中,如下:

1     public void register(URL url) {
2 if (url == null) {
3 throw new IllegalArgumentException("register url == null");
4 }
5 if (logger.isInfoEnabled()) {
6 logger.info("Register: " + url);
7 }
8 registered.add(url);
9 }

之后,从failedRegistered和failedUnregistered两个url集合中删除该url。然后执行真正的服务注册(创建节点,doRegister(url)),如果在创建过程中抛出异常,如果url的协议不是consumer并且开启了check=true的属性并且当前存储的URL registryUrl也有check=true的话,那么直接抛出异常,不会将该url加入到failedRegistered集合;当然抛出的异常如果是SkipFailbackWrapperException,那么也会直接抛出异常,不会将该url加入到failedRegistered集合。否则,会将该url加入到failedRegistered集合,然后DubboRegistryFailedRetryTimer线程会每隔5s执行一次doRegister(url)。

我们来看真正doRegister(url)。

2  ZookeeperRegistry.doRegister(registedProviderUrl)

1     protected void doRegister(URL url) {
2 try {
3 zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
4 } catch (Throwable e) {
5 throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
6 }
7 }

首先是对入参registedProviderUrl进行一顿处理,

 1     private String toUrlPath(URL url) {
2 return toCategoryPath(url) + Constants.PATH_SEPARATOR + URL.encode(url.toFullString());
3 }
4
5 private String toCategoryPath(URL url) {
6 return toServicePath(url) + Constants.PATH_SEPARATOR + url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
7 }
8
9 private String toServicePath(URL url) {
10 String name = url.getServiceInterface();
11 if (Constants.ANY_VALUE.equals(name)) {
12 return toRootPath();
13 }
14 return toRootDir() + URL.encode(name);// /dubbo/com.alibaba.dubbo.demo.DemoService
15 }
16
17 private String toRootDir() {
18 if (root.equals(Constants.PATH_SEPARATOR)) {
19 return root;
20 }
21 return root + Constants.PATH_SEPARATOR;// /dubbo/
22 }
23
24 private String toRootPath() {
25 return root;
26 }

这里就体现了上边的ZookeeperRegistry的root属性的作用。最终实际上得到的是:/dubbo/interface/category/encode过的export,该节点也将是创建在zk上的节点。

  • /dubbo是根节点
  • /interface是服务接口
  • /category是providers/consumers/routers/configurators等

最终得到的url是:

  • /dubbo/com.alibaba.dubbo.demo.DemoService/providers/dubbo%3A%2F%2F10.10.10.10%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D5148%26side%3Dprovider%26timestamp%3D1507291294629
  • 解码后:/dubbo/com.alibaba.dubbo.demo.DemoService/providers/dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5148&side=provider&timestamp=1507291294629

最后执行zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true))来创建节点,该方法由CuratorZookeeperClient的父类AbstractZookeeperClient来执行:

 1     public void create(String path, boolean ephemeral) {
2 int i = path.lastIndexOf('/');
3 if (i > 0) {
4 create(path.substring(0, i), false);
5 }
6 if (ephemeral) {
7 createEphemeral(path);
8 } else {
9 createPersistent(path);
10 }
11 }

这里实际上是通过递归分别创建持久化的/dubbo,/dubbo/com.alibaba.dubbo.demo.DemoService以及/dubbo/com.alibaba.dubbo.demo.DemoService/providers节点;最后创建临时节点/dubbo/com.alibaba.dubbo.demo.DemoService/providers/dubbo%3A%2F%2F10.10.10.10%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D5148%26side%3Dprovider%26timestamp%3D1507291294629,而实际上,如果使用了curator的话,可以直接使用递归创建节点即可(结合zk的特性,只有最后一个字节点可以是临时节点,父节点一定是持久化节点),这里这样的写法应该是兼容不能递归创建节点的Zkclient客户端。值得注意的是,url.getParameter(Constants.DYNAMIC_KEY, true)为true则最终创建的节点是临时节点,否则是持久化节点。

创建节点的操作是在CuratorZookeeperClient中进行的。

 1     public void createPersistent(String path) {
2 try {
3 client.create().forPath(path);
4 } catch (NodeExistsException e) {
5 } catch (Exception e) {
6 throw new IllegalStateException(e.getMessage(), e);
7 }
8 }
9
10 public void createEphemeral(String path) {
11 try {
12 client.create().withMode(CreateMode.EPHEMERAL).forPath(path);
13 } catch (NodeExistsException e) {
14 } catch (Exception e) {
15 throw new IllegalStateException(e.getMessage(), e);
16 }
17 }

到此为止,我们去zk上看一下节点的创建情况。

或者从zkui上看一下:

隐藏掉的是ip:10.10.10.10。

到目前为止,我们再来看看ZookeeperRegistry的属性变化。相较于注册前:

  • Set<URL> registered:[ dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5214&side=provider&timestamp=1507293238549 ]

7.7 服务远程暴露 - 订阅与通知

为了安全:服务启动的ip全部使用10.10.10.10

远程服务的暴露总体步骤:

  • 将ref封装为invoker
  • 将invoker转换为exporter
  • 启动netty
  • 注册服务到zookeeper
  • 订阅与通知
  • 返回新的exporter实例

7.4 服务远程暴露 - 创建Exporter与启动netty服务端中,实现了前三步,在7.6 服务远程暴露 - 注册服务到zookeeper实现了第四步。本节实现第五步:订阅。总体代码如下:RegistryProtocol.export(final Invoker<T> originInvoker)

1         // 订阅override数据
2 // FIXME 提供者订阅时,会影响同一JVM即暴露服务,又引用同一服务的的场景,因为subscribed以服务名为缓存的key,导致订阅信息覆盖。
3 final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
4 final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);
5 overrideListeners.put(overrideSubscribeUrl, overrideSubscribeListener);
6 registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

说明:

  • 第一句代码根据registedProviderUrl来获取overrideSubscribeUrl。
  • 第二句代码创建overrideSubscribeListener
  • 第三句代码将{ overrideSubscribeUrl : overrideSubscribeListener放入缓存 }
  • 第四句代码实现真正的订阅与通知

一  获取overrideSubscribeUrl

final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);
1     /**
2 * 1 将协议改为provider;
3 * 2 添加参数:category=configurators和check=false;
4 */
5 private URL getSubscribedOverrideUrl(URL registedProviderUrl) {
6 return registedProviderUrl.setProtocol(Constants.PROVIDER_PROTOCOL)
7 .addParameters(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY, Constants.CHECK_KEY, String.valueOf(false));
8 }

开始时的registedProviderUrl如下:

  • dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5259&side=provider&timestamp=1507294508053

最终的overrideSubscribeUrl如下:

  • provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5259&side=provider&timestamp=1507294508053

二  创建overrideSubscribeListener

final OverrideListener overrideSubscribeListener = new OverrideListener(overrideSubscribeUrl, originInvoker);

overrideSubscribeListener是RegistryProtocol的内部类,来看一下声明和属性:

1     private class OverrideListener implements NotifyListener {
2 private final URL subscribeUrl;
3 private final Invoker originInvoker;
4
5 public OverrideListener(URL subscribeUrl, Invoker originalInvoker) {
6 this.subscribeUrl = subscribeUrl;
7 this.originInvoker = originalInvoker;
8 }

这里创建出来的OverrideListener实例属性如下:

  • subscribeUrl:provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=1818&side=provider&timestamp=1507366969962
  • originInvoker:该实例还是在ServiceConfig.doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)创建出来的AbstractProxyInvoker实例(具体见7.4 服务远程暴露 - 创建Exporter与启动netty服务端
    • proxy:DemoServiceImpl实例
    • type:Class<com.alibaba.dubbo.demo.DemoService>
    • url:registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&export=dubbo%3A%2F%2F10.10.10.10%3A20880%2Fcom.alibaba.dubbo.demo.DemoService%3Fanyhost%3Dtrue%26application%3Ddemo-provider%26dubbo%3D2.0.0%26generic%3Dfalse%26interface%3Dcom.alibaba.dubbo.demo.DemoService%26methods%3DsayHello%26pid%3D993%26side%3Dprovider%26timestamp%3D1507100322516&pid=993&registry=zookeeper&timestamp=1507100319830

最后,将创建出来的OverrideListener实例存储在RegistryProtocol的属性Map<URL, NotifyListener> overrideListeners中:

  • key: (overrideSubscribeUrl,也就是subscribeUrl) provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=1818&side=provider&timestamp=1507366969962
  • value:  上述的OverrideListener实例

三  真正的订阅

registry.subscribe(overrideSubscribeUrl, overrideSubscribeListener);

这里的registry是ZookeeperRegistry实例,subscribe(URL url, NotifyListener listener)方法在其父类FailbackRegistry中,如下:

 1     @Override
2 public void subscribe(URL url, NotifyListener listener) {
3 if (destroyed.get()){
4 return;
5 }
6 super.subscribe(url, listener);
7 removeFailedSubscribed(url, listener);
8 try {
9 // 向服务器端发送订阅请求
10 doSubscribe(url, listener);
11 } catch (Exception e) {
12 Throwable t = e;
13
14 List<URL> urls = getCacheUrls(url);
15 if (urls != null && urls.size() > 0) {
16 notify(url, listener, urls);
17 logger.error("Failed to subscribe " + url + ", Using cached list: " + urls + " from cache file: " + getUrl().getParameter(Constants.FILE_KEY, System.getProperty("user.home") + "/dubbo-registry-" + url.getHost() + ".cache") + ", cause: " + t.getMessage(), t);
18 } else {
19 // 如果开启了启动时检测check=true,则直接抛出异常
20 boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
21 && url.getParameter(Constants.CHECK_KEY, true);
22 boolean skipFailback = t instanceof SkipFailbackWrapperException;
23 if (check || skipFailback) {
24 if (skipFailback) {
25 t = t.getCause();
26 }
27 throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);
28 } else {
29 logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
30 }
31 }
32 // 将失败的订阅请求记录到失败列表,定时重试
33 addFailedSubscribed(url, listener);
34 }
35 }

步骤:

  • 首先调用其父类AbstractRegistry的方法,将之前创建出来的overrideSubscribeListener实例加入到overrideSubscribeUrl所对应的监听器集合中;
  • 然后从failedSubscribed/failedUnsubscribed中overrideSubscribeUrl所对应的监听器集合中删除overrideSubscribeListener实例;从failedNotified获取当前url的通知失败map Map<NotifyListener, List<URL>>,之后从中删除掉该NotifyListener实例以及其需要通知的所有的url。
  • 之后使用具体的子类(这里是ZookeeperRegistry)向服务器端发送订阅请求
  • 如果在订阅的过程中抛出了异常,那么尝试获取缓存url,如果有缓存url,则进行失败通知,之后“将失败的订阅请求记录到失败列表,定时重试”,如果没有缓存url,如果开启了启动时检测或者直接抛出的异常是SkipFailbackWrapperException,则直接抛出异常,不会“将失败的订阅请求记录到失败列表,定时重试”

将之前创建出来的overrideSubscribeListener实例加入到overrideSubscribeUrl所对应的监听器集合中

 1    private final ConcurrentMap<URL, Set<NotifyListener>> subscribed = new ConcurrentHashMap<URL, Set<NotifyListener>>();//已经订阅的<URL, Set<NotifyListener>>
2
3 /**
4 * 首先从ConcurrentMap<URL, Set<NotifyListener>> subscribed中获取key为url的集合Set<NotifyListener>,
5 * 如果该集合存在,直接将当前的NotifyListener实例存入该集合,
6 * 如果集合不存在,先创建,之后放入subscribed中,并将当前的NotifyListener实例存入刚刚创建的集合
7 *
8 * @param url 订阅条件,不允许为空,如:consumer://10.10.10.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin
9 * @param listener 变更事件监听器,不允许为空
10 */
11 public void subscribe(URL url, NotifyListener listener) {
12 if (url == null) {
13 throw new IllegalArgumentException("subscribe url == null");
14 }
15 if (listener == null) {
16 throw new IllegalArgumentException("subscribe listener == null");
17 }
18 if (logger.isInfoEnabled()) {
19 logger.info("Subscribe: " + url);
20 }
21 Set<NotifyListener> listeners = subscribed.get(url);
22 if (listeners == null) {
23 subscribed.putIfAbsent(url, new ConcurrentHashSet<NotifyListener>());
24 listeners = subscribed.get(url);
25 }
26 listeners.add(listener);
27 }

从失败集合移除overrideSubscribeListener实例

 1     /**
2 * 1 从ConcurrentMap<URL, Set<NotifyListener>> failedSubscribed 中获取当前url的订阅失败列表Set<NotifyListener>,之后从中删除掉该NotifyListener实例;
3 * 2 从ConcurrentMap<URL, Set<NotifyListener>> failedUnsubscribed 中获取当前url的反订阅失败列表Set<NotifyListener>,之后从中删除掉该NotifyListener实例;
4 * 3 从ConcurrentMap<URL, Map<NotifyListener, List<URL>>> failedNotified 中获取当前url的通知失败map Map<NotifyListener, List<URL>>,之后从中删除掉该NotifyListener实例以及其需要通知的所有的url。
5 *
6 * @param url
7 * @param listener
8 */
9 private void removeFailedSubscribed(URL url, NotifyListener listener) {
10 Set<NotifyListener> listeners = failedSubscribed.get(url);
11 if (listeners != null) {
12 listeners.remove(listener);
13 }
14 listeners = failedUnsubscribed.get(url);
15 if (listeners != null) {
16 listeners.remove(listener);
17 }
18 Map<NotifyListener, List<URL>> notified = failedNotified.get(url);
19 if (notified != null) {
20 notified.remove(listener);
21 }
22 }

ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener)

 1 protected void doSubscribe(final URL url, final NotifyListener listener) {
2 try {
3 if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {//这条分支先不说
4 String root = toRootPath();
5 ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
6 if (listeners == null) {
7 zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
8 listeners = zkListeners.get(url);
9 }
10 ChildListener zkListener = listeners.get(listener);
11 if (zkListener == null) {
12 listeners.putIfAbsent(listener, new ChildListener() {
13 public void childChanged(String parentPath, List<String> currentChilds) {
14 for (String child : currentChilds) {
15 child = URL.decode(child);
16 if (!anyServices.contains(child)) {
17 anyServices.add(child);
18 subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,
19 Constants.CHECK_KEY, String.valueOf(false)), listener);
20 }
21 }
22 }
23 });
24 zkListener = listeners.get(listener);
25 }
26 zkClient.create(root, false);
27 List<String> services = zkClient.addChildListener(root, zkListener);
28 if (services != null && services.size() > 0) {
29 for (String service : services) {
30 service = URL.decode(service);
31 anyServices.add(service);
32 subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,
33 Constants.CHECK_KEY, String.valueOf(false)), listener);
34 }
35 }
36 } else {
37 /**
38 * ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners
39 * 1 根据url获取ConcurrentMap<NotifyListener, ChildListener>,没有就创建
40 * 2 根据listener从ConcurrentMap<NotifyListener, ChildListener>获取ChildListener,没有就创建(创建的ChildListener用来监听子节点的变化)
41 * 3 创建path持久化节点
42 * 4 创建path子节点监听器
43 */
44 List<URL> urls = new ArrayList<URL>();
45 for (String path : toCategoriesPath(url)) {
46 ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
47 if (listeners == null) {
48 zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
49 listeners = zkListeners.get(url);
50 }
51 ChildListener zkListener = listeners.get(listener);
52 if (zkListener == null) {
53 listeners.putIfAbsent(listener, new ChildListener() {
54 //监听子节点列表的变化
55 public void childChanged(String parentPath, List<String> currentChilds) {
56 ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
57 }
58 });
59 zkListener = listeners.get(listener);
60 }
61 zkClient.create(path, false);//创建持久化节点/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
62 List<String> children = zkClient.addChildListener(path, zkListener);
63 if (children != null) {
64 urls.addAll(toUrlsWithEmpty(url, path, children));
65 }
66 }
67 notify(url, listener, urls);
68 }
69 } catch (Throwable e) {
70 throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
71 }
72 }

说明:

  • url(overrideSubscribeUrl):provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076
  • listener:之前创建出来的overrideSubscribeListener实例

步骤:

  • 首先获取categorypath:实际上就是获取/dubbo/{servicename}/{url中的category参数,默认是providers,这里是final URL overrideSubscribeUrl = getSubscribedOverrideUrl(registedProviderUrl);这句代码中添加到overrideSubscribeUrl上的category=configurators}
 1     private String[] toCategoriesPath(URL url) {
2 String[] categroies;
3 if (Constants.ANY_VALUE.equals(url.getParameter(Constants.CATEGORY_KEY))) {
4 categroies = new String[]{Constants.PROVIDERS_CATEGORY, Constants.CONSUMERS_CATEGORY,
5 Constants.ROUTERS_CATEGORY, Constants.CONFIGURATORS_CATEGORY};
6 } else {
7 categroies = url.getParameter(Constants.CATEGORY_KEY, new String[]{Constants.DEFAULT_CATEGORY});
8 }
9 String[] paths = new String[categroies.length];
10 for (int i = 0; i < categroies.length; i++) {
11 paths[i] = toServicePath(url) + Constants.PATH_SEPARATOR + categroies[i];
12 }
13 return paths; // /dubbo/com.alibaba.dubbo.demo.DemoService/configurators
14 }
  • 然后就是获取并创建:ConcurrentMap<overrideSubscribeUrl, ConcurrentMap<overrideSubscribeListener实例, ChildListener>> zkListeners,这里创建出来的ChildListener实例中的childChanged(String parentPath, List<String> currentChilds)方法实际上就是最终当parentPath(实际上就是上边的categorypath)下的currentChilds发生变化时,执行的逻辑。
  • 之后创建持久化节点:/dubbo/com.alibaba.dubbo.demo.DemoService/configurators
  • 然后使用AbstractZookeeperClient<TargetChildListener>的addChildListener(String path, final ChildListener listener)方法为path下的子节点添加上边创建出来的内部类ChildListener实例
  • 最后进行通知

AbstractZookeeperClient<TargetChildListener>.addChildListener(String path, final ChildListener listener)

 1     /**
2 * 1 根据path从ConcurrentMap<String, ConcurrentMap<ChildListener, TargetChildListener>> childListeners获取ConcurrentMap<ChildListener, TargetChildListener>,没有就创建
3 * 2 根据ChildListener获取TargetChildListener,没有就创建,TargetChildListener是真正的监听path的子节点变化的监听器
4 * createTargetChildListener(String path, final ChildListener listener):创建一个真正的用来执行当path节点的子节点发生变化时的逻辑
5 * 3 addTargetChildListener(path, targetListener):将刚刚创建出来的子节点监听器订阅path的变化,这样之后,path的子节点发生了变化时,TargetChildListener才会执行相应的逻辑。
6 * 而实际上TargetChildListener又会调用ChildListener的实现类的childChanged(String parentPath, List<String> currentChilds)方法,而该实现类,正好是ZookeeperRegistry中实现的匿名内部类,
7 * 在该匿名内部类的childChanged(String parentPath, List<String> currentChilds)方法中,调用了ZookeeperRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
8 */
9 public List<String> addChildListener(String path, final ChildListener listener) {
10 ConcurrentMap<ChildListener, TargetChildListener> listeners = childListeners.get(path);
11 if (listeners == null) {
12 childListeners.putIfAbsent(path, new ConcurrentHashMap<ChildListener, TargetChildListener>());
13 listeners = childListeners.get(path);
14 }
15 TargetChildListener targetListener = listeners.get(listener);
16 if (targetListener == null) {
17 listeners.putIfAbsent(listener, createTargetChildListener(path, listener));
18 targetListener = listeners.get(listener);
19 }
20 return addTargetChildListener(path, targetListener);
21 }

步骤:

  • 首先是一顿获取和创建:ConcurrentMap<categorypath, ConcurrentMap<ZookeeperRegistry的内部类ChildListener实例, TargetChildListener>> childListeners,这里主要是创建TargetChildListener;
  • 之后是真正的为path添加TargetChildListener实例。

CuratorZookeeperClient.createTargetChildListener(path, listener)

 1     public CuratorWatcher createTargetChildListener(String path, ChildListener listener) {
2 return new CuratorWatcherImpl(listener);
3 }
4
5 private class CuratorWatcherImpl implements CuratorWatcher {
6
7 private volatile ChildListener listener;
8
9 public CuratorWatcherImpl(ChildListener listener) {
10 this.listener = listener;
11 }
12
13 public void unwatch() {
14 this.listener = null;
15 }
16
17 public void process(WatchedEvent event) throws Exception {
18 if (listener != null) {
19 listener.childChanged(event.getPath(), client.getChildren().usingWatcher(this).forPath(event.getPath()));
20 }
21 }
22 }

很简单,就是创建一个监听path子节点的watcher,当path下有子节点变化时,调用listener(即传入的ZookeeperRegistry的内部类ChildListener实例的childChanged(String parentPath, List<String> currentChilds)方法)。

CuratorZookeeperClient.addTargetChildListener(String path, CuratorWatcher targetChildListener)

1     public List<String> addTargetChildListener(String path, CuratorWatcher listener) {
2 try {
3 return client.getChildren().usingWatcher(listener).forPath(path);
4 } catch (NoNodeException e) {
5 return null;
6 } catch (Exception e) {
7 throw new IllegalStateException(e.getMessage(), e);
8 }
9 }

从上边的分析我们可以看出,当path节点下的子节点发生变化的时候,会首先调用TargetChildListener的process(WatchedEvent event)方法,在该方法中又会调用ChildListener实例的childChanged(String parentPath, List<String> currentChilds)方法,那么我们来分析一下该方法:

1                             //监听子节点列表的变化
2 public void childChanged(String parentPath, List<String> currentChilds) {
3 ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
4 }

步骤:

  • 首先获取子节点urls或者是一个consumer的empty协议的url

     1     /**
    2 * 过滤出providers中与consumer匹配的url集合
    3 */
    4 private List<URL> toUrlsWithoutEmpty(URL consumer, List<String> providers) {
    5 List<URL> urls = new ArrayList<URL>();
    6 if (providers != null && providers.size() > 0) {
    7 for (String provider : providers) {
    8 provider = URL.decode(provider);
    9 if (provider.contains("://")) {
    10 URL url = URL.valueOf(provider);
    11 if (UrlUtils.isMatch(consumer, url)) {
    12 urls.add(url);
    13 }
    14 }
    15 }
    16 }
    17 return urls;
    18 }
    19
    20 /**
    21 * 1 首先过滤出providers中与consumer匹配的providerUrl集合
    22 * 2 如果providerUrl集合不为空,直接返回这个集合
    23 * 3 如果为空,首先从path中获取category,然后将consumer的协议换成empty,添加参数category=configurators
    24 * @param consumer provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076
    25 * @param path /dubbo/com.alibaba.dubbo.demo.DemoService/configurators
    26 * @param providers
    27 */
    28 private List<URL> toUrlsWithEmpty(URL consumer, String path, List<String> providers) {
    29 List<URL> urls = toUrlsWithoutEmpty(consumer, providers);
    30 if (urls == null || urls.isEmpty()) {
    31 int i = path.lastIndexOf('/');
    32 String category = i < 0 ? path : path.substring(i + 1);//configurators
    33 URL empty = consumer.setProtocol(Constants.EMPTY_PROTOCOL).addParameter(Constants.CATEGORY_KEY, category);
    34 urls.add(empty);
    35 }
    36 return urls; // empty://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=1237&side=provider&timestamp=1507352638483
    37 }
  • 之后调用ZookeeperRegistry的父类FailbackRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
     1     @Override
    2 protected void notify(URL url, NotifyListener listener, List<URL> urls) {
    3 if (url == null) {
    4 throw new IllegalArgumentException("notify url == null");
    5 }
    6 if (listener == null) {
    7 throw new IllegalArgumentException("notify listener == null");
    8 }
    9 try {
    10 doNotify(url, listener, urls);
    11 } catch (Exception t) {
    12 // 将失败的通知请求记录到失败列表,定时重试
    13 Map<NotifyListener, List<URL>> listeners = failedNotified.get(url);
    14 if (listeners == null) {
    15 failedNotified.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, List<URL>>());
    16 listeners = failedNotified.get(url);
    17 }
    18 listeners.put(listener, urls);
    19 logger.error("Failed to notify for subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);
    20 }
    21 }
    22
    23 protected void doNotify(URL url, NotifyListener listener, List<URL> urls) {
    24 super.notify(url, listener, urls);
    25 }

    说明:这里传入的

    • url(overrideSubscribeUrl):provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076

    • listener:之前创建出来的overrideSubscribeListener实例
    • urls:[ empty://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076 ]
    • 这里首先执行父类的AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls),如果失败,则获取或创建ConcurrentMap<overrideSubscribeUrl, Map<overrideSubscribeListener实例, urls>> failedNotified,后续做重试

来看一下通知的最核心部分:

AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls)

 1     /**
2 * 1 首先遍历List<URL> urls,将urls按照category进行分类,存储在Map<"categoryName", List<URL>> result中;
3 * 2 之后遍历result:(每遍历一次,都是一个新的category)
4 * (1)将Map<"categoryName", List<URL>>存储在ConcurrentMap<URL, Map<String, List<URL>>> notified的Map<String, List<URL>>中
5 * (2)进行properties设置和文件保存
6 * (3)调用传入放入listener的notify()方法。
7 * @param url
8 * @param listener
9 * @param urls
10 */
11 protected void notify(URL url, NotifyListener listener, List<URL> urls) {
12 if (url == null) {
13 throw new IllegalArgumentException("notify url == null");
14 }
15 if (listener == null) {
16 throw new IllegalArgumentException("notify listener == null");
17 }
18 if ((urls == null || urls.size() == 0)
19 && !Constants.ANY_VALUE.equals(url.getServiceInterface())) {
20 logger.warn("Ignore empty notify urls for subscribe url " + url);
21 return;
22 }
23 if (logger.isInfoEnabled()) {
24 logger.info("Notify urls for subscribe url " + url + ", urls: " + urls);
25 }
26 /**
27 * 遍历List<URL> urls,将urls按照category进行分类
28 */
29 Map<String, List<URL>> result = new HashMap<String, List<URL>>(); //{ "categoryName" : List<URL> }
30 for (URL u : urls) {
31 if (UrlUtils.isMatch(url, u)) {
32 String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
33 List<URL> categoryList = result.get(category);
34 if (categoryList == null) {
35 categoryList = new ArrayList<URL>();
36 result.put(category, categoryList);
37 }
38 categoryList.add(u);
39 }
40 }
41 if (result.size() == 0) {
42 return;
43 }
44 Map<String, List<URL>> categoryNotified = notified.get(url);
45 if (categoryNotified == null) {
46 notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
47 categoryNotified = notified.get(url);
48 }
49 for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
50 String category = entry.getKey();
51 List<URL> categoryList = entry.getValue();
52 categoryNotified.put(category, categoryList);//填充notified集合
53 saveProperties(url);//该行代码为什么不写在循环体外边
54 listener.notify(categoryList);
55 }
56 }

说明:这里传入的

  • url(overrideSubscribeUrl):provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076

  • listener:之前创建出来的overrideSubscribeListener实例
  • urls:[ empty://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=9544&side=provider&timestamp=1507643800076 ]

步骤:

  • 首先遍历List<URL> urls,将urls按照category进行分类,存储在Map<"categoryName", List<URL>> result中;
  • 然后获取或创建ConcurrentMap<overrideSubscribeUrl, Map<"categoryName", subList(urls)>> notified
  • 最后遍历Map<"categoryName", List<URL>> result
    • 去填充notified集合
    • 保存传入的url到Properties properties(本地磁盘缓存中)
    • 调用传入的listener的notify方法(注意:这里调用的正是文章开头创建的overrideSubscribeListener实例的notify方法)

AbstractRegistry.saveProperties(URL url)

 1     /**
2 * 1 按照url从将ConcurrentMap<URL, Map<String, List<URL>>> notified中将Map<String, List<URL>>拿出来,之后将所有category的list组成一串buf(以空格分隔)
3 * 2 将< serviceKey<->buf >写入本地磁盘缓存中:Properties properties
4 * 3 将AtomicLong lastCacheChanged加1
5 * 4 之后根据syncSaveFile判断时同步保存properties到文件,还是异步保存properties到文件
6 * @param url
7 */
8 private void saveProperties(URL url) {
9 if (file == null) {
10 return;
11 }
12
13 try {
14 StringBuilder buf = new StringBuilder();
15 Map<String, List<URL>> categoryNotified = notified.get(url);
16 if (categoryNotified != null) {
17 for (List<URL> us : categoryNotified.values()) {
18 for (URL u : us) {
19 if (buf.length() > 0) {
20 buf.append(URL_SEPARATOR);
21 }
22 buf.append(u.toFullString());
23 }
24 }
25 }
26 properties.setProperty(url.getServiceKey(), buf.toString());
27 long version = lastCacheChanged.incrementAndGet();
28 if (syncSaveFile) {
29 doSaveProperties(version);
30 } else {
31 registryCacheExecutor.execute(new SaveProperties(version));
32 }
33 } catch (Throwable t) {
34 logger.warn(t.getMessage(), t);
35 }
36 }

说明:

  • 入参:url:provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5033&side=provider&timestamp=1507720343596
  • properties:{ "com.alibaba.dubbo.demo.DemoService" -> "empty://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5033&side=provider&timestamp=1507720343596" }
  • 最后采用异步线程将properties中的内容写入到文件中

AbstractRegistry$SaveProperties

 1     private class SaveProperties implements Runnable {
2 private long version;
3
4 private SaveProperties(long version) {
5 this.version = version;
6 }
7
8 public void run() {
9 doSaveProperties(version);
10 }
11 }

AbstractRegistry.doSaveProperties(long version)

 1     /**
2 * 1 先将文件中的内容读取到一个新的Properties newProperties中;
3 * 2 之后将properties中的信息写入这个newProperties中;
4 * 3 之后创建dubbo-registry-10.211.55.5.cache.lock文件;
5 * 4 最后将这个newProperties中的内容写入到文件中
6 */
7 public void doSaveProperties(long version) {
8 if (version < lastCacheChanged.get()) {
9 return;
10 }
11 if (file == null) {
12 return;
13 }
14 Properties newProperties = new Properties();
15 // 保存之前先读取一遍,防止多个注册中心之间冲突
16 InputStream in = null;
17 try {
18 if (file.exists()) {
19 in = new FileInputStream(file);
20 newProperties.load(in);
21 }
22 } catch (Throwable e) {
23 logger.warn("Failed to load registry store file, cause: " + e.getMessage(), e);
24 } finally {
25 if (in != null) {
26 try {
27 in.close();
28 } catch (IOException e) {
29 logger.warn(e.getMessage(), e);
30 }
31 }
32 }
33 // 保存
34 try {
35 newProperties.putAll(properties);
36 File lockfile = new File(file.getAbsolutePath() + ".lock");
37 if (!lockfile.exists()) {
38 lockfile.createNewFile();//创建lock文件
39 }
40 RandomAccessFile raf = new RandomAccessFile(lockfile, "rw");
41 try {
42 FileChannel channel = raf.getChannel();
43 try {
44 FileLock lock = channel.tryLock();
45 if (lock == null) {
46 throw new IOException("Can not lock the registry cache file " + file.getAbsolutePath() + ", ignore and retry later, maybe multi java process use the file, please config: dubbo.registry.file=xxx.properties");
47 }
48 // 保存
49 try {
50 if (!file.exists()) {
51 file.createNewFile();
52 }
53 FileOutputStream outputFile = new FileOutputStream(file);
54 try {
55 newProperties.store(outputFile, "Dubbo Registry Cache");
56 } finally {
57 outputFile.close();
58 }
59 } finally {
60 lock.release();
61 }
62 } finally {
63 channel.close();
64 }
65 } finally {
66 raf.close();
67 }
68 } catch (Throwable e) {
69 if (version < lastCacheChanged.get()) {
70 return;
71 } else {
72 registryCacheExecutor.execute(new SaveProperties(lastCacheChanged.incrementAndGet()));
73 }
74 logger.warn("Failed to save registry store file, cause: " + e.getMessage(), e);
75 }
76 }

步骤见注释。这里有一个version,实际上是一个CAS判断,我们在saveProperties(URL url)方法中执行了long version = lastCacheChanged.incrementAndGet();之后在doSaveProperties(long version)进行if (version < lastCacheChanged.get())判断,如果满足这个条件,说明当前线程在进行doSaveProperties(long version)时,已经有其他线程执行了saveProperties(URL url),马上就要执行doSaveProperties(long version),所以当前线程放弃操作,让后边的这个线程来做保存操作。

保存操作执行之后,会在文件夹/Users/jigangzhao/.dubbo下生成两个文件:

  • dubbo-registry-10.211.55.5.cache
  • dubbo-registry-10.211.55.5.cache.lock

前者的内容:

1
2
#Wed Oct 11 19:42:29 CST 2017
com.alibaba.dubbo.demo.DemoService=empty\://10.10.10.10\:20880/com.alibaba.dubbo.demo.DemoService?anyhost\=true&application\=demo-provider&category\=configurators&check\=false&dubbo\=2.0.0&generic\=false&interface\=com.alibaba.dubbo.demo.DemoService&methods\=sayHello&pid\=5165&side\=provider&timestamp\=1507722024953

最后就是OverrideListener.notify(List<URL> urls)

 1     /**
2 * 重新export
3 * 1.protocol中的exporter destroy问题
4 * 1.要求registryprotocol返回的exporter可以正常destroy
5 * 2.notify后不需要重新向注册中心注册
6 * 3.export 方法传入的invoker最好能一直作为exporter的invoker.
7 */
8 private class OverrideListener implements NotifyListener {
9 private final URL subscribeUrl;
10 private final Invoker originInvoker;
11
12 public OverrideListener(URL subscribeUrl, Invoker originalInvoker) {
13 this.subscribeUrl = subscribeUrl;
14 this.originInvoker = originalInvoker;
15 }
16
17 /**
18 * 目的:
19 * 对原本注册了的providerUrl进行校验,如果url发生了变化,那么要重新export
20 *
21 * @param urls 已注册信息列表,总不为空,含义同{@link com.alibaba.dubbo.registry.RegistryService#lookup(URL)}的返回值。
22 */
23 public synchronized void notify(List<URL> urls) {
24 logger.debug("original override urls: " + urls);
25 List<URL> matchedUrls = getMatchedUrls(urls, subscribeUrl);
26 logger.debug("subscribe url: " + subscribeUrl + ", override urls: " + matchedUrls);
27 //没有匹配的
28 if (matchedUrls.isEmpty()) {
29 return;
30 }
31
32 List<Configurator> configurators = RegistryDirectory.toConfigurators(matchedUrls);//这里是一个空列表
33
34 final Invoker<?> invoker;
35 if (originInvoker instanceof InvokerDelegete) {
36 invoker = ((InvokerDelegete<?>) originInvoker).getInvoker();
37 } else {
38 invoker = originInvoker;
39 }
40 //最原始的invoker
41 URL originUrl = RegistryProtocol.this.getProviderUrl(invoker);//dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5279&side=provider&timestamp=1507723571451
42 String key = getCacheKey(originInvoker);//dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5279&side=provider&timestamp=1507723571451
43 ExporterChangeableWrapper<?> exporter = bounds.get(key);//在doLocalExport方法中已经存放在这里了
44 if (exporter == null) {
45 logger.warn(new IllegalStateException("error state, exporter should not be null"));
46 return;
47 }
48 //当前的,可能经过了多次merge
49 URL currentUrl = exporter.getInvoker().getUrl();//dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=5279&side=provider&timestamp=1507723571451
50 //与本次配置merge的
51 URL newUrl = getConfigedInvokerUrl(configurators, originUrl);
52 if (!currentUrl.equals(newUrl)) {
53 RegistryProtocol.this.doChangeLocalExport(originInvoker, newUrl);//重新将invoker暴露为exporter
54 logger.info("exported provider url changed, origin url: " + originUrl + ", old export url: " + currentUrl + ", new export url: " + newUrl);
55 }
56 }
57
58 private List<URL> getMatchedUrls(List<URL> configuratorUrls, URL currentSubscribe) {
59 List<URL> result = new ArrayList<URL>();
60 for (URL url : configuratorUrls) {
61 URL overrideUrl = url;
62 // 兼容旧版本
63 if (url.getParameter(Constants.CATEGORY_KEY) == null
64 && Constants.OVERRIDE_PROTOCOL.equals(url.getProtocol())) {
65 overrideUrl = url.addParameter(Constants.CATEGORY_KEY, Constants.CONFIGURATORS_CATEGORY);
66 }
67
68 //检查是不是要应用到当前服务上
69 if (UrlUtils.isMatch(currentSubscribe, overrideUrl)) {
70 result.add(url);
71 }
72 }
73 return result;
74 }
75
76 //合并配置的url
77 private URL getConfigedInvokerUrl(List<Configurator> configurators, URL url) {
78 for (Configurator configurator : configurators) {
79 url = configurator.configure(url);
80 }
81 return url;
82 }
83 }

最后:总结一下:

当前的provider订阅了/dubbo/com.alibaba.dubbo.demo.DemoService/configurators,当其下的子节点发生变化时,如果其下的子节点的url或者当前的providerUrl发生了变化,需要重新暴露

重新暴露:

 1     /**
2 * 对修改了url的invoker重新export
3 *
4 * @param originInvoker
5 * @param newInvokerUrl
6 */
7 @SuppressWarnings("unchecked")
8 private <T> void doChangeLocalExport(final Invoker<T> originInvoker, URL newInvokerUrl) {
9 String key = getCacheKey(originInvoker);
10 final ExporterChangeableWrapper<T> exporter = (ExporterChangeableWrapper<T>) bounds.get(key);
11 if (exporter == null) {
12 logger.warn(new IllegalStateException("error state, exporter should not be null"));
13 } else {
14 final Invoker<T> invokerDelegete = new InvokerDelegete<T>(originInvoker, newInvokerUrl);
15 exporter.setExporter(protocol.export(invokerDelegete));
16 }
17 }

7.8 服务暴露总结

服务提供端配置:

 1     <!-- 提供方应用信息,用于计算依赖关系 -->
2 <dubbo:application name="demo-provider"/>
3
4 <!-- 使用zookeeper注册中心,并使用curator客户端 -->
5 <dubbo:registry protocol="zookeeper" address="10.211.55.5:2181" client="curator"/>
6
7 <!-- 用dubbo协议在20880端口暴露服务 -->
8 <dubbo:protocol name="dubbo" port="20880"/>
9
10 <!-- 和本地bean一样实现服务 -->
11 <bean id="demoService" class="com.alibaba.dubbo.demo.provider.DemoServiceImpl"/>
12
13 <!-- 声明需要暴露的服务接口 -->
14 <dubbo:service interface="com.alibaba.dubbo.demo.DemoService" ref="demoService"/>

一 ServiceBean

1 继承实现关系

2 最终的ServiceBean实例

-->String id: "com.alibaba.dubbo.demo.DemoService"
-->String beanName: "com.alibaba.dubbo.demo.DemoService"
-->ApplicationContext applicationContext: ClassPathXmlApplicationContext实例
-->supportedApplicationListener:true
-->List<URL> urls: ["dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461"]
-->List<Exporter<?>> exporters:[
-->InjvmExporter实例
-->String key:com.alibaba.dubbo.demo.DemoService
-->Map<String, Exporter<?>> exporterMap: {"com.alibaba.dubbo.demo.DemoService ", 当前的JvmExporter实例}
-->Invoker invoker:经过filter包装的AbstractProxyInvoker实例
-->RegistryProtocol返回的新的Exporter实例
-->Exporter exporter: ExporterChangeableWrapper<T> exporter实例
-->Invoker originInvoker:经过filter包装的AbstractProxyInvoker实例
-->Exporter exporter: DubboExporter
-->Registry registry: 上边的ZookeeperRegistry实例
]
-->String interfaceName:"com.alibaba.dubbo.demo.DemoService"
-->Class<?> interfaceClass:interface com.alibaba.dubbo.demo.DemoService
-->T ref: DemoServiceImpl实例
-->String path:"com.alibaba.dubbo.demo.DemoService"
-->List<ProtocolConfig> protocols:[解析:<dubbo:protocol name="dubbo" port="20880" id="dubbo" />]
-->ApplicationConfig application:[解析:<dubbo:application name="demo-provider" id="demo-provider" />]
-->List<RegistryConfig> registries:[解析:<dubbo:registry address="10.211.55.5:2181" protocol="zookeeper" id="com.alibaba.dubbo.config.RegistryConfig" />]

二 调用简图

三 代码调用链

ServiceBean.onApplicationEvent(ApplicationEvent event)
-->ServiceConfig.export()
-->doExport()
-->doExportUrls()
-->doExportUrlsFor1Protocol(ProtocolConfig protocolConfig, List<URL> registryURLs)
protocolConfig:<dubbo:protocol name="dubbo" port="20880" id="dubbo" />
registryURLs:registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&pid=3141&registry=zookeeper&timestamp=1510021313960
<!-- 一 本地暴露 -->
-->exportLocal(url)
url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3141&side=provider&timestamp=1510021401013
//1.1 将实现类ref封装成Invoker
-->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
proxy:DemoServiceImpl实例(即ref实例)
type:interface com.alibaba.dubbo.demo.DemoService
url:injvm://127.0.0.1/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3141&side=provider&timestamp=1510021401013
-->Wrapper.getWrapper(Class DemoServiceImpl)
-->new AbstractProxyInvoker<T>(proxy, type, url)
//1.2 将实现类Invoker暴露为Exporter
-->ProtocolFilterWrapper.buildInvokerChain(final Invoker<T> invoker, String key, String group)
组建invoker链,实际上只有最后一个是真正的AbstractProxyInvoker实例,前边的都是filter。
invoker:AbstractProxyInvoker实例
key:service.filter
group:provider
-->InjvmProtocol.export(Invoker<T> invoker)
invoker:经过filter包装的invoker
-->new InjvmExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap)
invoker:经过filter包装的invoker
key:com.alibaba.dubbo.demo.DemoService
exporterMap:传入时为空,构造器执行后为{"com.alibaba.dubbo.demo.DemoService ", 当前的JvmExporter实例}
-->List<Exporter<?>> exporters.add(上述的exporter)
<!-- 二 远程暴露 -->
//2.1 将实现类ref封装成Invoker
-->JavassistProxyFactory.getInvoker(T proxy, Class<T> type, URL url)
proxy:DemoServiceImpl实例(即ref实例)
type:interface com.alibaba.dubbo.demo.DemoService
url:registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&client=curator&dubbo=2.0.0&export=dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3141&side=provider&timestamp=1510021401013&pid=3141&registry=zookeeper&timestamp=1510021313960
-->Wrapper.getWrapper(Class DemoServiceImpl)
-->new AbstractProxyInvoker<T>(proxy, type, url)
-->RegistryProtocol.export(final Invoker<T> originInvoker)
originInvoker:上述的AbstractProxyInvoker实例
//2.2 将invoker转化为exporter
-->doLocalExport(originInvoker)
-->new InvokerDelegete(Invoker<T> invoker, URL url)
invoker:原始的AbstractProxyInvoker实例
url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461
-->ProtocolFilterWrapper.buildInvokerChain(final Invoker<T> invoker, String key, String group)
组建invoker链,实际上只有最后一个是真正的InvokerDelegete实例,前边的都是filter
invoker:InvokerDelegete实例
key:service.filter
group:provider
-->DubboProtocol.export(Invoker<T> invoker)
invoker:经过filter包装的InvokerDelegete实例
-->new DubboExporter(Invoker<T> invoker, String key, Map<String, Exporter<?>> exporterMap)
invoker:经过filter包装的InvokerDelegete实例
key:com.alibaba.dubbo.demo.DemoService:20880 (group/servicename:version:port)
exporterMap:传入时为空,构造器执行后又执行了put,为{"com.alibaba.dubbo.demo.DemoService:20880", 当前的DubboExporter实例}
//2.3 开启netty服务端监听客户端请求
-->openServer(URL url)
url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461
-->createServer(URL url)
-->HeaderExchanger.bind(URL url, ExchangeHandler handler)
url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&channel.readonly.sent=true&codec=dubbo&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461 handler:DubboProtocol.requestHandler
-->new DecodeHandler(new HeaderExchangeHandler(handler)))
-->NettyTransporter.bind(URL url, ChannelHandler listener)
url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&channel.readonly.sent=true&codec=dubbo&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461 listener:上边的DecodeHandler实例
-->new NettyServer(URL url, ChannelHandler handler)
-->ChannelHandler.wrapInternal(ChannelHandler handler, URL url)
handler:上边的DecodeHandler实例
url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&channel.readonly.sent=true&codec=dubbo&dubbo=2.0.0&generic=false&heartbeat=60000&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&threadname=DubboServerHandler-10.10.10.10:20880&timestamp=1510023456461
-->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler)))
-->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例
-->doOpen()//开启netty服务
-->new HeaderExchangeServer(Server server)
server:上述的NettyServer
-->startHeatbeatTimer()
-->new ExporterChangeableWrapper(Exporter<T> exporter, Invoker<T> originInvoker)
exporter:上述的DubboExporter实例
originInvoker:原始的AbstractProxyInvoker实例
//2.4 创建Registry:创建zkclient,连接zk
-->getRegistry(final Invoker<?> originInvoker)
-->AbstractRegistryFactory.getRegistry(URL url)
url:zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&export=dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461&pid=3508&timestamp=1510023439825
-->ZookeeperRegistryFactory.createRegistry(URL url)
-->new ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter)
url:zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=3508&timestamp=1510023439825
-->ZkclientZookeeperTransporter.connect(URL url)
-->new ZkclientZookeeperClient(URL url)
url:zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-provider&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=3508&timestamp=1510023439825
-->new ZkClient(url.getBackupAddress())//这里是10.211.55.5:2181
-->AbstractRegistryFactory.Map<String, Registry> REGISTRIES.put("zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService", 上边的ZookeeperRegistry实例)
//2.5 向注册中心注册服务
-->registry.register(registedProviderUrl)
-->ZookeeperRegistry.doRegister(URL url)
url:dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461
-->AbstractZookeeperClient.create(String path, boolean ephemeral)
path:/dubbo/com.alibaba.dubbo.demo.DemoService/providers/dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461
ephemeral=true
//2.6 订阅override数据
-->ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener)
url:provider://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&category=configurators&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=3508&side=provider&timestamp=1510023456461
listener:RegistryProtocol.OverrideListener实例
//2.7 创建新的Exporter实例
-->new Exporter<T>()//包含了上边的ExporterChangeableWrapper<T> exporter实例 + ZookeeperRegistry实例

8.1 构建客户端总体流程

一 示例

1 配置文件:

 1 <?xml version="1.0" encoding="UTF-8"?>
2 <beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
3 xmlns:dubbo="http://code.alibabatech.com/schema/dubbo"
4 xmlns="http://www.springframework.org/schema/beans"
5 xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
6 http://code.alibabatech.com/schema/dubbo http://code.alibabatech.com/schema/dubbo/dubbo.xsd">
7 <!-- 消费方应用名,用于计算依赖关系,不是匹配条件,不要与提供方一样 -->
8 <dubbo:application name="demo-consumer"/>
9 <!-- 使用zookeeper注册中心 -->
10 <dubbo:registry protocol="zookeeper" address="10.211.55.5:2181"/>
11 <!-- 生成远程服务代理,可以和本地bean一样使用demoService -->
12 <dubbo:reference id="demoService" check="false" interface="com.alibaba.dubbo.demo.DemoService"/>
13 </beans>

2 Consumer

 1 package com.alibaba.dubbo.demo.consumer;
2
3 import com.alibaba.dubbo.demo.DemoService;
4 import org.springframework.context.support.ClassPathXmlApplicationContext;
5
6 public class Consumer {
7 public static void main(String[] args) {
8 ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"META-INF/spring/dubbo-demo-consumer.xml"});
9 context.start();
10
11 DemoService demoService = (DemoService) context.getBean("demoService"); // 获取远程服务代理
12 String hello = demoService.sayHello("world"); // 执行远程方法
13
14 System.out.println(hello); // 显示调用结果
15 }
16 }

先来看DemoService demoService = (DemoService) context.getBean("demoService"); // 获取远程服务代理。

二 调用简图

三 总体代码调用链

ReferenceConfig.init()
-->createProxy(Map<String, String> map)
//一 获取Invoker
-->RegistryProtocol.refer(Class<T> type, URL url)
//1 获取注册中心:创建ZkClient实例,连接zk
-->Registry registry = registryFactory.getRegistry(url)
-->AbstractRegistryFactory.getRegistry(URL url)
-->ZookeeperRegistryFactory.createRegistry(URL url)
-->new ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter)
-->ZkclientZookeeperTransporter.connect(URL url)
-->new ZkclientZookeeperClient(URL url)
-->new ZkClient(url.getBackupAddress())
-->AbstractRegistryFactory.Map<String, Registry> REGISTRIES.put("zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService", 上边的ZookeeperRegistry实例)
-->doRefer(Cluster cluster, Registry registry, Class<T> type, URL url)
-->new RegistryDirectory<T>(type, url)
//2 向注册中心注册服务
-->registry.register(url)
-->ZookeeperRegistry.doRegister(URL url)
-->AbstractZookeeperClient.create(String path, boolean ephemeral)
    //3 订阅providers、configurators、routers
-->RegistryDirectory.subscribe(URL url)
-->ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener)
//3.1 会获取当前节点下已经存在的字节点(第一次服务发现发生在这里),添加子节点变化监听器
-->List<String> children = zkClient.addChildListener(path, zkListener)
-->AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls)
-->saveProperties(url)
-->RegistryDirectory.notify(List<URL> urls)
//仅仅针对的是providers
-->refreshInvoker(List<URL> invokerUrls)
-->toInvokers(List<URL> urls
-->ProtocolFilterWrapper.refer(Class<T> type, URL url)
-->DubboProtocol.refer(Class<T> serviceType, URL url)
//3.1.1 创建ExchangeClient,对第一次服务发现providers路径下的相关url建立长连接
-->getClients(URL url)
-->getSharedClient(URL url)
-->ExchangeClient exchangeClient = initClient(url)
-->Exchangers.connect(url, requestHandler)
-->HeaderExchanger.connect(URL url, ExchangeHandler handler)
-->new DecodeHandler(new HeaderExchangeHandler(handler)))
-->Transporters.connect(URL url, ChannelHandler... handlers)
-->NettyTransporter.connect(URL url, ChannelHandler listener)
-->new NettyClient(url, listener)
-->new MultiMessageHandler(HeartbeatHandler(AllChannelHandler(handler)))
-->getChannelCodec(url)//获取Codec2,这里是DubboCountCodec实例
-->doOpen()//开启netty客户端
-->doConnect()//连接服务端,建立长连接
-->new HeaderExchangeClient(Client client, boolean needHeartbeat)//上述的NettyClient实例,needHeartbeat:true
-->startHeatbeatTimer()//启动心跳计数器
-->ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap)/
-->Map<String, ReferenceCountExchangeClient> referenceClientMap.put("10.10.10.10:20880", 上边的ReferenceCountExchangeClient实例)
//3.2 创建DubboInvoker
-->new DubboInvoker(Class<T> serviceType, URL url, ExchangeClient[] clients, Set<Invoker<?>> invokers)
-->DubboProtocol.Set<Invoker<?>> invokers.add(上边的DubboInvoker实例)
-->ProtocolFilterWrapper.buildInvokerChain(final Invoker<T> invoker, String key, String group)
-->new InvokerDelegete(Invoker<T> invoker, URL url, URL providerUrl)
//3.3 将创建出来的Invoker缓存起来
-->newUrlInvokerMap.put("dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=16001&register.ip=10.10.10.10&remote.timestamp=1510127991625&side=consumer&timestamp=1510128022123", 上边的InvokerDelegate实例)
-->toMethodInvokers(newUrlInvokerMap)
-->Map<String, List<Invoker<T>>> newMethodInvokerMap:{sayHello=[InvokerDelegete实例], *=[InvokerDelegete实例]}
//4 将directory封装成一个ClusterInvoker(MockClusterInvoker)
-->cluster.join(directory)
-->Cluster$Adaptive.join(directory)
-->ExtensionLoader.getExtensionLoader(Cluster.class).getExtension("failover")//MockClusterWrapper包装FailoverCluster
-->MockClusterWrapper.join(Directory<T> directory)
-->FailoverCluster.join(Directory<T> directory)
-->new FailoverClusterInvoker<T>(directory)
-->MockClusterInvoker(Directory<T> directory, Invoker<T> invoker)//invoker:上边的FailoverClusterInvoker实例
//二 获取代理
-->JavassistProxyFactory.getProxy(Invoker<T> invoker, Class<?>[] interfaces)//invoker:上边的MockClusterInvoker实例, interfaces:[interface com.alibaba.dubbo.demo.DemoService, interface com.alibaba.dubbo.rpc.service.EchoService]
-->Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker))
-->Proxy.getProxy(ClassLoader cl, Class<?>... ics)//使用javassist获取一个动态类
-->new InvokerInvocationHandler(invoker)//invoker:上边的MockClusterInvoker实例

极简版流程图:

proxy0.xxxMethod()
-->InvokerInvocationHandler.invoke
// RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={}]
-->MockClusterInvoker.invoke(Invocation invocation)
-->FailoverClusterInvoker.invoke(final Invocation invocation)
-->RegistryDirectory.list(Invocation invocation) //根据RpcInvocation中的methodName获取Invoker
-->router过滤
-->loadBalancer选取一个Invoker
-->执行filter链
// RpcInvocation [methodName=sayHello, parameterTypes=[class java.lang.String], arguments=[world], attachments={path=com.alibaba.dubbo.demo.DemoService, interface=com.alibaba.dubbo.demo.DemoService, version=2.0.0, timeout=60000, group=dev}]
-->DubboInvoker.invoke(Invocation inv)

在服务端会根据RpcInvocation中的attachments中的path、group、version以及从channel中获取的port拼接一个serviceKey:group/path:version:port,例如:dev/com.alibaba.dubbo.demo.DemoService:2.0.0:20880,之后根据这个serviceKey从服务端获取DubboExporter。

注意:

1、每一个代理都会有自己的一个MockClusterInvoker,也就有自己的一个RegistryDirectory,所以,假设A引用了服务B和C,B和C中都有sayHello(String name)方法时,B的RegistryDirectory存储自己的sayHello=[Invoker对象],C的RegistryDirectory存储自己的sayHello=[Invoker对象]。

2、假设A调用了服务B,B中有方法String sayHello(String name)/String sayHello(String name, Integer age)时,在B的RegistryDirectory只存储一份的sayHello=[Invoker对象],注意这里一个Invoker对象其实对应一个Provider实例,根据从serviceKey:group/path:version:port获取出DubboExporter,DubboExporter获取到AbstractProxyInvoker,AbstractProxyInvoker中的wrapper类中就有String sayHello(String name)/String sayHello(String name, Integer age)两个方法,根据Request中的方法名、参数类型和参数值就可以找出执行哪一个方法。

准备工作:

先启动两个provider:

  • dubbo://10.211.55.5:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.7&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=318&revision=2.5.7&side=provider&timestamp=1510225244315
  • dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25215&side=provider&timestamp=1510225334486

来看一下ReferenceBean的继承实现关系图:

在执行DemoService demoService = (DemoService) context.getBean("demoService")时,由于ReferenceBean是一个FactoryBean,所以这里会通过FactoryBean.getObject方法获取Bean。

来看一下ReferenceBean的核心代码:

    public Object getObject() throws Exception {
return get();
} public synchronized T get() {
if (destroyed) {
throw new IllegalStateException("Already destroyed!");
}
if (ref == null) {
init();
}
return ref;
} private void init() {
...
ref = createProxy(map);
} private T createProxy(Map<String, String> map) {
...
if (urls.size() == 1) {
invoker = refprotocol.refer(interfaceClass, urls.get(0));
}
...
// 创建服务代理
return (T) proxyFactory.getProxy(invoker);
}

最核心的两行代码如上红色。

一 使用Protocol将interfaceClass转化为Invoker

1 invoker = refprotocol.refer(interfaceClass, urls.get(0))

这里的refprotocol是Protocol$Adaptive实例。

public class Protocol$Adaptive implements com.alibaba.dubbo.rpc.Protocol {
...
public com.alibaba.dubbo.rpc.Invoker refer(Class arg0, com.alibaba.dubbo.common.URL arg1) throws com.alibaba.dubbo.rpc.RpcException {
if (arg1 == null)
throw new IllegalArgumentException("url == null");
com.alibaba.dubbo.common.URL url = arg1;
String extName = (url.getProtocol() == null ? "dubbo" : url.getProtocol());
if(extName == null)
throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.Protocol) name from url(" + url.toString() + ") use keys([protocol])");
com.alibaba.dubbo.rpc.Protocol extension = (com.alibaba.dubbo.rpc.Protocol) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.Protocol.class).getExtension(extName);
return extension.refer(arg0, arg1);
}
...
}

这里extName="registry"。之后经过ProtocolListenerWrapper.refer->ProtocolFilterWrapper.refer->RegistryProtocol.refer,前两步什么都不做(registry协议)。来看RegistryProtocol.refer方法核心代码:

1     public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
2 url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
3 Registry registry = registryFactory.getRegistry(url);
4 ...
5 return doRefer(cluster, registry, type, url);
6 }

参数:

  • url:registry://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=25267&refer=application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&side=consumer&timestamp=1510225913509&registry=zookeeper&timestamp=1510225984358
  • type: interface com.alibaba.dubbo.demo.DemoService

第一行代码执行完成之后,替换了协议,此时的url为:

zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=25267&refer=application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&side=consumer&timestamp=1510225913509&timestamp=1510225984358

之后开始获取Registry。这里的registryFactory是RegistryFactory$Adaptive实例。

 1 public class RegistryFactory$Adaptive implements com.alibaba.dubbo.registry.RegistryFactory {
2 public com.alibaba.dubbo.registry.Registry getRegistry(com.alibaba.dubbo.common.URL arg0) {
3 if (arg0 == null)
4 throw new IllegalArgumentException("url == null");
5 com.alibaba.dubbo.common.URL url = arg0;
6 String extName = ( url.getProtocol() == null ? "dubbo" : url.getProtocol() );//zookeeper
7 if(extName == null)
8 throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.registry.RegistryFactory) name from url(" + url.toString() + ") use keys([protocol])");
9 com.alibaba.dubbo.registry.RegistryFactory extension = (com.alibaba.dubbo.registry.RegistryFactory)ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.registry.RegistryFactory.class).getExtension(extName);
10 return extension.getRegistry(arg0);
11 }
12 }

这里的extName是zookeeper。之后执行ZookeeperRegistryFactory的父类AbstractRegistryFactory.getRegistry,如下:

 1     public Registry getRegistry(URL url) {
2 url = url.setPath(RegistryService.class.getName())
3 .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName())
4 .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY);
5 String key = url.toServiceString();
6 // 锁定注册中心获取过程,保证注册中心单一实例
7 LOCK.lock();
8 try {
9 Registry registry = REGISTRIES.get(key);
10 if (registry != null) {
11 return registry;
12 }
13 registry = createRegistry(url);
14 if (registry == null) {
15 throw new IllegalStateException("Can not create registry " + url);
16 }
17 REGISTRIES.put(key, registry);
18 return registry;
19 } finally {
20 // 释放锁
21 LOCK.unlock();
22 }
23 }

经过处理的url为:

zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=25267&timestamp=1510225984358

之后调用ZookeeperRegistryFactory.createRegistry(URL url):

1     public Registry createRegistry(URL url) {
2 return new ZookeeperRegistry(url, zookeeperTransporter);
3 }

这里的zookeeperTransporter为ZookeeperTransporter$Adaptive实例。

 1     public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {
2 super(url);
3 if (url.isAnyHost()) {
4 throw new IllegalStateException("registry address == null");
5 }
6 String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);
7 if (!group.startsWith(Constants.PATH_SEPARATOR)) {
8 group = Constants.PATH_SEPARATOR + group;
9 }
10 this.root = group;
11 zkClient = zookeeperTransporter.connect(url);
12 zkClient.addStateListener(new StateListener() {
13 public void stateChanged(int state) {
14 if (state == RECONNECTED) {
15 try {
16 recover();
17 } catch (Exception e) {
18 logger.error(e.getMessage(), e);
19 }
20 }
21 }
22 });
23 }

通过super(url)这句代码,调用了ZookeeperRegistry的父类FailbackRegistry(启动失败处理器:注册失败/注销失败/订阅失败/反订阅失败/通知失败)和AbstractRegistry(将信息写入properties文件,进行相应通知-这里没有url的订阅器,所以没做什么事)。

然后获取ZkClient客户端,最后添加失败重连监听器。

执行zookeeperTransporter.connect(url),该类中的extName是"zkClient"(我们在provider部分使用了curator)。之后执行ZkclientZookeeperTransporter.connect:

1     public ZookeeperClient connect(URL url) {
2 return new ZkclientZookeeperClient(url);
3 }
 1     public ZkclientZookeeperClient(URL url) {
2 super(url);
3 client = new ZkClientWrapper(url.getBackupAddress(), 30000);
4 client.addListener(new IZkStateListener() {
5 public void handleStateChanged(KeeperState state) throws Exception {
6 ZkclientZookeeperClient.this.state = state;
7 if (state == KeeperState.Disconnected) {
8 stateChanged(StateListener.DISCONNECTED);
9 } else if (state == KeeperState.SyncConnected) {
10 stateChanged(StateListener.CONNECTED);
11 }
12 }
13
14 public void handleNewSession() throws Exception {
15 stateChanged(StateListener.RECONNECTED);
16 }
17 });
18 client.start();
19 }

此处的client是ZkClientWrapper实例,来看ZkClientWrapper.start():

 1     private ListenableFutureTask<ZkClient> listenableFutureTask;
2
3 public ZkClientWrapper(final String serverAddr, long timeout) {
4 this.timeout = timeout;
5 listenableFutureTask = ListenableFutureTask.create(new Callable<ZkClient>() {
6 @Override
7 public ZkClient call() throws Exception {
8 return new ZkClient(serverAddr, Integer.MAX_VALUE);
9 }
10 });
11 }
12
13 public void start() {
14 if (!started) {
15 Thread connectThread = new Thread(listenableFutureTask);
16 connectThread.setName("DubboZkclientConnector");
17 connectThread.setDaemon(true);
18 connectThread.start();
19 try {
20 client = listenableFutureTask.get(timeout, TimeUnit.MILLISECONDS);
21 } catch (Throwable t) {
22 logger.error("Timeout! zookeeper server can not be connected in : " + timeout + "ms!", t);
23 }
24 started = true;
25 } else {
26 logger.warn("Zkclient has already been started!");
27 }
28 }

此处会new ZkClient,连接zookeeper。

之后设置失败重连监听器。到此为止,创建Registry就完成了!再回到RegistryProtocol.refer方法核心代码:

1     public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
2 url = url.setProtocol(url.getParameter(Constants.REGISTRY_KEY, Constants.DEFAULT_REGISTRY)).removeParameter(Constants.REGISTRY_KEY);
3 Registry registry = registryFactory.getRegistry(url);
4 ...
5 return doRefer(cluster, registry, type, url);
6 }

之后执行最后一行代码:

 1     private <T> Invoker<T> doRefer(Cluster cluster, Registry registry, Class<T> type, URL url) {
2 RegistryDirectory<T> directory = new RegistryDirectory<T>(type, url);
3 directory.setRegistry(registry);
4 directory.setProtocol(protocol);
5 // REFER_KEY的所有属性
6 Map<String, String> parameters = new HashMap<String, String>(directory.getUrl().getParameters());
7 URL subscribeUrl = new URL(Constants.CONSUMER_PROTOCOL, parameters.remove(Constants.REGISTER_IP_KEY), 0, type.getName(), parameters);
8 if (!Constants.ANY_VALUE.equals(url.getServiceInterface())
9 && url.getParameter(Constants.REGISTER_KEY, true)) {
10 registry.register(subscribeUrl.addParameters(Constants.CATEGORY_KEY, Constants.CONSUMERS_CATEGORY,
11 Constants.CHECK_KEY, String.valueOf(false)));
12 }
13 directory.subscribe(subscribeUrl.addParameter(Constants.CATEGORY_KEY,
14 Constants.PROVIDERS_CATEGORY
15 + "," + Constants.CONFIGURATORS_CATEGORY
16 + "," + Constants.ROUTERS_CATEGORY));
17 return cluster.join(directory);
18 }

总体步骤:

  • 首先创建RegistryDirectory实例;
  • 之后向zk注册消费者
  • 然后开启监听器(此处发生了第一次服务发现/长连接的建立/netty客户端的建立)
  • 最后使用将RegistryDirectory实例

首先是创建RegistryDirectory,创建完成的实例:

-->List<Router> routers: [MockInvokersSelector实例]
-->Registry registry: 上述的ZookeeperRegistry实例(zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&interface=com.alibaba.dubbo.registry.RegistryService&pid=25267&timestamp=1510225984358)
-->String serviceKey: com.alibaba.dubbo.registry.RegistryService
-->String[] serviceMethods: [sayHello]
-->Class<T> serviceType: interface com.alibaba.dubbo.demo.DemoService
-->URL url: zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=25267&refer=application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&side=consumer&timestamp=1510225913509&timestamp=1510225984358
-->URL consumerUrl: zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&dubbo=2.0.0&pid=25267&refer=application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&side=consumer&timestamp=1510225913509&timestamp=1510225984358
-->URL directoryUrl: zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&side=consumer&timestamp=1510225913509
-->URL overrideDirectoryUrl: zookeeper://10.211.55.5:2181/com.alibaba.dubbo.registry.RegistryService?application=demo-consumer&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&side=consumer&timestamp=1510225913509
-->Map<String, String> queryMap: {side=consumer, application=demo-consumer, register.ip=10.10.10.10, methods=sayHello, dubbo=2.0.0, pid=25267, check=false, interface=com.alibaba.dubbo.demo.DemoService, timestamp=1510225913509}

其中Router是在RegistryDirectory的父类AbstractDirectory中创建的,代码如下:

    public AbstractDirectory(URL url, URL consumerUrl, List<Router> routers) {
if (url == null)
throw new IllegalArgumentException("url == null");
this.url = url;
this.consumerUrl = consumerUrl;
setRouters(routers);
} protected void setRouters(List<Router> routers) {
// copy list
routers = routers == null ? new ArrayList<Router>() : new ArrayList<Router>(routers);
// append url router
String routerkey = url.getParameter(Constants.ROUTER_KEY);
if (routerkey != null && routerkey.length() > 0) {
RouterFactory routerFactory = ExtensionLoader.getExtensionLoader(RouterFactory.class).getExtension(routerkey);
routers.add(routerFactory.getRouter(url));
}
// append mock invoker selector
routers.add(new MockInvokersSelector());
Collections.sort(routers);
this.routers = routers;
}

之后向注册中心注册消费者,注册的方式与服务提供者一样。先是通过FailbackRegistry.register,内部调用子类ZookeeperRegistry的doRegister(),如果失败,加入注册失败列表(会被修复线程后台重新注册)。

 1     public void register(URL url) {
2 if (destroyed.get()){
3 return;
4 }
5 super.register(url);
6 failedRegistered.remove(url);
7 failedUnregistered.remove(url);
8 try {
9 // 向服务器端发送注册请求
10 doRegister(url);
11 } catch (Exception e) {
12 Throwable t = e;
13
14 // 如果开启了启动时检测,则直接抛出异常
15 boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)
16 && url.getParameter(Constants.CHECK_KEY, true)
17 && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol());
18 boolean skipFailback = t instanceof SkipFailbackWrapperException;
19 if (check || skipFailback) {
20 if (skipFailback) {
21 t = t.getCause();
22 }
23 throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t);
24 } else {
25 logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t);
26 }
27
28 // 将失败的注册请求记录到失败列表,定时重试
29 failedRegistered.add(url);
30 }
31 }

最后来看ZookeeperRegistry的doRegister方法:

1     protected void doRegister(URL url) {
2 try {
3 zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));
4 } catch (Throwable e) {
5 throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
6 }
7 }

在zk上创建临时节点:/dubbo/com.alibaba.dubbo.demo.DemoService/consumers/consumer://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=consumers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&side=consumer&timestamp=1510225913509

到此,消费者注册完成!之后directory.subscribe进行订阅。RegistryDirectory.subscribe(URL url):

1     public void subscribe(URL url) {
2 setConsumerUrl(url);
3 registry.subscribe(url, this);
4 }

FailbackRegistry.subscribe(URL url, NotifyListener listener)核心代码:

 1     public void subscribe(URL url, NotifyListener listener) {
2 ...
3 super.subscribe(url, listener);
4 removeFailedSubscribed(url, listener);
5 try {
6 // 向服务器端发送订阅请求
7 doSubscribe(url, listener);
8 } catch (Exception e) {
9 ...
10 // 将失败的订阅请求记录到失败列表,定时重试
11 addFailedSubscribed(url, listener);
12 }
13 }

ZookeeperRegistry.doSubscribe(final URL url, final NotifyListener listener)

 1     protected void doSubscribe(final URL url, final NotifyListener listener) {
2 try {
3 ...
4 List<URL> urls = new ArrayList<URL>();
5 for (String path : toCategoriesPath(url)) {
6 ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);
7 if (listeners == null) {
8 zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());
9 listeners = zkListeners.get(url);
10 }
11 ChildListener zkListener = listeners.get(listener);
12 if (zkListener == null) {
13 listeners.putIfAbsent(listener, new ChildListener() {
14 public void childChanged(String parentPath, List<String> currentChilds) {
15 ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));
16 }
17 });
18 zkListener = listeners.get(listener);
19 }
20 zkClient.create(path, false);
21 List<String> children = zkClient.addChildListener(path, zkListener);
22 if (children != null) {
23 urls.addAll(toUrlsWithEmpty(url, path, children));
24 }
25 }
26 notify(url, listener, urls);
27 } catch (Throwable e) {
28 throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);
29 }
30 }

这里的for循环是3次:

  • /dubbo/com.alibaba.dubbo.demo.DemoService/providers
  • /dubbo/com.alibaba.dubbo.demo.DemoService/configurators
  • /dubbo/com.alibaba.dubbo.demo.DemoService/routers

执行完上述for循环后,来看此时的:

ConcurrentMap<URL, ConcurrentMap<NotifyListener, ChildListener>> zkListeners:

1 {
2 consumer://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=providers,configurators,routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&side=consumer&timestamp=1510225913509
3 =
4 {RegistryDirectory实例=ZookeeperRegistry中的匿名内部类ChildListener实例}
5 }

List<URL> urls:(4个元素)

[
dubbo://10.211.55.5:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.7&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=318&revision=2.5.7&side=provider&timestamp=1510225244315, dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25215&side=provider&timestamp=1510225334486, empty://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=configurators&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&side=consumer&timestamp=1510225913509, empty://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&side=consumer&timestamp=1510225913509
]

注意:前边两个元素是在执行List<String> children = zkClient.addChildListener(path, zkListener)代码时,会返回当前path下的节点(实际上就是第一次服务发现)。

之后一路执行到AbstractRegistry.notify(URL url, NotifyListener listener, List<URL> urls)

 1     protected void notify(URL url, NotifyListener listener, List<URL> urls) {
2 ...
3 Map<String, List<URL>> result = new HashMap<String, List<URL>>();
4 for (URL u : urls) {
5 if (UrlUtils.isMatch(url, u)) {
6 String category = u.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
7 List<URL> categoryList = result.get(category);
8 if (categoryList == null) {
9 categoryList = new ArrayList<URL>();
10 result.put(category, categoryList);
11 }
12 categoryList.add(u);
13 }
14 }
15 if (result.size() == 0) {
16 return;
17 }
18 Map<String, List<URL>> categoryNotified = notified.get(url);
19 if (categoryNotified == null) {
20 notified.putIfAbsent(url, new ConcurrentHashMap<String, List<URL>>());
21 categoryNotified = notified.get(url);
22 }
23 for (Map.Entry<String, List<URL>> entry : result.entrySet()) {
24 String category = entry.getKey();
25 List<URL> categoryList = entry.getValue();
26 categoryNotified.put(category, categoryList);
27 saveProperties(url);
28 listener.notify(categoryList);
29 }
30 }

首先是一个for循环对传入的url列表进行分类,分类结果如下:

Map<String, List<URL>> result:

{
configurators=[
empty://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=configurators&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&side=consumer&timestamp=1510225913509
], routers=[
empty://10.10.10.10/com.alibaba.dubbo.demo.DemoService?application=demo-consumer&category=routers&check=false&dubbo=2.0.0&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&side=consumer&timestamp=1510225913509
], providers=[
dubbo://10.211.55.5:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.5.7&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=318&revision=2.5.7&side=provider&timestamp=1510225244315, dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-provider&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25215&side=provider&timestamp=1510225334486
]
}

之后执行第二个for循环,对上述的result进行遍历,分别进行保存文件和通知。其中前两个entry没做什么核心事,直接来看providers的entry的通知。代码RegistryDirectory.

notify(List<URL> urls)。这里的urls就是上边的providers的两个value值。

 1     public synchronized void notify(List<URL> urls) {
2 List<URL> invokerUrls = new ArrayList<URL>();
3 List<URL> routerUrls = new ArrayList<URL>();
4 List<URL> configuratorUrls = new ArrayList<URL>();
5 for (URL url : urls) {
6 String protocol = url.getProtocol();
7 String category = url.getParameter(Constants.CATEGORY_KEY, Constants.DEFAULT_CATEGORY);
8 if (Constants.ROUTERS_CATEGORY.equals(category)
9 || Constants.ROUTE_PROTOCOL.equals(protocol)) {
10 routerUrls.add(url);
11 } else if (Constants.CONFIGURATORS_CATEGORY.equals(category)
12 || Constants.OVERRIDE_PROTOCOL.equals(protocol)) {
13 configuratorUrls.add(url);
14 } else if (Constants.PROVIDERS_CATEGORY.equals(category)) {
15 invokerUrls.add(url);
16 } else {
17 logger.warn("Unsupported category " + category + " in notified url: " + url + " from registry " + getUrl().getAddress() + " to consumer " + NetUtils.getLocalHost());
18 }
19 }
20 // configurators
21 if (configuratorUrls != null && configuratorUrls.size() > 0) {
22 this.configurators = toConfigurators(configuratorUrls);
23 }
24 // routers
25 if (routerUrls != null && routerUrls.size() > 0) {
26 List<Router> routers = toRouters(routerUrls);
27 if (routers != null) { // null - do nothing
28 setRouters(routers);
29 }
30 }
31 List<Configurator> localConfigurators = this.configurators; // local reference
32 // 合并override参数
33 this.overrideDirectoryUrl = directoryUrl;
34 if (localConfigurators != null && localConfigurators.size() > 0) {
35 for (Configurator configurator : localConfigurators) {
36 this.overrideDirectoryUrl = configurator.configure(overrideDirectoryUrl);
37 }
38 }
39 // providers
40 refreshInvoker(invokerUrls);
41 }

这里首先将输入的两个provider的url存放在invokerUrls列表中,之后调用refreshInvoker(invokerUrls)。

 1     private void refreshInvoker(List<URL> invokerUrls) {
2 ...
3 Map<String, Invoker<T>> oldUrlInvokerMap = this.urlInvokerMap; // local reference
4 ...
5 this.cachedInvokerUrls = new HashSet<URL>();
6 this.cachedInvokerUrls.addAll(invokerUrls);//缓存invokerUrls列表,便于交叉对比
7 ...
8 Map<String, Invoker<T>> newUrlInvokerMap = toInvokers(invokerUrls);// 将URL列表转成Invoker列表
9 Map<String, List<Invoker<T>>> newMethodInvokerMap = toMethodInvokers(newUrlInvokerMap); // 换方法名映射Invoker列表
10 this.methodInvokerMap = multiGroup ? toMergeMethodInvokerMap(newMethodInvokerMap) : newMethodInvokerMap;
11 this.urlInvokerMap = newUrlInvokerMap;
12 ...
13 }
 1     private Map<String, Invoker<T>> toInvokers(List<URL> urls) {
2 Map<String, Invoker<T>> newUrlInvokerMap = new HashMap<String, Invoker<T>>();
3 ...
4 for (URL providerUrl : urls) {
5 String key = url.toFullString(); // URL参数是排序的
6 ...
7 Invoker<T> invoker = new InvokerDelegete<T>(protocol.refer(serviceType, url), url, providerUrl);
8
9 newUrlInvokerMap.put(key, invoker);
10 }
11 ...
12 return newUrlInvokerMap;
13 }

这里会遍历两个providerUrl:protocol是Protocol$Adaptive实例,依旧是走listener->filter->DubboProtocol,看一下filter部分:

1     public <T> Invoker<T> refer(Class<T> type, URL url) throws RpcException {
2 if (Constants.REGISTRY_PROTOCOL.equals(url.getProtocol())) {
3 return protocol.refer(type, url);
4 }
5 return buildInvokerChain(protocol.refer(type, url), Constants.REFERENCE_FILTER_KEY, Constants.CONSUMER);
6 }

两个常量是:reference.filter和consumer。最后来看DubboProtocol.refer

1     public <T> Invoker<T> refer(Class<T> serviceType, URL url) throws RpcException {
2 // create rpc invoker.
3 DubboInvoker<T> invoker = new DubboInvoker<T>(serviceType, url, getClients(url), invokers);
4 invokers.add(invoker);
5 return invoker;
6 }

这里首先执行getClients创建Netty客户端,创建客户端与服务端的长连接,之后封装为DubboInvoker,最后返回。返回之后进行filter链包装该DubboInvoker实例。最后又会使用InvokerDelegete包装带有filter链的DubboInvoker实例。在最后,将该InvokerDelegete实例放置到newUrlInvokerMap缓存中,这就是整个toInvokers(List<URL> urls)的逻辑。最后再将newUrlInvokerMap转换封装到Map<String, List<Invoker<T>>> newMethodInvokerMap缓存中。这就是整个refreshInvoker(List<URL> invokerUrls)的逻辑。执行完成之后,订阅通知就执行完了。

来看一下getClients(url):

 1     private ExchangeClient[] getClients(URL url) {
2 //是否共享连接
3 boolean service_share_connect = false;
4 int connections = url.getParameter(Constants.CONNECTIONS_KEY, 0);
5 //如果connections不配置,则共享连接,否则每服务每连接
6 if (connections == 0) {
7 service_share_connect = true;
8 connections = 1;
9 }
10
11 ExchangeClient[] clients = new ExchangeClient[connections];
12 for (int i = 0; i < clients.length; i++) {
13 if (service_share_connect) {
14 clients[i] = getSharedClient(url);
15 } else {
16 clients[i] = initClient(url);
17 }
18 }
19 return clients;
20 }
21
22 /**
23 * 获取共享连接
24 */
25 private ExchangeClient getSharedClient(URL url) {
26 String key = url.getAddress();
27 ReferenceCountExchangeClient client = referenceClientMap.get(key);
28 if (client != null) {
29 if (!client.isClosed()) {
30 client.incrementAndGetCount();
31 return client;
32 } else {
33 referenceClientMap.remove(key);
34 }
35 }
36 synchronized (key.intern()) {
37 ExchangeClient exchangeClient = initClient(url);
38 client = new ReferenceCountExchangeClient(exchangeClient, ghostClientMap);
39 referenceClientMap.put(key, client);
40 ghostClientMap.remove(key);
41 return client;
42 }
43 }
44
45
46 /**
47 * 创建新连接.
48 */
49 private ExchangeClient initClient(URL url) {
50
51 // client type setting.
52 String str = url.getParameter(Constants.CLIENT_KEY, url.getParameter(Constants.SERVER_KEY, Constants.DEFAULT_REMOTING_CLIENT));
53
54 String version = url.getParameter(Constants.DUBBO_VERSION_KEY);
55 boolean compatible = (version != null && version.startsWith("1.0."));
56 url = url.addParameter(Constants.CODEC_KEY, DubboCodec.NAME);
57 //默认开启heartbeat
58 url = url.addParameterIfAbsent(Constants.HEARTBEAT_KEY, String.valueOf(Constants.DEFAULT_HEARTBEAT));
59
60 // BIO存在严重性能问题,暂时不允许使用
61 if (str != null && str.length() > 0 && !ExtensionLoader.getExtensionLoader(Transporter.class).hasExtension(str)) {
62 throw new RpcException("Unsupported client type: " + str + "," +
63 " supported client type is " + StringUtils.join(ExtensionLoader.getExtensionLoader(Transporter.class).getSupportedExtensions(), " "));
64 }
65
66 ExchangeClient client;
67 try {
68 //设置连接应该是lazy的
69 if (url.getParameter(Constants.LAZY_CONNECT_KEY, false)) {
70 client = new LazyConnectExchangeClient(url, requestHandler);
71 } else {
72 client = Exchangers.connect(url, requestHandler);
73 }
74 } catch (RemotingException e) {
75 throw new RpcException("Fail to create remoting client for service(" + url + "): " + e.getMessage(), e);
76 }
77 return client;
78 }

注意:这里由于使用了共享链接,实际上就是在一个消费者机器和一个服务提供者机器之间只建立一条nio长连接,也可以指定连接数,那样就会建立多条连接。

最后执行到HeaderExchanger.connect(URL url, ExchangeHandler handler)

1     public ExchangeClient connect(URL url, ExchangeHandler handler) throws RemotingException {
2 return new HeaderExchangeClient(Transporters.connect(url, new DecodeHandler(new HeaderExchangeHandler(handler))), true);
3 }

执行Transporters.connect:

 1     public static Client connect(URL url, ChannelHandler... handlers) throws RemotingException {
2 if (url == null) {
3 throw new IllegalArgumentException("url == null");
4 }
5 ChannelHandler handler;
6 if (handlers == null || handlers.length == 0) {
7 handler = new ChannelHandlerAdapter();
8 } else if (handlers.length == 1) {
9 handler = handlers[0];
10 } else {
11 handler = new ChannelHandlerDispatcher(handlers);
12 }
13 return getTransporter().connect(url, handler);
14 }

执行NettyTransporter.connect:

1     public Client connect(URL url, ChannelHandler listener) throws RemotingException {
2 return new NettyClient(url, listener);
3 }
1     public NettyClient(final URL url, final ChannelHandler handler) throws RemotingException {
2 super(url, wrapChannelHandler(url, handler));
3 }

这里继续包装handler。和provider一样,6层。之后进行一系列的赋值后,打开netty客户端:

 1     protected void doOpen() throws Throwable {
2 NettyHelper.setNettyLoggerFactory();
3 bootstrap = new ClientBootstrap(channelFactory);
4 // config
5 // @see org.jboss.netty.channel.socket.SocketChannelConfig
6 bootstrap.setOption("keepAlive", true);
7 bootstrap.setOption("tcpNoDelay", true);
8 bootstrap.setOption("connectTimeoutMillis", getTimeout());
9 final NettyHandler nettyHandler = new NettyHandler(getUrl(), this);
10 bootstrap.setPipelineFactory(new ChannelPipelineFactory() {
11 public ChannelPipeline getPipeline() {
12 NettyCodecAdapter adapter = new NettyCodecAdapter(getCodec(), getUrl(), NettyClient.this);
13 ChannelPipeline pipeline = Channels.pipeline();
14 pipeline.addLast("decoder", adapter.getDecoder());
15 pipeline.addLast("encoder", adapter.getEncoder());
16 pipeline.addLast("handler", nettyHandler);
17 return pipeline;
18 }
19 });
20 }

之后进行连接netty服务端:

 1     protected void doConnect() throws Throwable {
2 long start = System.currentTimeMillis();
3 ChannelFuture future = bootstrap.connect(getConnectAddress());
4 try {
5 boolean ret = future.awaitUninterruptibly(getConnectTimeout(), TimeUnit.MILLISECONDS);
6
7 if (ret && future.isSuccess()) {
8 Channel newChannel = future.getChannel();
9 newChannel.setInterestOps(Channel.OP_READ_WRITE);
10 try {
11 // 关闭旧的连接
12 Channel oldChannel = NettyClient.this.channel; // copy reference
13 if (oldChannel != null) {
14 try {
15 if (logger.isInfoEnabled()) {
16 logger.info("Close old netty channel " + oldChannel + " on create new netty channel " + newChannel);
17 }
18 oldChannel.close();
19 } finally {
20 NettyChannel.removeChannelIfDisconnected(oldChannel);
21 }
22 }
23 } finally {
24 if (NettyClient.this.isClosed()) {
25 try {
26 if (logger.isInfoEnabled()) {
27 logger.info("Close new netty channel " + newChannel + ", because the client closed.");
28 }
29 newChannel.close();
30 } finally {
31 NettyClient.this.channel = null;
32 NettyChannel.removeChannelIfDisconnected(newChannel);
33 }
34 } else {
35 NettyClient.this.channel = newChannel;
36 }
37 }
38 } else if (future.getCause() != null) {
39 throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
40 + getRemoteAddress() + ", error message is:" + future.getCause().getMessage(), future.getCause());
41 } else {
42 throw new RemotingException(this, "client(url: " + getUrl() + ") failed to connect to server "
43 + getRemoteAddress() + " client-side timeout "
44 + getConnectTimeout() + "ms (elapsed: " + (System.currentTimeMillis() - start) + "ms) from netty client "
45 + NetUtils.getLocalHost() + " using dubbo version " + Version.getVersion());
46 }
47 } finally {
48 if (!isConnected()) {
49 future.cancel();
50 }
51 }
52 }

到此为止NettyClient就创建好了,之后将该client封装为HeaderExchangeClient中。

 1     public HeaderExchangeClient(Client client, boolean needHeartbeat) {
2 if (client == null) {
3 throw new IllegalArgumentException("client == null");
4 }
5 this.client = client;
6 this.channel = new HeaderExchangeChannel(client);
7 String dubbo = client.getUrl().getParameter(Constants.DUBBO_VERSION_KEY);
8 this.heartbeat = client.getUrl().getParameter(Constants.HEARTBEAT_KEY, dubbo != null && dubbo.startsWith("1.0.") ? Constants.DEFAULT_HEARTBEAT : 0);
9 this.heartbeatTimeout = client.getUrl().getParameter(Constants.HEARTBEAT_TIMEOUT_KEY, heartbeat * 3);
10 if (heartbeatTimeout < heartbeat * 2) {
11 throw new IllegalStateException("heartbeatTimeout < heartbeatInterval * 2");
12 }
13 if (needHeartbeat) {
14 startHeatbeatTimer();
15 }
16 }

启动心跳。

最后将HeaderExchangeClient实例封装为ReferenceCountExchangeClient:

1     public ReferenceCountExchangeClient(ExchangeClient client, ConcurrentMap<String, LazyConnectExchangeClient> ghostClientMap) {
2 this.client = client;
3 refenceCount.incrementAndGet();
4 this.url = client.getUrl();
5 if (ghostClientMap == null) {
6 throw new IllegalStateException("ghostClientMap can not be null, url: " + url);
7 }
8 this.ghostClientMap = ghostClientMap;
9 }

最后放到缓存Map<String, ReferenceCountExchangeClient> referenceClientMap中。最后将ReferenceCountExchangeClient封装到DubboInvoker中。我们来看此时的DubboInvoker:

-->Map<String, String> attachment: {interface=com.alibaba.dubbo.demo.DemoService}
-->ExchangeClient[] clients:[ReferenceCountExchangeClient实例]//如果设置了多条连接,此处有多个client
-->Class<T> type: interface com.alibaba.dubbo.demo.DemoService
-->Url url: dubbo://10.211.55.5:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.5.7&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&remote.timestamp=1510225244315&revision=2.5.7&side=consumer&timestamp=1510225913509

之后对DubboInvoker实例进行filter链的包装。

ConsumerContextFilter->FutureFilter->MonitorFilter->DubboInvoker.

最后将包装后的Invoker实例包装为InvokerDelegete实例。最后的最后,我们的终极目的:初始化RegistryDirectory的两个属性:

Map<String, List<Invoker<T>>> methodInvokerMap={
sayHello=[provider1的RegistryDirectory$InvokerDelegete实例, provider2的RegistryDirectory$InvokerDelegete实例], *=[provider1的RegistryDirectory$InvokerDelegete实例, provider2的RegistryDirectory$InvokerDelegete实例]} Map<String, Invoker<T>> urlInvokerMap={dubbo://10.10.10.10:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.0.0&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&remote.timestamp=1510225334486&side=consumer&timestamp=1510225913509
=
provider1的RegistryDirectory$InvokerDelegete实例, dubbo://10.211.55.5:20880/com.alibaba.dubbo.demo.DemoService?anyhost=true&application=demo-consumer&check=false&dubbo=2.5.7&generic=false&interface=com.alibaba.dubbo.demo.DemoService&methods=sayHello&pid=25267&register.ip=10.10.10.10&remote.timestamp=1510225244315&revision=2.5.7&side=consumer&timestamp=1510225913509=provider2的RegistryDirectory$InvokerDelegete实例}

到此为止,订阅就完成了。现在来看RegistryProtocol.doRefer的最后一行代码:return cluster.join(directory)

这里的cluster是Cluster$Adaptive实例:

 1 public class Cluster$Adaptive implements com.alibaba.dubbo.rpc.cluster.Cluster {
2 public com.alibaba.dubbo.rpc.Invoker join(com.alibaba.dubbo.rpc.cluster.Directory arg0) throws com.alibaba.dubbo.rpc.RpcException {
3 if (arg0 == null)
4 throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument == null");
5 if (arg0.getUrl() == null)
6 throw new IllegalArgumentException("com.alibaba.dubbo.rpc.cluster.Directory argument getUrl() == null");
7 com.alibaba.dubbo.common.URL url = arg0.getUrl();
8 String extName = url.getParameter("cluster", "failover");
9 if (extName == null)
10 throw new IllegalStateException("Fail to get extension(com.alibaba.dubbo.rpc.cluster.Cluster) name from url(" + url.toString() + ") use keys([cluster])");
11 com.alibaba.dubbo.rpc.cluster.Cluster extension = (com.alibaba.dubbo.rpc.cluster.Cluster) ExtensionLoader.getExtensionLoader(com.alibaba.dubbo.rpc.cluster.Cluster.class).getExtension(extName);
12 return extension.join(arg0);
13 }
14 }

这里的extName="failover",这里会进行aop:MockClusterWrapper包装FailoverCluster。

 1 public class MockClusterWrapper implements Cluster {
2 private Cluster cluster;
3
4 public MockClusterWrapper(Cluster cluster) {
5 this.cluster = cluster;
6 }
7
8 public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
9 return new MockClusterInvoker<T>(directory,
10 this.cluster.join(directory));
11 }
12 }

这里的cluster是FailoverCluster实例。

 1 /**
2 * 失败转移,当出现失败,重试其它服务器,通常用于读操作,但重试会带来更长延迟。
3 */
4 public class FailoverCluster implements Cluster {
5 public final static String NAME = "failover";
6
7 public <T> Invoker<T> join(Directory<T> directory) throws RpcException {
8 return new FailoverClusterInvoker<T>(directory);
9 }
10 }
1     public FailoverClusterInvoker(Directory<T> directory) {
2 super(directory);
3 }

这里实际上就是创建一个FailoverClusterInvokers实例,通过其父类AbstractClusterInvoker存储属性。

最后创建一个MockClusterInvoker实例:

1     private final Directory<T> directory;
2 private final Invoker<T> invoker;
3
4 public MockClusterInvoker(Directory<T> directory, Invoker<T> invoker) {
5 this.directory = directory;
6 this.invoker = invoker;
7 }

到此为止,下边的第一行代码就结束了!最终得到一个MockClusterInvoker实例:

  • directory=RegistryDirectory实例:
  • invoker=FailoverClusterInvokers实例(该实例中又包含一个Directory<T> directory属性,值为上述的RegistryDirectory实例)
1     private T createProxy(Map<String, String> map) {
2 ...
3 if (urls.size() == 1) {
4 invoker = refprotocol.refer(interfaceClass, urls.get(0));
5 }
6 ...
7 // 创建服务代理
8 return (T) proxyFactory.getProxy(invoker);
9 }

二 使用ProxyFactory创建代理

1 (T) proxyFactory.getProxy(invoker)

上述的proxyFactory是ProxyFactory$Adaptive实例,其getProxy内部最终得到是一个被StubProxyFactoryWrapper包装后的JavassistProxyFactory。直接来看JavassistProxyFactory.getProxy方法

1     public <T> T getProxy(Invoker<T> invoker, Class<?>[] interfaces) {
2 return (T) Proxy.getProxy(interfaces).newInstance(new InvokerInvocationHandler(invoker));
3 }
  • invoker:MockClusterInvoker实例
  • interfaces:[interface com.alibaba.dubbo.demo.DemoService, interface com.alibaba.dubbo.rpc.service.EchoService]

注意这里的Proxy不是jdk的,而是dubbo的。

Proxy.getProxy(interfaces)

 1     public static Proxy getProxy(ClassLoader cl, Class<?>... ics) {
2 ...
3 Proxy proxy = null;
4 ...
5 // create ProxyInstance class.
6 String pcn = pkg + ".proxy" + id;
7 ccp.setClassName(pcn);
8 ccp.addField("public static java.lang.reflect.Method[] methods;");
9 ccp.addField("private " + InvocationHandler.class.getName() + " handler;");
10 ccp.addConstructor(Modifier.PUBLIC, new Class<?>[]{InvocationHandler.class}, new Class<?>[0], "handler=$1;");
11 ccp.addDefaultConstructor();
12 Class<?> clazz = ccp.toClass();
13 clazz.getField("methods").set(null, methods.toArray(new Method[0]));
14
15 // create Proxy class.
16 String fcn = Proxy.class.getName() + id;
17 ccm = ClassGenerator.newInstance(cl);
18 ccm.setClassName(fcn);
19 ccm.addDefaultConstructor();
20 ccm.setSuperClass(Proxy.class);
21 ccm.addMethod("public Object newInstance(" + InvocationHandler.class.getName() + " h){ return new " + pcn + "($1); }");
22 Class<?> pc = ccm.toClass();
23 proxy = (Proxy) pc.newInstance();
24 ...
25 return proxy;
26 }

从代码来看,会生成两个Class对象:pc是创建代理类的工厂类;clazz是真实对象的代理类。最终返回的proxy是如下Proxy0对象;之后调用了Proxy0.newInstance(InvocationHandler paramInvocationHandler)方法:创建出了proxy0对象,并初始化了其中的InvocationHandler handler对象为InvokerInvocationHandler。

最终会生成两个类:(这两个类都是笔者都是直接导出.class文件之后通过jd-gui反编译出来的)。

工厂类

1 package com.alibaba.dubbo.common.bytecode;
2
3 import java.lang.reflect.InvocationHandler;
4
5 public class Proxy0 extends Proxy {
6 public Object newInstance(InvocationHandler paramInvocationHandler) {
7 return new proxy0(paramInvocationHandler);
8 }
9 }

真实对象代理类

 1 package com.alibaba.dubbo.common.bytecode;
2
3 import com.alibaba.dubbo.demo.DemoService;
4 import com.alibaba.dubbo.rpc.service.EchoService;
5 import java.lang.reflect.InvocationHandler;
6 import java.lang.reflect.Method;
7
8 public class proxy0 implements EchoService, DemoService {
9 public static Method[] methods;
10 private InvocationHandler handler;
11
12 public String sayHello(String paramString) {
13 Object[] arrayOfObject = new Object[1];
14 arrayOfObject[0] = paramString;
15 Object localObject = this.handler.invoke(this, methods[0], arrayOfObject);
16 return (String) localObject;
17 }
18
19 public Object $echo(Object paramObject) {
20 Object[] arrayOfObject = new Object[1];
21 arrayOfObject[0] = paramObject;
22 Object localObject = this.handler.invoke(this, methods[1], arrayOfObject);
23 return (Object) localObject;
24 }
25
26 public proxy0() {
27 }
28
29 public proxy0(InvocationHandler paramInvocationHandler) {
30 this.handler = paramInvocationHandler;
31 }
32 }

上边的methods数组实际上已经包含了两个元素:

[public abstract java.lang.String com.alibaba.dubbo.demo.DemoService.sayHello(java.lang.String),

public abstract java.lang.Object com.alibaba.dubbo.rpc.service.EchoService.$echo(java.lang.Object)]

如上所示,我们最终返回的代理对象其实是一个proxy0对象,当我们调用其sayHello方法时,其调用内部的handler.invoke方法。

 1 public class InvokerInvocationHandler implements InvocationHandler {
2 private final Invoker<?> invoker;
3
4 public InvokerInvocationHandler(Invoker<?> handler) {
5 this.invoker = handler;
6 }
7
8 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
9 String methodName = method.getName();
10 Class<?>[] parameterTypes = method.getParameterTypes();
11 if (method.getDeclaringClass() == Object.class) {
12 return method.invoke(invoker, args);
13 }
14 if ("toString".equals(methodName) && parameterTypes.length == 0) {
15 return invoker.toString();
16 }
17 if ("hashCode".equals(methodName) && parameterTypes.length == 0) {
18 return invoker.hashCode();
19 }
20 if ("equals".equals(methodName) && parameterTypes.length == 1) {
21 return invoker.equals(args[0]);
22 }
23 return invoker.invoke(new RpcInvocation(method, args)).recreate();
24 }
25
26 }

这里的invoke是上述的MockClusterInvoker实例。

到此为止,DemoService demoService = (DemoService) context.getBean("demoService"); 该行代码就结束了。最终得到的demoService是一个proxy0实例(是一个代理)!

第三章 dubbo源码解析目录的更多相关文章

  1. 第零章 dubbo源码解析目录

    第一章 第一个dubbo项目 第二章  dubbo内核之spi源码解析 2.1  jdk-spi的实现原理 2.2 dubbo-spi源码解析 第三章 dubbo内核之ioc源码解析 第四章 dubb ...

  2. 第三章 CopyOnWriteArrayList源码解析

    注:在看这篇文章之前,如果对ArrayList底层不清楚的话,建议先去看看ArrayList源码解析. http://www.cnblogs.com/java-zhao/p/5102342.html ...

  3. 第三章 LinkedList源码解析

    一.对于LinkedList需要掌握的八点内容 LinkedList的创建:即构造器 往LinkedList中添加对象:即add(E)方法 获取LinkedList中的单个对象:即get(int in ...

  4. dubbo源码解析五 --- 集群容错架构设计与原理分析

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 博客园 Dubbo 入门之二 --- 项目结构解析 博客园 Dubbo 源码分析系列之 ...

  5. dubbo源码解析-spi(一)

    前言 虽然标题是dubbo源码解析,但是本篇并不会出现dubbo的源码,本篇和之前的dubbo源码解析-简单原理.与spring融合一样,为dubbo源码解析专题的知识预热篇. 插播面试题 你是否了解 ...

  6. Dubbo 源码解析四 —— 负载均衡LoadBalance

    欢迎来我的 Star Followers 后期后继续更新Dubbo别的文章 Dubbo 源码分析系列之一环境搭建 Dubbo 入门之二 --- 项目结构解析 Dubbo 源码分析系列之三 -- 架构原 ...

  7. 第十四章 Executors源码解析

    前边两章介绍了基础线程池ThreadPoolExecutor的使用方式.工作机理.参数详细介绍以及核心源码解析. 具体的介绍请参照: 第十二章 ThreadPoolExecutor使用与工作机理 第十 ...

  8. 第九章 LinkedBlockingQueue源码解析

    1.对于LinkedBlockingQueue需要掌握以下几点 创建 入队(添加元素) 出队(删除元素) 2.创建 Node节点内部类与LinkedBlockingQueue的一些属性 static ...

  9. dubbo源码解析-spi(4)

    前言 本篇是spi的第四篇,本篇讲解的是spi中增加的AOP,还是和上一篇一样,我们先从大家熟悉的spring引出AOP. AOP是老生常谈的话题了,思想都不会是一蹴而就的.比如架构设计从All in ...

  10. dubbo源码解析-spi(3)

    前言 在上一篇的末尾,我们提到了dubbo的spi中增加了IoC和AOP的功能.那么本篇就讲一下这个增加的IoC,spi部分预计会有四篇,因为这东西实在是太重要了.温故而知新,我们先来回顾一下,我们之 ...

随机推荐

  1. [转载] Ubuntu上Firefox字体太小--高分屏背锅——高清分辨率屏幕下浏览器设置

    版权声明:本文为CSDN博主「mythinker2」的原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接及本声明.原文链接:https://blog.csdn.net/myth ...

  2. 想要硬件设计不用愁?首先要搞懂这三类GPIO!

    合宙低功耗4G模组经典型号Air780E,支持两种软件开发方式: 一种是传统的AT指令:一种是基于模组做Open开发. 传统AT指令的开发方式,合宙模组与行业内其它模组品牌在软件上区别不大,在硬件功耗 ...

  3. Python之pandas读取Excel

    #! -*- coding utf-8 -*- """ 模块功能:读取当前文件夹下的Source里的Excel文件,显示其相关信息 说明:默认把Excel的第一行当做列名 ...

  4. 如何在wpf窗口中播放PPT。

    前一段时间接到一个需求(大概内容讲一下): 将PPT播放窗口嵌入到我们的系统中,用自己系统控制PPT的播放,在PPT页面上可以手写将手写内容记录下来. 一开始,对于WPF还是一个彩笔的我是懵逼的.后来 ...

  5. 小白PDF阅读器开发-页面元素分割

    以前用手机看PDF格式的电子书时,总感觉非常别扭,PDF格式的电子书在手机上缩放严重,字体太小,想看清楚得来回放大拖动,看书的兴致就在来回缩放拖动间被消耗没了!每次用手机看PDF电子书时就想着得做款能 ...

  6. Nuxt.js 应用中的 render:response 事件钩子

    title: Nuxt.js 应用中的 render:response 事件钩子 date: 2024/11/29 updated: 2024/11/29 author: cmdragon excer ...

  7. 使用 httputils + protostuff 实现高性能 rpc

    1.先讲讲 protostuf protostuf 一直是高性能序列化的代表之一.但是用起来,可难受了,你得先申明 protostuf 配置文件,并且要把这个配置文件转成类.所以必然要学习新语法.新工 ...

  8. c++动态库详解

    dmjcb个人博客 原文地址 概念 动态库, 又称动态链接库(\(Dynamic\) \(Link\) \(Library\), \(DLL\)), 是包含程序代码和数据的可执行文件, 在运行时被程序 ...

  9. 如何在 duxapp 中开发一个兼容 RN 的动画库

    Taro 一直以来都没有一个能兼容 RN 的动画方案,duxapp 中扩展了 createAnimation 方法,让这个方法兼容了 RN 端,下面让我们来看看实现思路 createAnimation ...

  10. OS之《进程管理》

    进程同步 同步实际上是指:将多个进程,按照顺序,有序执行. 让进程有序进行的场景有很多.比如:一个进程依赖另外一个进程的计算结果:一个进程等待另外一个对临界资源的访问:还有像生产者消费者模型中的相互配 ...