Dubbo源码分析(四):Dubbo之Registry

来源:互联网 发布:运维自动化软件 编辑:程序博客网 时间:2024/05/01 00:31

http://blog.csdn.net/flashflight/article/details/44529805


服务注册
对于服务提供方,它需要发布服务,而且由于应用系统的复杂性,服务的数量、类型也不断膨胀;对于服务消费方,它最关心如何获取到它所需要的服务,而面对复杂的应用系统,需要管理大量的服务调用。而且,对于服务提供方和服务消费方来说,他们还有可能兼具这两种角色,即既需要提供服务,有需要消费服务。
通过将服务统一管理起来,可以有效地优化内部应用对服务发布/使用的流程和管理。服务注册中心可以通过特定协议来完成服务对外的统一。Dubbo提供的注册中心有如下几种类型可供选择:

  • Multicast注册中心
  • Zookeeper注册中心
  • Redis注册中心
  • Simple注册中心

    服务首先暴露在服务端,然后调用Registry的register方法在注册中心(它是一个服务协调中心,dubbo以外的独立服务端,dubbo提供了客户端实现)注册服务,然后用户通过配置文件中配置的service的url去subscribe(订阅服务),Registry接收到订阅消息后会往url对应的的List<NotifyListener>中塞入当前NotifyListener,反之从这个list中移除listener就是取消订阅。registry会调用据consumer的订阅情况调用notify方法推送服务列表给Consumer。这里我们以Zookeeper注册中心来说明:

 ZookeeperRegistry.java的构造函数,创建Zookeeper客户端:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. public ZookeeperRegistry(URL url, ZookeeperTransporter zookeeperTransporter) {  
  2.         super(url);  
  3.         //如果provider的url是“0.0.0.0”或者在参数中带anyHost=true则抛出异常注册地址不存在  
  4.         if (url.isAnyHost()) {  
  5.             throw new IllegalStateException("registry address == null");  
  6.         }  
  7.         //服务分组(默认“dubbo”)  
  8.         String group = url.getParameter(Constants.GROUP_KEY, DEFAULT_ROOT);  
  9.         //在group头补齐“/”  
  10.         if (! group.startsWith(Constants.PATH_SEPARATOR)) {  
  11.             group = Constants.PATH_SEPARATOR + group;  
  12.         }  
  13.         //服务分组根地址  
  14.         this.root = group;  
  15.         //创建Zookeeper客户端  
  16.         zkClient = zookeeperTransporter.connect(url);  
  17.         //添加状态监听器  
  18.         zkClient.addStateListener(new StateListener() {  
  19.             public void stateChanged(int state) {  
  20.                 if (state == RECONNECTED) {  
  21.                     try {  
  22.                         recover();  
  23.                     } catch (Exception e) {  
  24.                         logger.error(e.getMessage(), e);  
  25.                     }  
  26.                 }  
  27.             }  
  28.         });  
  29.     }  

ZookeeperRegistry的doRegister方法:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. protected void doRegister(URL url) {  
  2.         try {  
  3.             //连接注册中心注册  
  4.             zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true));  
  5.         } catch (Throwable e) {  
  6.             throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);  
  7.         }  
  8.     }  


    Provider初始化时会调用doRegister方法向注册中心发起注册。那么客户端又是怎么subscribe在注册中心订阅服务的呢?答案是服务消费者在初始化ConsumerConfig时会调用RegistryProtocol的refer方法进一步调用RegistryDirectory的subscribe方法最终调用ZookeeperRegistry的subscribe方法向注册中心订阅服务。

com.alibaba.dubbo.registry.support.FailBackRegistry的subscribe方法:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. @Override  
  2.     public void subscribe(URL url, NotifyListener listener) {  
  3.         super.subscribe(url, listener);  
  4.         removeFailedSubscribed(url, listener);  
  5.         try {  
  6.             // 向服务器端发送订阅请求  
  7.             doSubscribe(url, listener);  
  8.         } catch (Exception e) {  
  9.             Throwable t = e;  
  10.   
  11.             List<URL> urls = getCacheUrls(url);  
  12.             if (urls != null && urls.size() > 0) {  
  13.                 notify(url, listener, urls);  
  14.                 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);  
  15.             } else {  
  16.                 // 如果开启了启动时检测,则直接抛出异常  
  17.                 boolean check = getUrl().getParameter(Constants.CHECK_KEY, true)  
  18.                         && url.getParameter(Constants.CHECK_KEY, true);  
  19.                 boolean skipFailback = t instanceof SkipFailbackWrapperException;  
  20.                 if (check || skipFailback) {  
  21.                     if(skipFailback) {  
  22.                         t = t.getCause();  
  23.                     }  
  24.                     throw new IllegalStateException("Failed to subscribe " + url + ", cause: " + t.getMessage(), t);  
  25.                 } else {  
  26.                     logger.error("Failed to subscribe " + url + ", waiting for retry, cause: " + t.getMessage(), t);  
  27.                 }  
  28.             }  
  29.   
  30.             // 将失败的订阅请求记录到失败列表,定时重试  
  31.             addFailedSubscribed(url, listener);  
  32.         }  
  33.     }  

com.alibaba.dubbo.registry.zookeeper.Zookeeper的doSubscribe方法:

[java] view plaincopy在CODE上查看代码片派生到我的代码片
  1. protected void doSubscribe(final URL url, final NotifyListener listener) {  
  2.         try {  
  3.             //如果provider的service的接口配置的是“*”  
  4.             if (Constants.ANY_VALUE.equals(url.getServiceInterface())) {  
  5.                 //获取服务分组根路径  
  6.                 String root = toRootPath();  
  7.                 //获取服务的NotifyListener  
  8.                 ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);  
  9.                 if (listeners == null) {  
  10.                     //如果没有则创建一个  
  11.                     zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());  
  12.                     listeners = zkListeners.get(url);  
  13.                 }  
  14.                 ChildListener zkListener = listeners.get(listener);  
  15.                 //如果没有子监听器则创建一个  
  16.                 if (zkListener == null) {  
  17.                     listeners.putIfAbsent(listener, new ChildListener() {  
  18.                         public void childChanged(String parentPath, List<String> currentChilds) {  
  19.                             for (String child : currentChilds) {  
  20.                                 child = URL.decode(child);  
  21.                                 if (! anyServices.contains(child)) {  
  22.                                     anyServices.add(child);  
  23.                                     subscribe(url.setPath(child).addParameters(Constants.INTERFACE_KEY, child,   
  24.                                             Constants.CHECK_KEY, String.valueOf(false)), listener);  
  25.                                 }  
  26.                             }  
  27.                         }  
  28.                     });  
  29.                     zkListener = listeners.get(listener);  
  30.                 }  
  31.                 //向服务器订阅服务,注册中心会调用NotifyListener的notify函数返回服务列表  
  32.                 zkClient.create(root, false);  
  33.                 //获取服务地址列表  
  34.                 List<String> services = zkClient.addChildListener(root, zkListener);  
  35.                 if (services != null && services.size() > 0) {  
  36.                     //如果存在服务  
  37.                     for (String service : services) {  
  38.                         service = URL.decode(service);  
  39.                         anyServices.add(service);  
  40.                         //如果serviceInterface是“*”则从分组根路径遍历service并订阅所有服务  
  41.                         subscribe(url.setPath(service).addParameters(Constants.INTERFACE_KEY, service,   
  42.                                 Constants.CHECK_KEY, String.valueOf(false)), listener);  
  43.                     }  
  44.                 }  
  45.             } else {  
  46.                 //如果serviceInterface不是“*”则创建Zookeeper客户端索取服务列表,并通知(notify)消费者(consumer)这些服务可以用了  
  47.                 List<URL> urls = new ArrayList<URL>();  
  48.                 //获取类似于http://xxx.xxx.xxx.xxx/context/com.service.xxxService/consumer的地址  
  49.                 for (String path : toCategoriesPath(url)) {  
  50.                     //获取例如com.service.xxxService对应的NotifyListener map  
  51.                     ConcurrentMap<NotifyListener, ChildListener> listeners = zkListeners.get(url);  
  52.                     if (listeners == null) {  
  53.                         zkListeners.putIfAbsent(url, new ConcurrentHashMap<NotifyListener, ChildListener>());  
  54.                         listeners = zkListeners.get(url);  
  55.                     }  
  56.                     //获取ChildListener  
  57.                     ChildListener zkListener = listeners.get(listener);  
  58.                     if (zkListener == null) {  
  59.                         listeners.putIfAbsent(listener, new ChildListener() {  
  60.                             public void childChanged(String parentPath, List<String> currentChilds) {  
  61.                                 ZookeeperRegistry.this.notify(url, listener, toUrlsWithEmpty(url, parentPath, currentChilds));  
  62.                             }  
  63.                         });  
  64.                         zkListener = listeners.get(listener);  
  65.                     }  
  66.                     //创建Zookeeper客户端  
  67.                     zkClient.create(path, false);  
  68.                     List<String> children = zkClient.addChildListener(path, zkListener);  
  69.                     if (children != null) {  
  70.                         urls.addAll(toUrlsWithEmpty(url, path, children));  
  71.                     }  
  72.                 }  
  73.                 //提醒消费者  
  74.                 notify(url, listener, urls);  
  75.             }  
  76.         } catch (Throwable e) {  
  77.             throw new RpcException("Failed to subscribe " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e);  
  78.         }  
  79.     }  

    至此,Dubbo的源码解析结束,以后还会对一些细节进行补充。特别在此建议看官使用apache的开源项目Zookeeper作注册中心,来完成分布式服务的协调调用。
0 0
原创粉丝点击