图解+源码讲解 Eureka Server 注册表缓存逻辑

1,073 阅读4分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第3天,点击查看活动详情

图解+源码讲解 Eureka Server 注册表缓存逻辑

让珊瑚远离惊涛骇浪的侵蚀吗?那无异是将它们的美丽葬送 相关文章
eureka-server 项目结构分析
图解+源码讲解 Eureka Server 启动流程分析
图解+源码讲解 Eureka Client 启动流程分析
图解+源码讲解 Eureka Server 注册表缓存逻辑
图解+源码讲解 Eureka Client 拉取注册表流程
图解+源码讲解 Eureka Client 服务注册流程
图解+源码讲解 Eureka Client 心跳机制流程
图解+源码讲解 Eureka Client 下线流程分析
图解+源码讲解 Eureka Server 服务剔除逻辑
图解+源码讲解 Eureka Server 集群注册表同步机制

客户端拉取注册表流程图

    eureka server 核心亮点是构造了读写缓存只读缓存操作,30s进行缓存的对比操作,这样一来就是客户端拉取注册表的逻辑就是,如果开启了只读缓存那么就先从只读缓存中获取注册表,如果只读注册表中不存在的情况下从读写缓存中获取,如果读写缓存也没有的话那么就进行从注册中心拉取之后同步到读写缓存,在同步到只读缓存中
image.png

注册表核心逻辑流程图

image.png

从哪里开始分析

    服务端上下文初始化的时候 serverContext.initialize() 进行缓存操作的,这里面的 registry.init()方法是核心操作,主要的初始化缓存逻辑都在这里面了

@PostConstruct
@Override
public void initialize() {
    logger.info("Initializing ...");
    try {
        ....
        /**
         * 基于eureka server集群的信息,来初始化注册表,将eureka server集群中所有的
         * eureka server的注册表的信息,都抓取过来
         */
        registry.init(peerEurekaNodes);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
    logger.info("Initialized");
}

Eureka Server 注册表核心流程

1. init 方法

    这个方法是注册表类的init方法,PeerAwareInstanceRegistryImpl#init()方法,里面的初始化本地缓存核心方法是 initializedResponseCache()

@Override
public void init(PeerEurekaNodes peerEurekaNodes) throws Exception {
    this.numberOfReplicationsLastMin.start();
    this.peerEurekaNodes = peerEurekaNodes;
    /**
     * 初始化缓存
     */
    initializedResponseCache();
    ...
}

2. 构造读写缓存

    如果 responseCache对象为空的情况,那我们就new 一个 ResponseCacheImpl 对象,这里面有一个读写缓存readWriteCacheMap,该Map读写缓存是通过构造器进行进行创建的,里面的 generatePayload(key) 方法是获取注册表的信息

ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, 
                  AbstractInstanceRegistry registry) {
    // shouldUseReadOnlyResponseCache 是否使用只读缓存默认是true
    this.shouldUseReadOnlyResponseCache = serverConfig.shouldUseReadOnlyResponseCache();
    this.registry = registry;
    // 缓存更新间隔 (30 * 1000) = 30s
    long responseCacheUpdateIntervalMs = serverConfig.getResponseCacheUpdateIntervalMs();
    this.readWriteCacheMap =
            // serverConfig.getInitialCapacityOfResponseCache() 初始化容量是1000
          CacheBuilder.newBuilder().initialCapacity(
            serverConfig.getInitialCapacityOfResponseCache())
        // 设置自动过期时间是 180s ,所以你往readWriteCacheMap中放入一个数据过后,
        // 自动会等180秒过后,就将这个数据给他过期了
           .expireAfterWrite(serverConfig.getResponseCacheAutoExpirationInSeconds(), 
                             TimeUnit.SECONDS)
            ...
    .build(new CacheLoader<Key, Value>() {
        @Override
        public Value load(Key key) throws Exception {
            if (key.hasRegions()) {
                Key cloneWithNoRegions = key.cloneWithoutRegions();
                regionSpecificKeys.put(cloneWithNoRegions, key);
            }
            // 获取全量注册表
            Value value = generatePayload(key);
            return value;
        }
    });

}

流程图

image.png

3. 定时任务 getCacheUpdateTask()

if (shouldUseReadOnlyResponseCache) {
    /**
     * 每30s进行更新只读缓存从读写缓存中获取数据
     * 默认是每隔30秒,执行一个定时调度的线程任务,TimerTask,有一个逻辑,会每隔30秒,
     * 对readOnlyCacheMap和readWriteCacheMap中的数据进行一个比对,
     * 如果两块数据是不一致的,那么就将readWriteCacheMap中的数据放到readOnlyCacheMap中来
     */
   timer.schedule(getCacheUpdateTask(),
     new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) *
    responseCacheUpdateIntervalMs) + responseCacheUpdateIntervalMs),
    responseCacheUpdateIntervalMs);
}

    每隔30s 就就会进行一个任务调度,进行读写缓存以及只读缓存同步操作

private TimerTask getCacheUpdateTask() {
   return new TimerTask() {
    @Override
    public void run() {
        for (Key key : readOnlyCacheMap.keySet()) {
            // 获取读写缓存中的key值
            Value cacheValue = readWriteCacheMap.get(key);
            // 获取只读缓存中的key值
            Value currentCacheValue = readOnlyCacheMap.get(key);
           //如果只读缓存中的key值和读写缓存中的key值不同的话那么将读写缓存中的值放入只读缓存中
            if (cacheValue != currentCacheValue) {
                readOnlyCacheMap.put(key, cacheValue);
            }
        }
    }};
}

流程图

image.png

小结

  1. 服务端初始化上下文
  2. 通过构造器模式构造读写缓存 readWriteMap
  3. 定义一个30s执行一次任务的调度器 getCacheUpdateTask() 进行任务比对操作