马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
x
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的内存缓存机制:
- public class EurekaServerDemo {
- public static void main(String[] args) throws Exception {
- // 创建Eureka Server实例
- EurekaServerContext serverContext = EurekaServerContextHolder.getInstance().getServerContext();
- EurekaServerConfig serverConfig = serverContext.getServerConfig();
- EurekaServer eurekaServer = new EurekaServer(serverConfig);
- // 启动Eureka Server
- eurekaServer.start();
- // 获取Eureka Server的内存缓存
- PeerAwareInstanceRegistry registry = serverContext.getRegistry();
- // 添加服务注册信息
- InstanceInfo instanceInfo = InstanceInfo.Builder.newBuilder()
- .setAppName("demo")
- .setInstanceId("demo-1")
- .setHostName("localhost")
- .setIPAddr("127.0.0.1")
- .setPort(8080)
- .build();
- registry.register(instanceInfo);
- // 查询服务注册信息
- Applications applications = registry.getApplications();
- Application application = applications.getRegisteredApplications("demo");
- InstanceInfo registeredInstance = application.getInstances().get(0);
- // 输出服务注册信息
- System.out.println("Registered instance: " + registeredInstance);
-
- // 关闭Eureka Server
- eurekaServer.shutdown();
- }
- }
复制代码 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客户端的缓存机制:
- public class EurekaClientDemo {
- public static void main(String[] args) throws Exception {
- // 创建Eureka客户端实例
- DiscoveryClientConfig clientConfig = new DefaultEurekaClientConfig();
- EurekaClient eurekaClient = new DiscoveryClient(new MyInstanceConfig(), clientConfig);
- // 获取服务注册信息
- List<ServiceInstance> instances = eurekaClient.getInstancesById("demo");
- ServiceInstance instance = instances.get(0);
- // 输出服务注册信息
- System.out.println("Service instance: " + instance);
- // 关闭Eureka客户端
- eurekaClient.shutdown();
- }
- private static class MyInstanceConfig extends MyDataCenterInstanceConfig {
- @Override
- public String getAppname() {
- return "demo-client";
- }
- }
- }
复制代码 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这个类的构造实现中,初始化了一个定时任务,这个定时任务每个
- ResponseCacheImpl(EurekaServerConfig serverConfig, ServerCodecs serverCodecs, AbstractInstanceRegistry registry) {
- //省略...
- if (shouldUseReadOnlyResponseCache) {
- timer.schedule(getCacheUpdateTask(),
- new Date(((System.currentTimeMillis() / responseCacheUpdateIntervalMs) * responseCacheUpdateIntervalMs)
- + responseCacheUpdateIntervalMs),
- responseCacheUpdateIntervalMs);
- }
- }
复制代码 默认每30s从readWriteCacheMap更新有差别的数据同步到readOnlyCacheMap中
- private TimerTask getCacheUpdateTask() {
- return new TimerTask() {
- @Override
- public void run() {
- logger.debug("Updating the client cache from response cache");
- for (Key key : readOnlyCacheMap.keySet()) { //遍历只读集合
- if (logger.isDebugEnabled()) {
- logger.debug("Updating the client cache from response cache for key : {} {} {} {}",
- key.getEntityType(), key.getName(), key.getVersion(), key.getType());
- }
- try {
- CurrentRequestVersion.set(key.getVersion());
- Value cacheValue = readWriteCacheMap.get(key);
- Value currentCacheValue = readOnlyCacheMap.get(key);
- if (cacheValue != currentCacheValue) { //判断差异信息,如果有差异,则更新
- readOnlyCacheMap.put(key, cacheValue);
- }
- } catch (Throwable th) {
- logger.error("Error while updating the client cache from response cache for key {}", key.toStringCompact(), th);
- } finally {
- CurrentRequestVersion.remove();
- }
- }
- }
- };
- }
复制代码 缓存失效
在AbstractInstanceRegistry.register这个方法中,当完成服务信息保存后,会调用invalidateCache失效缓存
- public void register(InstanceInfo registrant, int leaseDuration, boolean isReplication) {
- //....
- invalidateCache(registrant.getAppName(), registrant.getVIPAddress(), registrant.getSecureVipAddress());
- //....
- }
复制代码 最终调用ResponseCacheImpl.invalidate方法,完成缓存的失效机制
- public void invalidate(Key... keys) {
- for (Key key : keys) {
- logger.debug("Invalidating the response cache key : {} {} {} {}, {}",
- key.getEntityType(), key.getName(), key.getVersion(), key.getType(), key.getEurekaAccept());
- readWriteCacheMap.invalidate(key);
- Collection<Key> keysWithRegions = regionSpecificKeys.get(key);
- if (null != keysWithRegions && !keysWithRegions.isEmpty()) {
- for (Key keysWithRegion : keysWithRegions) {
- logger.debug("Invalidating the response cache key : {} {} {} {} {}",
- key.getEntityType(), key.getName(), key.getVersion(), key.getType(), key.getEurekaAccept());
- readWriteCacheMap.invalidate(keysWithRegion);
- }
- }
- }
- }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。 |