图文详述Eureka的缓存机制/三级缓存

简介: 图文详述Eureka的缓存机制/三级缓存

前言

1、为什么说Eureka是CAP理论中的AP?

从CAP理论看,Eureka是一个AP系统,其优先保证可用性(A)和分区容错性(P),不保证强一致性(C),但能做到最终一致性。

  • 因为只要集群中任意一个实例不出现问题,Eureka服务就是可用的;即Eureka Client 在向某个 Eureka Server 注册时,如果发现连接失败,则会自动切换至其它节点;
  • 另外Eureka集群中没有主从的概念,各个节点都是平等的,节点间采用Replicate异步的方式来同步数据;

也正因为Eureka 本身不保证数据的强一致性,所以在架构又设计了很多缓存。

2、为什么要设计那么多缓存(三级缓存)?

这个和MySQL用主从做读写分离很相似,数据库层面的读写分离是为了分摊主数据库的读压力;而Eureka的三级缓存也是为了做读写分离,使写操作不阻塞读操作。

  • 因为在的时候,线程会持有ConcurrentHashmap相应Hash桶节点对象的锁,阻塞同一个Hash桶的其他读线程。
  • 这样可以有效的降低读写并发,避免读写读写争抢资源所带来的压力。

那么加了那么多缓存,如何保证缓存数据的最终一致性?

我们下面就详细聊一下Eureka是如何做的。

缓存机制

Eureka Server中有三个变量用来保存服务注册信息,分别是:registryreadWriteCacheMapreadOnlyCacheMap;默认情况下定时任务每30s将二级缓存readWriteCacheMap同步至三级缓存readOnlyCacheMap;

1、Eureka Server每60s清理超过90s * 2(官方彩蛋)未续约的节点;
2、Eureka Client每30s从readOnlyCacheMap更新服务注册信息;
3、UI界面则从registry获取最新的服务注册信息。

1、三级缓存分别是什么?

缓存 缓存类型 所处类 概述
registry
一级缓存
ConcurrentHashMap AbstractInstanceRegistry 实时更新,又名注册表,UI界面从这里获取服务注册信息;
readWriteCacheMap<br/>二级缓存 Guava Cache(LoadingCache) ResponseCacheImpl 实时更新,缓存时间180秒;
readOnlyCacheMap<br/>三级缓存 ConcurrentHashMap ResponseCacheImpl 周期更新,默认每30s从二级缓存readWriteCacheMap中同步数据更新;
Eureka Client默认从这里获取服务注册信息,可配为直接从readWriteCacheMap获取

二级缓存又称读写缓存、三级缓存又称只读缓存;

1)Eureka Server缓存相关配置

配置名 默认值 概述
eureka.server.useReadOnlyResponseCache true Client从readOnlyCacheMap更新数据,false则跳过readOnlyCacheMap直接从readWriteCacheMap更新
eureka.server.responsecCacheUpdateIntervalMs 30000 readWriteCacheMap更新至readOnlyCacheMap的周期,默认30s

2、缓存之间的数据同步

AbstractInstanceRegistry类中的registry字段为注册表、并与保存一级缓存(实时最新数据);ResponseCacheImpl类中的readWriteCacheMap字段和readOnlyCacheMap字段分别表示二级缓存和三级缓存;下面我们就围绕这两个类、三个字段的数据同步展开讨论。

1)注册一个服务实例

Eureka Server中做的操作:

向注册表registry中写入服务实例信息,并使得二级缓存失效;

在这里插入图片描述
Eureka client第一次向Eureka Server注册服务或者发送心跳续约时,会进去到Eureka Serve中ApplicationResource#addInstance()方法中:
在这里插入图片描述
最终进入到AbstractInstanceRegistry#register()方法中,往其registry字段中添加服务注册信息:
在这里插入图片描述
这里以服务注册为例,我们知道了registry的数据来源;

注意,在做服务注册的最后会将二级缓存清空/失效;
在这里插入图片描述
下面我们接着来看一下Eureka的二级缓存和三级缓存是如何运作的?

2)二级/三级缓存什么时候初始化?

Eureka Server启动的时候会根据SpringBoot自动装配的特性,初始化EurekaServerContext接口的实现类DefaultEurekaServerContext;在DefaultEurekaServerContext初始化时会执行构造器后置逻辑initialize(),其中会初始化注册中心;
在这里插入图片描述
接着进入到PeerAwareInstanceRegistry接口的实现类PeerAwareInstanceRegistryImpl#init()方法中,其中会通过调用initializedResponseCache()方法初始化二级/三级缓存

3)二级/三级缓存初始化都做了什么?

这里可以看到initializedResponseCache()方法中直接new了一个ResponseCacheImpl类;
在这里插入图片描述
我们接着进入到ResponseCacheImpl类中,看一下它的构造函数:
在这里插入图片描述
总的来说,在初始化ResponseCacheImpl类时:

  1. 会设置初始化二级缓存readWriteCacheMap过期时间180s),设置二级缓存往三级缓存readOnlyCacheMap同步的时间间隔(默认30s)。
  2. 设置是否使用三级缓存(默认使用),如果使用则启动一个定时任务,默认每隔30s从二级缓存中同步数据到三级缓存(只更新三级缓存中已存在的key);

4、发现/寻找一个服务

针对Eureka Client和UI界面,他们读取的服务注册信息的方式略有不同:

  • 针对Eureka Client:
  • 1、如果使用只读缓存(三级缓存)<默认使用>,则先从只读缓存中获取;如果获取不到,则从读写缓存(二级缓存)中获取,并将数据缓存到只读缓存;
    2、不使用只读缓存,则直接从读写缓存中获取;如果获取不到则触发guava的回调函数从注册表registry中同步数据(即从一级缓存 -- 注册表registry中取)
  • 而对于UI界面,则是实时从一级缓存(注册表registry)中取。

对于Eureka Client无论是获取一个Application的信息(入口为ApplicationResource#getApplication())还是获取所有Application的信息(入口为ApplicationsResource#getContainers())都会进入到ResponseCacheImple#get(Key key)方法,然后进过此路径《get(key, shouldUseReadOnlyResponseCache) --> getValue(final Key key, boolean useReadOnlyCache)》最终走到真正从缓存中读取数据的逻辑:
在这里插入图片描述

在这里插入图片描述

总结

1、Eureka Server 在接收Eureka Client注册的时候,会将读写缓存(二级缓存)清空;
2、Eureka Server启动时会做两件事:

  1. 会初始化读写缓存(二级缓存),从注册表registry(一级缓存)中实时加载数据,默认180s过期
  2. 判定是否使用只读缓存(三级缓存),默认开启;如果使用则开启一个定时任务,默认每30s做一次读写缓存到只读缓存的数据同步;

3、Eureka Client获取服务信息时,默认先从只读缓存获取;获取不到再从读写缓存中获取,并将数据缓存到只读缓存;获取不到,再触发guava的回调函数从注册表中同步(即从一级缓存 -- 注册表registry中取)。

1、多级缓存带来的好处?

  • 尽可能的避免服务注册出现频繁的读写冲突,写阻塞读;
  • 提高Eureka Server服务的读写性能。

面对频繁读写资源争抢、写阻塞读等情况,我可以考虑借鉴Eureka的多级缓存方案做读写分离。

相关文章
|
4月前
|
存储 缓存 前端开发
HTTP的缓存机制是什么?
HTTP的缓存机制是什么?
33 1
|
7月前
|
SQL 缓存 Java
Mybatis-plus缓存机制
MyBatis-Plus(简称MP)是一个基于MyBatis的增强工具,提供了更便捷的CRUD操作和其他功能。与MyBatis相比,MyBatis-Plus并没有引入自己的缓存机制,而是直接使用了MyBatis的缓存机制。 在MyBatis中,缓存分为一级缓存和二级缓存。 1. 一级缓存:一级缓存是SqlSession级别的缓存,它默认是开启的。当查询操作执行时,查询的结果会被缓存在SqlSession的内部数据结构中。如果后续再次执行相同的查询,MyBatis会先检查一级缓存中是否存在结果,如果存在则直接返回缓存的结果,而不会再次执行SQL语句。一级缓存的生命周期与SqlSession相同,
259 0
|
6天前
|
存储 缓存 运维
【Docker 专栏】Docker 镜像的分层存储与缓存机制
【5月更文挑战第8天】Docker 镜像采用分层存储,减少空间占用并提升构建效率。每个镜像由多个层组成,共享基础层(如 Ubuntu)和应用层。缓存机制加速构建和运行,通过检查已有层来避免重复操作。有效管理缓存,如清理无用缓存和控制大小,可优化性能。分层和缓存带来资源高效利用、快速构建和灵活管理,但也面临缓存失效和层管理挑战。理解这一机制对开发者和运维至关重要。
【Docker 专栏】Docker 镜像的分层存储与缓存机制
|
4月前
|
存储 消息中间件 缓存
redis的缓存机制
redis的缓存机制
102 0
|
12天前
|
缓存 NoSQL Java
17:缓存机制-Java Spring
17:缓存机制-Java Spring
38 5
|
13天前
|
存储 缓存 自然语言处理
深入PHP内核:探索Opcode缓存机制
【5月更文挑战第1天】 在动态语言如PHP的执行过程中,每次脚本被请求时都需要经过一系列复杂的解析和编译步骤。为了优化这一过程并提高性能,PHP引入了Opcode缓存机制。本文将详细探讨Opcode的概念、作用以及它如何显著提升PHP应用的执行效率。我们将从缓存原理出发,分析几种常见的Opcode缓存工具,并通过实例说明如何在实际项目中实现和优化缓存策略。
|
15天前
|
缓存 NoSQL PHP
【PHP开发专栏】PHP缓存机制与实现
【4月更文挑战第29天】本文介绍了PHP缓存的基本概念、策略及实现方式。PHP缓存包括应用缓存、Web服务器缓存、数据库缓存和分布式缓存,常见策略有缓存预热、更新和懒加载。PHP的缓存实现包括文件缓存、APC、OPcache、Memcached和Redis。最佳实践包括缓存热点数据、控制粒度、设置失效策略、保证一致性和确保安全性。文中还提供了一个新闻列表和详情页的缓存实战示例,帮助开发者理解如何在实际项目中应用缓存。
|
21天前
|
缓存 流计算
缓存命中率和过期机制的一般思路
【4月更文挑战第20天】缓存命中率是评估缓存效果的关键,目标是达到90%以上,但某些频繁的小请求场景可能无法实现。过期机制可采用定时删除(精确但开销大)、延迟队列(精确但有队列开销)、懒惰删除(简单但时间不精确)或定期删除(简单但性能损耗不可控)。
19 4
|
21天前
|
缓存 Linux
linux系统缓存机制
linux系统缓存机制
|
29天前
|
缓存 数据库 开发者
Django视图中的缓存机制:提升页面加载速度
【4月更文挑战第15天】本文介绍了Django的缓存机制在提升页面加载速度中的作用。Django提供视图缓存和片段缓存,通过`cache_page`装饰器和`CacheMixin`实现视图级别的缓存,使用`{% cache %}`模板标签实现页面片段缓存。开发者可选择不同的缓存后端,并在设置中配置缓存参数。同时,注意合理控制缓存粒度、及时更新和管理缓存,以优化用户体验和网站性能。