dubbo 源码学习笔记 (五) —— 注册中心模块
来源:互联网 发布:手机淘宝6.3.2 旧版本 编辑:程序博客网 时间:2024/05/16 08:19
欢迎访问我的个人博客休息的风
dubbo的注册中心模块,封装服务地址的注册与发现,以服务URL为中心,扩展接口为RegistryFactory,Registry,RegistryService。以下为整个模块的类图。(图片有点小,请在新标签页打开图片查看)
我们先来看下,如果要实现一个注册中心,应该怎么做。首先看下RegistryProtocol,这个类是注册中心的入口。在获取registry时,都是通过RegistryFactory工厂类去获取的。我们想自己实现一个注册中心,就要先实现一个RegistryFactory。
@SPI("dubbo")public interface RegistryFactory { /** * 连接注册中心. * <p> * 连接注册中心需处理契约:<br> * 1. 当设置check=false时表示不检查连接,否则在连接不上时抛出异常。<br> * 2. 支持URL上的username:password权限认证。<br> * 3. 支持backup=10.20.153.10备选注册中心集群地址。<br> * 4. 支持file=registry.cache本地磁盘文件缓存。<br> * 5. 支持timeout=1000请求超时设置。<br> * 6. 支持session=60000会话超时或过期设置。<br> * * @param url 注册中心地址,不允许为空 * @return 注册中心引用,总不返回空 */ @Adaptive({"protocol"}) Registry getRegistry(URL url);}在实现RegistryFactory接口时,dubbo提供了AbstractRegistryFactory的抽象类,如果要自已实现注册中心,可以实现RegistryFactory接口,并且继承AbstractRegistryFactory。这个父类获取Registry过程已经定义好,我们只需实现createRegistry抽象方法即可。
public Registry getRegistry(URL url) { url = url.setPath(RegistryService.class.getName()) .addParameter(Constants.INTERFACE_KEY, RegistryService.class.getName()) .removeParameters(Constants.EXPORT_KEY, Constants.REFER_KEY); String key = url.toServiceString(); // 锁定注册中心获取过程,保证注册中心单一实例 LOCK.lock(); try { Registry registry = REGISTRIES.get(key); if (registry != null) { return registry; } registry = createRegistry(url); if (registry == null) { throw new IllegalStateException("Can not create registry " + url); } REGISTRIES.put(key, registry); return registry; } finally { // 释放锁 LOCK.unlock(); }}protected abstract Registry createRegistry(URL url);
这个抽象方法需要返回一个Registry。Registry继承Node和RegistryService接口。我们来看下RegistryService接口:
public interface RegistryService { /** * 注册数据,比如:提供者地址,消费者地址,路由规则,覆盖规则,等数据。 * <p> * 注册需处理契约:<br> * 1. 当URL设置了check=false时,注册失败后不报错,在后台定时重试,否则抛出异常。<br> * 2. 当URL设置了dynamic=false参数,则需持久存储,否则,当注册者出现断电等情况异常退出时,需自动删除。<br> * 3. 当URL设置了category=routers时,表示分类存储,缺省类别为providers,可按分类部分通知数据。<br> * 4. 当注册中心重启,网络抖动,不能丢失数据,包括断线自动删除数据。<br> * 5. 允许URI相同但参数不同的URL并存,不能覆盖。<br> * * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin */ void register(URL url); /** * 取消注册. * <p> * 取消注册需处理契约:<br> * 1. 如果是dynamic=false的持久存储数据,找不到注册数据,则抛IllegalStateException,否则忽略。<br> * 2. 按全URL匹配取消注册。<br> * * @param url 注册信息,不允许为空,如:dubbo://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin */ void unregister(URL url); /** * 订阅符合条件的已注册数据,当有注册数据变更时自动推送. * <p> * 订阅需处理契约:<br> * 1. 当URL设置了check=false时,订阅失败后不报错,在后台定时重试。<br> * 2. 当URL设置了category=routers,只通知指定分类的数据,多个分类用逗号分隔,并允许星号通配,表示订阅所有分类数据。<br> * 3. 允许以interface,group,version,classifier作为条件查询,如:interface=com.alibaba.foo.BarService&version=1.0.0<br> * 4. 并且查询条件允许星号通配,订阅所有接口的所有分组的所有版本,或:interface=*&group=*&version=*&classifier=*<br> * 5. 当注册中心重启,网络抖动,需自动恢复订阅请求。<br> * 6. 允许URI相同但参数不同的URL并存,不能覆盖。<br> * 7. 必须阻塞订阅过程,等第一次通知完后再返回。<br> * * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin * @param listener 变更事件监听器,不允许为空 */ void subscribe(URL url, NotifyListener listener); /** * 取消订阅. * <p> * 取消订阅需处理契约:<br> * 1. 如果没有订阅,直接忽略。<br> * 2. 按全URL匹配取消订阅。<br> * * @param url 订阅条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin * @param listener 变更事件监听器,不允许为空 */ void unsubscribe(URL url, NotifyListener listener); /** * 查询符合条件的已注册数据,与订阅的推模式相对应,这里为拉模式,只返回一次结果。 * * @param url 查询条件,不允许为空,如:consumer://10.20.153.10/com.alibaba.foo.BarService?version=1.0.0&application=kylin * @return 已注册信息列表,可能为空,含义同{@link com.alibaba.dubbo.registry.NotifyListener#notify(List<URL>)}的参数。 * @see com.alibaba.dubbo.registry.NotifyListener#notify(List) */ List<URL> lookup(URL url);}也就是说我们在createRegistry方法返回的Registry至少需要register,unregister,subscribe,unsubscribe,lookup这几个功能。关于实现Registry接口,dubbo也为我们提供了FailbackRegistry抽象类。需要自己实现Registry,可以继承这个抽象类,并实现抽象类里面的模板方法。
// ==== 模板方法 ====protected abstract void doRegister(URL url);protected abstract void doUnregister(URL url);protected abstract void doSubscribe(URL url, NotifyListener listener);protected abstract void doUnsubscribe(URL url, NotifyListener listener);
到这里,我们知道可以通过继承AbstractRegistryFactory来实现RegistaryFacotry接口;通过继承FailbackRegistry来实现Registry接口。接下来,我们用比较熟悉的zookeeper为例子,来分析注册中心的工作原理,其实大部分的过程也还是在FailbackRegistry这个类中。
首先,先分析FailbackRegistry中retry,register,unregister,subscribe,unsubscribe这几个方法:
// 重试失败的动作protected void retry() { //对失败的注册动作进行重试直至成功 if (!failedRegistered.isEmpty()) { //省略代码。。 doRegister(url); failedRegistered.remove(url); //省略代码。。 } //对失败的反注册动作进行重试直至成功 if (!failedUnregistered.isEmpty()) { //省略代码。。 doUnregister(url); failedUnregistered.remove(url); //省略代码。。 } //对失败的订阅动作进行重试直至成功 if (!failedSubscribed.isEmpty()) { //省略代码。。 doSubscribe(url, listener); listeners.remove(listener); //省略代码。。 } //对失败的反订阅动作进行重试直至成功 if (!failedUnsubscribed.isEmpty()) { //省略代码。。 doUnsubscribe(url, listener); listeners.remove(listener); //省略代码。。 } //对失败的通知进行重试,直至成功 if (!failedNotified.isEmpty()) { //省略代码。。 for (Map<NotifyListener, List<URL>> values : failed.values()) { for (Map.Entry<NotifyListener, List<URL>> entry : values.entrySet()) { try { NotifyListener listener = entry.getKey(); List<URL> urls = entry.getValue(); listener.notify(urls); values.remove(listener); } catch (Throwable t) { // 忽略所有异常,等待下次重试 logger.warn("Failed to retry notify " + failed + ", waiting for again, cause: " + t.getMessage(), t); } } } } //省略代码。。 }}retry方法的重试,在构建方法中会创建线程池,定期执行
public FailbackRegistry(URL url) { super(url); int retryPeriod = url.getParameter(Constants.REGISTRY_RETRY_PERIOD_KEY, Constants.DEFAULT_REGISTRY_RETRY_PERIOD); this.retryFuture = retryExecutor.scheduleWithFixedDelay(new Runnable() { public void run() { // 检测并连接注册中心 try { retry(); } catch (Throwable t) { // 防御性容错 logger.error("Unexpected error occur at failed retry, cause: " + t.getMessage(), t); } } }, retryPeriod, retryPeriod, TimeUnit.MILLISECONDS);}register方法中,会定义好注册的过程,具体注册的动作,由子类实现的doRegistry方法实现
public void register(URL url) { if (destroyed.get()){ return; } //这里调用父类方法,把url加入registered中 super.register(url); failedRegistered.remove(url); failedUnregistered.remove(url); try { // 向服务器端发送注册请求 doRegister(url); } catch (Exception e) { Throwable t = e; // 如果开启了启动时检测,则直接抛出异常 boolean check = getUrl().getParameter(Constants.CHECK_KEY, true) && url.getParameter(Constants.CHECK_KEY, true) && !Constants.CONSUMER_PROTOCOL.equals(url.getProtocol()); boolean skipFailback = t instanceof SkipFailbackWrapperException; if (check || skipFailback) { if (skipFailback) { t = t.getCause(); } throw new IllegalStateException("Failed to register " + url + " to registry " + getUrl().getAddress() + ", cause: " + t.getMessage(), t); } else { logger.error("Failed to register " + url + ", waiting for retry, cause: " + t.getMessage(), t); } // 将失败的注册请求记录到失败列表,定时重试 failedRegistered.add(url); }}
其他unregister,subscribe,unsubscribe这几个方法跟register方法类似,这里不再展示。在ZookeeperRegistry中,会调用具体的注册动作:
protected void doRegister(URL url) { try { zkClient.create(toUrlPath(url), url.getParameter(Constants.DYNAMIC_KEY, true)); } catch (Throwable e) { throw new RpcException("Failed to register " + url + " to zookeeper " + getUrl() + ", cause: " + e.getMessage(), e); }}至此,注册中心模块的大概组织情况我们也了解了。这里再总结一下。首先RegistryProtocol为注册中心的入口,这里会用RegistryFactory去创建一个Registry。dubbo帮助我们实现了AbstractRegistryFactory去实现RegistryFactory接口来获取Registry的过程;FailbackRegistry去实现Registry接口来定义注册,反注册,订阅,反订阅操作过程,并且会进行定时失败重试。具体的创建注册器及注册,反注册,订阅,反订阅操作,由具体的注册中心(比如zookeeper,Multicast,Dubbo等)来实现。
阅读全文
0 0
- dubbo 源码学习笔记 (五) —— 注册中心模块
- Dubbo学习——注册中心
- dubbo 源码学习笔记 (四) —— 配置模块
- dubbo 源码学习笔记 (六) —— 集群模块
- Dubbo学习笔记(三)------Zookeeper注册中心
- 学习笔记-dubbo监控和注册中心
- dubbo源码 学习笔记(五)
- Dubbo学习总结(3)——Dubbo-Admin管理平台和Zookeeper注册中心的搭建
- Dubbo学习总结(3)——Dubbo-Admin管理平台和Zookeeper注册中心的搭建
- dubbo 源码学习笔记 (七) —— 远程调用模块
- dubbo 源码学习笔记 (八) —— 远程通讯模块
- dubbo学习笔记(1)-注册中心和管理平台部署
- Dubbo分布式服务治理(一)——Dubbo注册中心&&管理平台安装(Linux)
- Dubbo&ZK分布式服务化改造(四)——Dubbo多注册中心 & 服务迁移
- Dubbo&ZK分布式服务化改造(四)——Dubbo多注册中心 & 服务迁移
- Dubbo&ZK分布式服务化改造(四)——Dubbo多注册中心 & 服务迁移
- Dubbo学习(六):多注册中心
- Dubbo入门学习--协议及注册中心
- 关于java中参数传递的理解和总结
- 函数模板
- idea 出现 Cannot resolve symbol string 的问题
- Tablayout 横向滚动条的简单实现
- Flex学习之路之二十三 List的使用
- dubbo 源码学习笔记 (五) —— 注册中心模块
- 任学堂:在教育行业,我们怎么利用人工智能?
- 枚举举例,生理周期
- Eclipse创建一个简单的Springmvc程序(Maven工程)
- c3p0 MySQL连接8小时失效问题的官方解决方案
- apache Lucene
- J2ME逆向工程实战(逆向某著名即时通讯软件
- SGU 142. Keyword(暴力枚举)
- dubbo 源码学习笔记 (四) —— 配置模块