服务注册与发现原理
2026/1/15大约 5 分钟Eureka源码分析原理
服务注册与发现原理
一、Eureka 核心概念
1.1 Register(服务注册)
服务启动时,向 Eureka Server 发送 REST 请求注册自身信息。
1.2 Renew(服务续约)
服务定期向 Server 发送心跳,默认每 30 秒一次。
// 心跳请求
PUT /eureka/apps/{appName}/{instanceId}?status=UP1.3 Fetch Registry(获取注册表)
客户端启动时获取全量注册表,之后增量获取。
// 全量获取
GET /eureka/apps
// 增量获取
GET /eureka/apps/delta1.4 Cancel(服务下线)
服务正常关闭时,发送下线请求。
// 下线请求
DELETE /eureka/apps/{appName}/{instanceId}1.5 Eviction(服务剔除)
Server 定期检查,剔除超时未续约的服务(默认 90 秒)。
二、Eureka Server 数据结构
2.1 注册表结构
// 核心数据结构
ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry;
// 结构说明
// 第一层 Key: 服务名称(如 user-service)
// 第二层 Key: 实例 ID(如 192.168.1.100:user-service:8080)
// Value: Lease 对象,包含实例信息和租约信息2.2 Lease 租约对象
public class Lease<T> {
// 实例信息
private T holder;
// 服务注册时间
private long registrationTimestamp;
// 最后更新时间
private long lastUpdateTimestamp;
// 服务下线时间
private long evictionTimestamp;
// 租约持续时间(默认90秒)
private long duration;
}2.3 InstanceInfo 实例信息
public class InstanceInfo {
private String instanceId; // 实例ID
private String appName; // 应用名称
private String ipAddr; // IP地址
private int port; // 端口
private InstanceStatus status; // 状态
private String homePageUrl; // 主页URL
private String healthCheckUrl; // 健康检查URL
private Map<String, String> metadata; // 元数据
// ...
}三、服务注册流程
3.1 客户端注册流程
3.2 服务端处理流程
// ApplicationResource.addInstance() 伪代码
public Response addInstance(InstanceInfo info) {
// 1. 参数校验
validate(info);
// 2. 注册到本地注册表
registry.register(info, isReplication);
// 3. 同步到其他 Eureka Server
replicateToPeers(Action.Register, info);
return Response.status(204).build();
}3.3 注册表写入
// PeerAwareInstanceRegistryImpl.register() 伪代码
public void register(InstanceInfo info, boolean isReplication) {
// 1. 获取或创建应用的实例Map
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
if (gMap == null) {
gMap = new ConcurrentHashMap<>();
registry.put(appName, gMap);
}
// 2. 创建租约
Lease<InstanceInfo> lease = new Lease<>(info, duration);
// 3. 存入注册表
gMap.put(instanceId, lease);
// 4. 更新最近变更队列(用于增量获取)
recentlyChangedQueue.add(new RecentlyChangedItem(lease));
// 5. 使响应缓存失效
invalidateCache(appName);
}四、服务续约流程
4.1 客户端心跳
// HeartbeatThread 伪代码
class HeartbeatThread implements Runnable {
public void run() {
// 发送心跳
boolean success = renew();
if (!success) {
// 心跳失败,重新注册
register();
}
}
}4.2 服务端处理
// InstanceResource.renewLease() 伪代码
public Response renewLease() {
// 1. 查找实例
Lease<InstanceInfo> lease = registry.get(appName).get(instanceId);
if (lease == null) {
// 实例不存在,返回404,客户端会重新注册
return Response.status(404).build();
}
// 2. 更新最后更新时间
lease.renew();
// 3. 同步到其他节点
replicateToPeers(Action.Heartbeat, info);
return Response.ok().build();
}五、服务发现流程
5.1 全量获取
5.2 增量获取
// 增量获取流程
public Applications getApplicationsDelta() {
// 1. 从最近变更队列获取变更
Applications delta = new Applications();
for (RecentlyChangedItem item : recentlyChangedQueue) {
// 只返回最近3分钟的变更
if (item.getLastUpdateTime() > System.currentTimeMillis() - 180000) {
delta.addApplication(item.getApplication());
}
}
return delta;
}5.3 三级缓存机制
| 缓存层级 | 说明 | 过期时间 |
|---|---|---|
| registry | 实时数据 | 不过期 |
| readWriteCacheMap | 读写缓存 | 180秒 |
| readOnlyCacheMap | 只读缓存 | 30秒同步一次 |
六、服务剔除流程
6.1 剔除定时任务
// EvictionTask 伪代码
class EvictionTask implements Runnable {
public void run() {
// 1. 检查是否开启自我保护
if (isLeaseExpirationEnabled()) {
// 2. 遍历所有实例
for (Lease<InstanceInfo> lease : getAllLeases()) {
// 3. 检查是否过期(默认90秒)
if (lease.isExpired()) {
// 4. 剔除实例
evict(lease);
}
}
}
}
}6.2 剔除流程
七、集群同步机制
7.1 同步流程
7.2 同步代码
// PeerAwareInstanceRegistryImpl.replicateToPeers() 伪代码
private void replicateToPeers(Action action, InstanceInfo info) {
// 遍历所有 peer 节点
for (PeerEurekaNode node : peerEurekaNodes) {
// 异步同步
node.replicate(action, info);
}
}7.3 防止循环同步
通过 isReplication 标识区分是客户端请求还是节点同步请求,避免循环同步。
八、总结
核心要点:
- 注册表:ConcurrentHashMap 存储,支持高并发
- 三级缓存:提高读取性能,降低注册表压力
- 增量获取:减少网络传输,提高效率
- 集群同步:异步复制,保证最终一致性
- 服务剔除:定时检查,配合自我保护机制
