Eureka 客户端核心机制:服务获取与服务续约的实现解析
Eureka 客户端(Eureka Client)通过两个核心定时任务与 Eureka Server 交互:服务获取(拉取服务注册表)和服务续约(发送心跳维持存活状态)。这两个任务在DiscoveryClient类中初始化,是保证服务发现准确性和服务状态有效性的关键。
定时任务初始化:initScheduledTasks 方法
initScheduledTasks是 Eureka 客户端初始化时的核心方法,用于启动服务获取和服务续约的定时任务。其核心逻辑是:根据配置判断是否需要拉取注册表和注册服务,若需要则创建对应的定时任务。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
| private void initScheduledTasks() { if (clientConfig.shouldFetchRegistry()) { int registryFetchIntervalSeconds = clientConfig.getRegistryFetchIntervalSeconds(); int expBackOffBound = clientConfig.getCacheRefreshExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "cacheRefresh", scheduler, cacheRefreshExecutor, registryFetchIntervalSeconds, TimeUnit.SECONDS, expBackOffBound, new CacheRefreshThread() ), registryFetchIntervalSeconds, TimeUnit.SECONDS); }
if (clientConfig.shouldRegisterWithEureka()) { int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs(); int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound(); scheduler.schedule( new TimedSupervisorTask( "heartbeat", scheduler, heartbeatExecutor, renewalIntervalInSecs, TimeUnit.SECONDS, expBackOffBound, new HeartbeatThread() ), renewalIntervalInSecs, TimeUnit.SECONDS); } }
|
核心设计:
- 两个任务独立线程池执行(
cacheRefreshExecutor和heartbeatExecutor),避免相互影响;
- 使用
TimedSupervisorTask包装,实现任务失败时的指数退避重试(避免频繁失败占用资源);
- 执行间隔可通过配置自定义,平衡实时性与性能。
服务获取:CacheRefreshThread
服务获取的核心目标是:定期从 Eureka Server 拉取服务注册表并更新本地缓存,确保服务消费者能获取最新的服务实例列表。
1. 核心逻辑:fetchRegistry 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27
| private boolean fetchRegistry(boolean forceFullRegistryFetch) { try { Applications applications = getApplications(); if (clientConfig.shouldDisableDelta() || forceFullRegistryFetch || applications == null) { getAndStoreFullRegistry(); } else { getAndUpdateDelta(applications); } onCacheRefreshed(); updateInstanceRemoteStatus(); return true; } catch (Throwable e) { logger.error("无法刷新服务缓存!", e); return false; } }
|
拉取策略:
- 全量拉取(getAndStoreFullRegistry):首次启动或配置禁用增量时,拉取完整的服务注册表(所有服务 + 所有实例);
- 增量拉取(getAndUpdateDelta):后续拉取仅获取上次同步后新增 / 修改 / 删除的服务实例,减少网络传输开销。
2. 缓存刷新:onCacheRefreshed
拉取注册表后,需要更新本地缓存,并通知负载均衡器(如 Ribbon)刷新实例列表:
1 2 3 4 5 6 7 8 9 10
| private void onCacheRefreshed() { if (cacheRefreshedListener != null) { cacheRefreshedListener.onCacheRefreshed(); } for (EurekaEventListener listener : eventListeners) { listener.onEvent(new CacheRefreshedEvent()); } }
|
负载均衡器更新:
缓存刷新事件被EurekaNotificationServerListUpdater捕获后,会触发负载均衡器的实例列表更新:
1 2 3
| updateAction.doUpdate(); lastUpdated.set(System.currentTimeMillis());
|
这样,服务消费者在调用服务时,就能使用最新的实例列表进行负载均衡。
服务续约:HeartbeatThread
服务续约的核心目标是:定期向 Eureka Server 发送心跳,证明服务实例存活,避免被 Eureka Server 剔除。
1. 核心逻辑:renew 方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| boolean renew() { EurekaHttpResponse<InstanceInfo> httpResponse; try { httpResponse = eurekaTransport.registrationClient.sendHeartBeat( instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null ); if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) { REREGISTER_COUNTER.increment(); logger.info("实例已不存在,重新注册服务..."); long timestamp = instanceInfo.setIsDirtyWithTime(); boolean success = register(); if (success) { instanceInfo.unsetIsDirty(timestamp); } return success; } return httpResponse.getStatusCode() == Status.OK.getStatusCode(); } catch (Throwable e) { logger.error("发送心跳失败!", e); return false; } }
|
核心机制:
- 心跳请求格式:
PUT /eureka/apps/{服务名}/{实例ID},携带实例当前状态;
- 异常处理:若心跳失败(如网络波动),
TimedSupervisorTask会触发重试(指数退避策略);
- 重新注册:若 Server 返回 404(实例已被删除),客户端会自动重新注册,保证服务可用性。
2. 续约配置参数
| 配置参数 |
默认值 |
说明 |
eureka.instance.lease-renewal-interval-in-seconds |
30 |
心跳间隔(秒),即服务续约的定时任务间隔 |
eureka.instance.lease-expiration-duration-in-seconds |
90 |
服务过期时间(秒):Server 多久未收到心跳则标记实例为失效 |
总结:服务获取与续约的核心价值
- 服务获取:
通过 “全量拉取 + 增量更新” 的策略,在减少网络开销的同时,保证客户端缓存的服务注册表尽可能新,为服务调用提供准确的实例列表。
- 服务续约:
通过定时心跳机制,让 Eureka Server 实时感知服务实例的存活状态,结合 “自动重新注册” 逻辑,最大程度避免服务因临时故障被误剔除。
- 整体设计思想:
Eureka 客户端通过 “定时任务 + 容错重试 + 增量更新” 的组合策略,在保证服务发现实时性的同时,兼顾了网络开销和系统稳定性,这也是 Eureka 作为 AP 系统高可用特性的重要体现