Dubbo源码解读 — 注册中心的实现

原创
2020/03/14 19:48
阅读数 903

Dubbo源码解读 — 注册中心的实现

Dubbo源码解读 — 注册中心的实现  0、概述  1、注册中心的工作流  2、注册中心的数据结构  3、Redis注册源码跟读  4、源码跟读记录的知识点

本源码解读dubbo版本为:2.7.6,今天主要阅读注册中心源码Redis的实现,从Redis实现原理发现,发现两个问题:

1、如果服务宕机,就会造成服务没有广播?

2、Redis的发布订阅通道并不是消息可靠的,如果使用集群,主节点挂了,从节点数据没有同步?

以上两个会导致订阅方不知道服务方已经下线,该如何解决?资料查询需要依赖服务治理中心

0、概述

  • 注册中心的作用:

    • 动态加入:服务提供者通过注册中心动态把自己暴露给消费者,无需消费者更新配置文件

    • 动态发现:一个消费者可以动态地感知新的配置,路由规则和新服务提供者,不需要重启服务

    • 动态调整:参数调整,自动更新相关服务节点

    • 统一配置:

  • Dubbo注册中心源码在dubbo-registry模块下:

dubbo-registry-api API和抽象实现类
dubbo-registry-default 基于内存的默认实现
dubbo-registry-multicast 
dubbo-registry-zookeeper zk实现
dubbo-registry-redis redis实现
dubbo-registry-consul 服务网格实现
dubbo-registry-etcd3
dubbo-registry-nacos nacos实现
dubbo-registry-multiple
dubbo-registry-sofa
dubbo-registry-eureka

1、注册中心的工作流

 

2、注册中心的数据结构

  • ZooKeeper

    ZK是树形结构的注册中心,节点有持久、持久顺序、临时、临时顺序 【Zk使用场景

    对Dubbo,Zk会创建持久、临时节点,对顺序无要求

  • Redis

    Redis使用Key/Map

3、Redis注册源码跟读

  • 类RedisRegistry

  • 工厂注册

  • RedisRegistryFactory类

4、源码跟读记录的知识点

  • 订阅发布的实现

    1)Redis 中 key = toCategoryPath(url)即:root+service+type【providers、consumers、routers、configurators】作为key

    private String toServicePath(URL url) {
        return root + url.getServiceInterface();
    }
    private String toCategoryPath(URL url) {
        return toServicePath(url) + PATH_SEPARATOR + url.getParameter(CATEGORY_KEY, DEFAULT_CATEGORY);
    }

    2)Redis订阅发布机制使用的是过期时间和publish/subscribe通道,启动调度线程池不断刷新过期时间

    this.expireFuture = expireExecutor.scheduleWithFixedDelay(() -> {
                try {
                    deferExpired(); // Extend the expiration time
                } catch (Throwable t) { // Defensive fault tolerance
               logger.error("Unexpected exception occur at defer expire time, cause: " + t.getMessage(), t);
                }
            }, expirePeriod / 2, expirePeriod / 2, TimeUnit.MILLISECONDS);
     
    核心代码: getRegistered从缓存中获取url,然后组装key,过期后就发布
    for (URL url : new HashSet<>(getRegistered())) {
        if (url.getParameter(DYNAMIC_KEY, true)) {
            String key = toCategoryPath(url);
            if (jedis.hset(key, url.toFullString(),         String.valueOf(System.currentTimeMillis() + expirePeriod)) == 1) {
            jedis.publish(key, REGISTER);
            }
        }
    }        
  • 缓存机制

    Dubbo的缓存机制是在AbstractRegistry类中实现的,消费者或服务治理中心会获取注册信息后会在本地做缓存,内存中有一份,也会持久化一份。构造函数中进行的加载

    private final ConcurrentMap<URL, Map<String, List<URL>>> notified = new ConcurrentHashMap<>(); 这个是内存中的缓存对象
    private File file; 文件缓存对象
  • 重试机制

    FailbackRegistry类中实现几种注册,订阅,通知失败(五种)时的重试,启动一个定时任务不断尝试。

     public FailbackRegistry(URL url) {
            super(url);
            this.retryPeriod = url.getParameter(REGISTRY_RETRY_PERIOD_KEY, DEFAULT_REGISTRY_RETRY_PERIOD);
            // 定时任务
            retryTimer = new HashedWheelTimer(new NamedThreadFactory("DubboRegistryRetryTimer", true), retryPeriod, TimeUnit.MILLISECONDS, 128);
        }
  • 设计模式

    从源码可以发现,注册中心使用了模板模式(抽象类提取公共方法)和工厂模式,在工厂类中使用了重入锁(ReentrantLock)控制获取不同的注册形式

    // Lock the registry access process to ensure a single instance of the registry
    LOCK.lock();
    try {
        Registry registry = REGISTRIES.get(key);
        if (registry != null) {
            return registry;
        }
        //create registry by spi/ioc
        registry = createRegistry(url);
        if (registry == null) {
            throw new IllegalStateException("Can not create registry " + url);
        }
        REGISTRIES.put(key, registry);
        return registry;
    } finally {
        // Release the lock
        LOCK.unlock();
    }

读取源码不易,其读其珍惜 2020-03-14

展开阅读全文
加载中

作者的其它热门文章

打赏
0
0 收藏
分享
打赏
3 评论
0 收藏
0
分享
返回顶部
顶部