eureka源码之server端的多级缓存机制(代码片段)

author author     2022-12-05     233

关键词:

一、前言

上一讲我们讲到了 Eureka 注册中心的 Server 端有三级缓存来保存注册信息,可以利用缓存的快速读取来提高系统性能。我们再来细看下:

一级缓存:只读缓存 readOnlyCacheMap,数据结构 ConcurrentHashMap。相当于数据库。

二级缓存:读写缓存 readOnlyCacheMap,Guava Cache。相当于 Redis 主从架构中主节点,既可以进行读也可以进行写。

三级缓存:本地注册表 registry,数据结构 ConcurentHashMap。相当于 Redis 主从架构的从节点,只负责读。

看图更清晰,如下图所示:

另外 ConcurrenthashMap 也是一种 map 结构,也就是以键值对的方式进行存储,如下图所示:

本篇悟空哥会带着大家来看下 Eureka 的缓存架构是怎么样,通过学习这篇,我们也可以借鉴 Eureka 的缓存设计思想,将其运用到项目当中。

二、引发的几个思考

我们再来看下 Eureka 源码,其实不难看懂,下面会做解释。

  • 默认会先从只读缓存里面找。
  • 没有的话,再从读写缓存里面找。
  • 找到了的话就更新只读缓存,并返回找到的缓存。
  • 还找不到的话,就从本地缓存 registry 中加载进来。

带来了三个问题:

三、本地缓存

我们先来看下本地缓存 registry,它是一种定义为 ConcurrentHashMap 的数据结构,之前也详细讲解过。

当客户端发起注册请求的时候,就会把注册信息放到 registry 中。如下代码所示:

registry.putIfAbsent(app)

putIfAbsent 表示如果存在重复的 key,就不会放入值,如果传入的 key 对应的 value 已经存在,就返回存在的 value,不进行替换。

经过 putIfAbsent 操作就把客户端的注册信息放到 registry 中了。

我们再来看下其中的一种缓存结构:读写缓存。

四、读写缓存

读写缓存,顾名思义,就是既可以进行读,也可以进行写的缓存。读主要是给只读缓存来读取的。写主要是将缓存更新到自己的 Map 中。

下面分别从写缓存的原理、写缓存的源码、过期时机的原理、过期时机的源码几个方面来分别解答。

3.1 写缓存的原理和源码

我开始以为当我们读缓存读不到的时候,就会去数据库查了。找了半天,没找到读数据库的地方。

然后我就用 IDEA 工具查找 readOnlyCacheMap 被使用的地方,终于让我找到了。

读写缓存用的是 Guava Cache工具类,这篇不会深究。简单来说就是当访问读写缓存时,如果这个 key 在缓存中不存在,则从本地去查,查到后再放回缓存。

然后又实现抽象方法 load(key),这个方法的作用就是当读写缓存中没有,则从本地 registry 缓存中拿。

读写缓存过期的时候其实分两种:定时过期和实时过期。由于上面的源码已经定义了定时过期的时间间隔,所以我们先来看定时过期。

3.2 定时过期

当构建这个读写缓存时,就会定义间隔多久过期整个读写缓存。如下代码所示,180 s 会定时过期读写缓存。

expireAfterWrite(180s)

3.3 实时过期

当有新的服务实例进行注册或者下线、发生故障时,就会把这个对应的服务实例的缓存给过期掉。

如下图所示,最上面的时注册中心,下面三个是服务实例。服务实例发生注册、下线、发生故障,注册中心都是可以感知到的,然后就会主动过期读写缓存对应的服务实例。

3.4 实时过期源码

从源码层面我们再来看下读写缓存过期的源码。调用了 invalidateCache 方法,进行过期。

文件路径:com/netflix/eureka/registry/AbstractInstanceRegistry.java

五、只读缓存

5.1 定时更新

只读缓存 readOnlyCacheMap,有一个定时更新的机制,每隔 30 秒就会更新一次只读缓存中的某些 key。

它其实是遍历自己的所有注册信息,然后和读写缓存进行比对,如果注册信息不一致,则替换为读写缓存的数据。

源码如下,有一个定时调度任务,每隔 30 秒调度一次。

5.2 更新

另外当客户端获取注册信息时,也会先读只读缓存,如果只读缓存中没有,则会从读写缓存中找,找到后就放到只读缓存中。如果读写缓存中没有,则从本地注册表 registry 中加载到读写缓存中,然后将注册表信息返回。

其实这里的不变是相对于客户端来说的,客户端获取注册表信息时,最开始访问的就是只读缓存,类似数据库或 Redis 的主从架构,主负责读写,从负责读。然后系统内部会把主节点的信息同步给从节点。大家明白了吗?

六、缓存相关配置

下面我们来看下 Eureka Server 对于缓存有哪些配置呢?

6.1 是否开启只读缓存

当客户端获取注册信息时,是否先从只读缓存获取。如果为 false,则直接从读写缓存获取。默认为 true。

6.2 定时更新只读缓存的间隔时间

默认每隔 30 秒将读写缓存更新的缓存同步到只读缓存。

七、缓存带来的问题

三级缓存看似可以带来性能的提升。但是也会引入其他问题,比如缓存不一致问题。

只读缓存每隔 30s 才会刷新一次,和读写缓存会造成数据的不一致,客户端在 30s 内获取的注册表信息是滞后的。

当使用 Eureka 集群时,这种缓存不一致的问题会更明显,不同的节点之间也会出现只读缓存的数据不一致,所以 Eureka 只能保证高可用,并不能保证强一致性,也就是保证了 AP,不保证 CP,另外我们可以选用强一致性的注册中心,比如 Zookeeper、Nacos,这是后续要讲的内容了。

(1)在服务端,我们可以设置更新只读缓存的时间间隔,默认是 30 秒,缩短一点,比如 15 秒,频率太高,可能对 Eureka 造成性能问题。

(2)服务端,我们也可以考虑关闭从只读缓存读注册表信息,Eureka Client 直接从读写缓存读取。

八、总结

本篇学习了 Eureka 注册中心 Server 端的三层缓存架构,分为 registry、readOnlyCacheMap、readWriteCacheMap,用来保存服务注册信息。

  • 默认情况下,每隔 30 秒从读写缓存将注册信息更新到只读缓存。
  • 默认情况下,客户端读取注册表时,先从只读缓存读,如果没有,则从读写缓存中读取,如果还是没有,则从本地注册表 registry 读取。
  • 默认情况下,每隔 180 秒定时过期读写缓存。
  • 服务实例注册、下线、故障时,会实时过期读写缓存。

参考资料: 《微服务架构深度解析》 Eureka 源码

glide4.12图片框架之多级缓存源码设计分析(代码片段)

一、Glide缓存初识在上两篇文章中,我们从源码角度分析Glide框架加载图片的流程、以及Glide图片通过巧妙的空view的Fragment的设计实现的Glide的图片加载的三大生命周期函数onStart、onStop、onDestroy。Glide的框架的源码量确实比较... 查看详情

wireshark抓包分析eureka注册发现协议(代码片段)

欢迎访问我的GitHub前面的系列文章中,我们分析源码对Eureka有了深入了解,其中获取服务列表、注册、续约等操作都涉及到client和server端的交互,今天我们通过Wireshark抓包来分析这些交互的内容,以印证之前的代码分析,加深印... 查看详情

mybatis的缓存机制源码分析之二级缓存解析(代码片段)

引言本篇源码解析基于mybatis3.5.8版本。MyBatis中的缓存指的是MyBatis在执行一次SQL查询时,在满足一定的条件下,会把这个sql和对应的查询结果缓存起来。当再次执行相同SQL语句的时候,就会直接从缓存中进行提取,... 查看详情

glide4.12图片框架之多级缓存源码设计分析(代码片段)

一、Glide缓存初识在上两篇文章中,我们从源码角度分析Glide框架加载图片的流程、以及Glide图片通过巧妙的空view的Fragment的设计实现的Glide的图片加载的三大生命周期函数onStart、onStop、onDestroy。Glide的框架的源码量确实比较... 查看详情

glide4.12图片框架之多级缓存源码设计分析(代码片段)

一、Glide缓存初识在上两篇文章中,我们从源码角度分析Glide框架加载图片的流程、以及Glide图片通过巧妙的空view的Fragment的设计实现的Glide的图片加载的三大生命周期函数onStart、onStop、onDestroy。Glide的框架的源码量确实比较... 查看详情

glide缓存机制及源码(代码片段)

Glide里的缓存默认情况下,Glide会在开始一个新的图片请求之前检查以下多级的缓存:活动资源(ActiveResources)-现在是否有另一个View正在展示这张图片?内存缓存(Memorycache)-该图片是否最近被加载过并仍存在于内存中ÿ... 查看详情

图解kafka源码实现机制之客户端缓存架构

...读完这篇文章,我相信你会对Kafka客户端缓存架构的源码有更加深刻的理解。这篇文章干货很多,希望你可以耐心读完。01总体概述通过场景驱动的方式,当被发送消息通过网络请求封装、NIO多路复用器监听网络读写... 查看详情

kafka源码解析:server端的运行过程

摘要:Kafka网络模块之Server端,介绍Server端启动、接收请求和处理请求的过程。本文分享自华为云社区《Kafka网络模块-Server端》,原文作者:中间件小哥。SocketServer是Kafkaserver端用于处理请求的模块,在Kafka启动过程创建、初始化... 查看详情

1.sprigncloud之快速搭建一个简单的微服务工程

...springcloud相关包的时候就不用特意指定版本了。我们选择eureka作为注册中心。新建一个子工程,指定parent为刚才我们建立的父工程Eureka服务端启动器导入Eureka服务端完整pom文件:application.properties配置文件启动类新建一个子工程订单... 查看详情

springcloud:eureka配置心跳机制

Server服务端server: port:8761eureka: client:  #实例是否在eureka服务器上注册自己的信息以提供其他服务发现,默认为true  register-with-eureka:false  #此客户端是否获取eureka服务器注册表上的注册信息,默认为true&nbs... 查看详情

springcloud技术专题「eureka源码分析」从源码层面让你认识eureka工作流程和运作机制(下)(代码片段)

承接上文的对应的Eureka的上篇介绍,我们开始介绍,详见[【SpringCloud技术专题】「Eureka源码分析」从源码层面让你认识Eureka工作流程和运作机制(上)]原理回顾EurekaServer提供服务注册服务,各个节点启动后... 查看详情

eureka工作原理

参考技术AEureka作为SpringCloud体系中最核心、默认的注册中心组件,研究它的运行机制,有助于我们在工作中更好地使用它。回到上节的服务注册调用示意图,服务提供者和服务的消费者,本质上也是EurekaClient角色。整体上可以分... 查看详情

eureka常见问题

一 Eureka注册慢问题默认情况下,服务注册到Eureka Server过程较慢。在开发或测试时,常常希望加速这一过程,从而提高工作效率。服务注册涉及到周期性心跳,默认30秒一次。只有当实例、服务端和客户端的本地缓存中的... 查看详情

eureka源码之服务端接收注册信息(代码片段)

Eureka注册中心系列文章汇总:[领导让我研究Eureka源码|启动过程][领导“叕”让我研究Eureka源码:注册过程[值得收藏的Eureka控制台详解]大家好,我是悟空。本篇从源码角度带你学习Eureka服务端接收注册的流程。另外我从源码中也... 查看详情

springcloud技术专题「eureka源码分析」从源码层面让你认识eureka工作流程和运作机制(上)(代码片段)

...从过去到现在,SpringCloud中用的最多的注册中心就是Eureka了,所以深入Eureka的原理和源码,接下来我们要进行讲解下eureka的源码分析,由此应运而产生的本章节的内容。基本原理EurekaServer提供服务注册服务 查看详情

eureka源码分析(十二)网络通信

参考技术A下面我们来说一下eureka的网络通信。eureka主要包含两个方面的网络通信:Eureka-Client请求Eureka-Server的网络通信Eureka-Server集群内,Eureka-Server请求其它的Eureka-Server的网络通信EurekaJerseyClient,EurekaHttpClient接口。主要是基于Ap... 查看详情

hadoop源码解析之rpc通信client到server通信

rpc是Hadoop分布式底层通信的基础,无论是client和namenode,namenode和datanode,以及yarn新框架之间的通信模式等等都是采用的rpc方式。下面我们来概要分析一下Hadoop2的rpc。Hadoop通信模式主要是C/S方式,及客户端和服务端的模式。客户... 查看详情

源码角度了解skywalking之告警机制是怎么实现的(代码片段)

源码角度了解Skywalking之告警机制是怎么实现的我们在使用Skywalking的时候有没有想过它的告警机制是怎么实现的呢,本篇文章就介绍一下它的告警机制的实现机制skywalking插件的源码部分在server-alarm-plugin模块中告警初始化AlarmModule... 查看详情