Eureka三级缓存架构

打印 上一主题 下一主题

主题 529|帖子 529|积分 1587

Eureka Server缓存机制概述

Eureka是Netflix开源的服务发现框架,它具有高可用、可扩展、易于部署等优点,被广泛应用于微服务架构中。此中一个重要的组件就是Eureka Server,它负责维护服务注册表,以及向客户端提供服务注册信息。
在Eureka Server中,缓存机制是一个非常重要的优化点,它可以显著提拔服务注册表的性能和可靠性。本文将深入分析Eureka Server的缓存机制,并通过源码分析,探究其实现原理和优化计谋。
三级缓存

一级缓存:只读缓存 readOnlyCacheMap,数据结构 ConcurrentHashMap。相当于数据库。
二级缓存:读写缓存 readOnlyCacheMap,Guava Cache。相当于 Redis 主从架构中主节点,既可以进行读也可以进行写。
三级缓存:本地注册表 registry,数据结构 ConcurentHashMap。相当于 Redis 主从架构的从节点,只负责读。
多级缓存的意义

这里为什么要计划多级缓存呢?原因很简单,就是当存在大规模的服务注册和更新时,假如只是修改一个ConcurrentHashMap数据,那么势必由于锁的存在导致竞争,影响性能。
而Eureka又是AP模子,只需要满足最终可用就行。所以它在这里用到多级缓存来实现读写分离。注册方法写的时候直接写内存注册表,写完表之后主动失效读写缓存。
获取注册信息接口先从只读缓存取,只读缓存没有再去读写缓存取,读写缓存没有再去内存注册表里取(不只是取,此处较复杂)。而且,读写缓存会更新回写只读缓存


  • responseCacheUpdateIntervalMs :readOnlyCacheMap 缓存更新的定时器时间间隔,默认为30秒
  • responseCacheAutoExpirationInSeconds : readWriteCacheMap 缓存逾期时间,默认为 180 秒 。
Eureka Server内存缓存实现原理

Eureka Server的内存缓存机制是通过EurekaRegistry类实现的。EurekaRegistry类是Eureka Server中最焦点的组件之一,它负责维护服务注册表,并提供服务注册、注销、查询等接口。在EurekaRegistry类中,内存缓存主要通过以下两个类实现:ReadOnlyClusterRegistry和PeerAwareInstanceRegistry。
ReadOnlyClusterRegistry类是EurekaRegistry的一个内部类,它主要用于向客户端提供服务注册信息。当客户端向Eureka Server发起查询请求时,Eureka Server会从ReadOnlyClusterRegistry中获取服务注册信息,并返回给客户端。同时,ReadOnlyClusterRegistry还负责监控服务注册表的变革,并及时更新缓存中的服务注册信息。
PeerAwareInstanceRegistry类则是EurekaRegistry的另一个内部类,它主要用于维护服务注册表。当Eureka Server收到客户端的服务注册请求时,PeerAwareInstanceRegistry会将服务注册信息添加到内存缓存中。当客户端发起注销请求时,PeerAwareInstanceRegistry会将服务注册信息从内存缓存中删除。同时,PeerAwareInstanceRegistry还负责定时从其他Eureka Server节点获取服务注册信息,并更新本地内存缓存。
PeerAwareInstanceRegistry的内存缓存是通过ConcurrentHashMap实现的。ConcurrentHashMap是Java中线程安全的HashMap实现,它可以支持多个线程同时对同一个HashMap进行操作,从而包管线程安全。在PeerAwareInstanceRegistry中,ConcurrentHashMap主要用于存储服务注册信息。当服务注册信息发生变革时,PeerAwareInstanceRegistry会通过ConcurrentHashMap的put、remove等方法更新内存缓存中的服务注册信息。
下面是一段示例代码,演示了如何利用Eureka Server的内存缓存机制:
  1. public class EurekaServerDemo {
  2.     public static void main(String[] args) throws Exception {
  3.         // 创建Eureka Server实例
  4.         EurekaServerContext serverContext = EurekaServerContextHolder.getInstance().getServerContext();
  5.         EurekaServerConfig serverConfig = serverContext.getServerConfig();
  6.         EurekaServer eurekaServer = new EurekaServer(serverConfig);
  7.         // 启动Eureka Server
  8.         eurekaServer.start();
  9.         // 获取Eureka Server的内存缓存
  10.         PeerAwareInstanceRegistry registry = serverContext.getRegistry();
  11.         // 添加服务注册信息
  12.         InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder()
  13.                 .setAppName("demo")
  14.                 .setInstanceId("demo-1")
  15.                 .setHostName("localhost")
  16.                 .setIPAddr("127.0.0.1")
  17.                 .setPort(8080)
  18.                 .build();
  19.         registry.register(instanceInfo);
  20.         // 查询服务注册信息
  21.         Applications applications = registry.getApplications();
  22.         Application application = applications.getRegisteredApplications("demo");
  23.         InstanceInfo registeredInstance = application.getInstances().get(0);
  24.         // 输出服务注册信息
  25.         System.out.println("Registered instance: " + registeredInstance);
  26.         
  27.         // 关闭Eureka Server
  28.         eurekaServer.shutdown();
  29.     }
  30. }
复制代码
Eureka客户端缓存实现原理

Eureka客户端的缓存机制是通过DiscoveryClient类实现的。DiscoveryClient类是Eureka客户端中最焦点的组件之一,它负责向Eureka Server发起查询请求,并将获取到的服务注册信息缓存到内存中,以便后续快速访问。
在DiscoveryClient中,内存缓存主要通过以下两个类实现:Applications和InstanceInfo。
Applications类是Eureka客户端缓存的服务注册表,它包罗了所有已注册的服务信息。当DiscoveryClient向Eureka Server发起查询请求时,Eureka Server会返回完备的服务注册表信息,并由DiscoveryClient将其缓存到Applications中。同时,DiscoveryClient还会定时从Eureka Server获取服务注册表的增量信息,并更新Applications中的缓存。
InstanceInfo类则是服务注册信息的抽象表现,它包罗了服务的根本信息,比方应用名、实例ID、主机名、IP地址、端口号等。当DiscoveryClient从Eureka Server获取服务注册信息时,会将其转换成InstanceInfo,并缓存到内存中。此外,InstanceInfo还包罗了服务的健康状态、元数据信息等,这些信息也会被缓存到内存中,以便后续快速访问。
下面是一段示例代码,演示了如何利用Eureka客户端的缓存机制:
  1. public class EurekaClientDemo {
  2.     public static void main(String[] args) throws Exception {
  3.         // 创建Eureka客户端实例
  4.         DiscoveryClientConfig clientConfig = new DefaultEurekaClientConfig();
  5.         EurekaClient eurekaClient = new DiscoveryClient(new MyInstanceConfig(), clientConfig);
  6.         // 获取服务注册信息
  7.         List<ServiceInstance> instances = eurekaClient.getInstancesById("demo");
  8.         ServiceInstance instance = instances.get(0);
  9.         // 输出服务注册信息
  10.         System.out.println("Service instance: " + instance);
  11.         // 关闭Eureka客户端
  12.         eurekaClient.shutdown();
  13.     }
  14.     private static class MyInstanceConfig extends MyDataCenterInstanceConfig {
  15.         @Override
  16.         public String getAppname() {
  17.             return "demo-client";
  18.         }
  19.     }
  20. }
复制代码
Eureka缓存优化计谋

Eureka缓存机制的性能和可靠性对于微服务架构的稳定运行至关重要。为了提拔缓存的性能和可靠性,Eureka Server采用了以下优化计谋:

  • 客户端缓存时间:Eureka客户端会将从Eureka Server获取的服务注册表信息缓存在本地内存中。为了避免本地缓存的信息逾期,Eureka客户端会定时从Eureka Server获取服务注册表的增量信息,以更新本地缓存。这个定时更新的时间间隔可以通过设置项eureka.client.registryFetchIntervalSeconds来设置,默认为30秒。
  • 服务端缓存时间:Eureka Server会将服务注册信息缓存在内存中,以便快速响应客户端的查询请求。为了避免缓存的信息逾期,Eureka Server会定时清理逾期的缓存信息,并从其他Eureka Server节点获取最新的服务注册信息。这个定时清理的时间间隔可以通过设置项eureka.server.responseCacheAutoExpirationInSeconds来设置,默认为180秒。
  • 客户端缓存巨细:Eureka客户端缓存的巨细对于性能和可靠性都有很大的影响。假如缓存太小,会影响客户端的查询性能;假如缓存太大,会占用过多的内存资源。Eureka客户端的缓存巨细可以通过设置项eureka.client.fetchRegistry=true和eureka.client.registryRefreshSingleVipAddress来设置。
  • 服务端缓存巨细:Eureka Server缓存的巨细也对于性能和可靠性有很大的影响。假如缓存太小,会影响服务端的响应性能;假如缓存太大,会占用过多的内存资源。Eureka Server的缓存巨细可以通过设置项eureka.server.responseCacheMaxSize来设置,默认为1000。
缓存同步

在ResponseCacheImpl这个类的构造实现中,初始化了一个定时任务,这个定时任务每个
  1. ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
  2.     //省略...
  3.     if (shouldUseReadOnlyResponseCache) {
  4.         timer.schedule(getCacheUpdateTask(),
  5.                        new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
  6.                                 + responseCacheUpdateIntervalMs),
  7.                        responseCacheUpdateIntervalMs);
  8.     }
  9. }
复制代码
默认每30s从readWriteCacheMap更新有差别的数据同步到readOnlyCacheMap中
  1. private TimerTask getCacheUpdateTask() {
  2.     return new TimerTask() {
  3.         @Override
  4.         public void run() {
  5.             logger.debug("Updating the client cache from response cache");
  6.             for (Key key : readOnlyCacheMap.keySet()) { //遍历只读集合
  7.                 if (logger.isDebugEnabled()) {
  8.                     logger.debug("Updating the client cache from response cache for key : {} {} {} {}",
  9.                                  key.getEntityType(), key.getName(), key.getVersion(), key.getType());
  10.                 }
  11.                 try {
  12.                     CurrentRequestVersion.set(key.getVersion());
  13.                     Value cacheValue = readWriteCacheMap.get(key);
  14.                     Value currentCacheValue = readOnlyCacheMap.get(key);
  15.                     if (cacheValue != currentCacheValue) { //判断差异信息,如果有差异,则更新
  16.                         readOnlyCacheMap.put(key, cacheValue);
  17.                     }
  18.                 } catch (Throwable th) {
  19.                     logger.error("Error while updating the client cache from response cache for key {}", key.toStringCompact(), th);
  20.                 } finally {
  21.                     CurrentRequestVersion.remove();
  22.                 }
  23.             }
  24.         }
  25.     };
  26. }
复制代码
缓存失效

在AbstractInstanceRegistry.register这个方法中,当完成服务信息保存后,会调用invalidateCache失效缓存
  1. public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
  2.     //....
  3.      invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
  4.     //....
  5. }
复制代码
最终调用ResponseCacheImpl.invalidate方法,完成缓存的失效机制
  1. public void invalidate(Key... keys) {
  2.     for (Key key : keys) {
  3.         logger.debug("Invalidating the response cache key : {} {} {} {}, {}",
  4.                      key.getEntityType(), key.getName(), key.getVersion(), key.getType(), key.getEurekaAccept());
  5.         readWriteCacheMap.invalidate(key);
  6.         Collection<Key> keysWithRegions = regionSpecificKeys.get(key);
  7.         if (null != keysWithRegions && !keysWithRegions.isEmpty()) {
  8.             for (Key keysWithRegion : keysWithRegions) {
  9.                 logger.debug("Invalidating the response cache key : {} {} {} {} {}",
  10.                              key.getEntityType(), key.getName(), key.getVersion(), key.getType(), key.getEurekaAccept());
  11.                 readWriteCacheMap.invalidate(keysWithRegion);
  12.             }
  13.         }
  14.     }
  15. }
复制代码


免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

水军大提督

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表