Eureka Client 服务注册流程


决心是成功的开始

注册核心流程图

图解+源码讲解 Eureka Client 服务注册流程_ipad

从哪里开始分析

在客户端初始化的时候 clientConfig.shouldEnforceRegistrationAtInit() 初始化的时候是否开启强制注册,这个值默认是false所以不会进行注册的,是在后续的30s发送心跳机制任务中的renew()方法中进行注册的

if (clientConfig.shouldRegisterWithEureka() &&
// clientConfig.shouldEnforceRegistrationAtInit() 默认是false
clientConfig.shouldEnforceRegistrationAtInit()) {
if (!register()) {
throw new IllegalStateException("Registration error at startup. Invalid server response.");
}
}

核心逻辑

    通过renew()发送心跳的时候返回找不到实例信息,所以再进行注册操作,通过 eurekaTransport.registrationClient 客户端进行发送心跳请求操作

1. 通过心跳定时任务发起注册

boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
httpResponse = eurekaTransport.registrationClient.
sendHeartBeat(instanceInfo.getAppName(),
instanceInfo.getId(), instanceInfo, null);
if (httpResponse.getStatusCode() == Status.NOT_FOUND.getStatusCode()) {
REREGISTER_COUNTER.increment();
long timestamp = instanceInfo.setIsDirtyWithTime();
boolean success = register(); // 注册实例
if (success) {
instanceInfo.unsetIsDirty(timestamp);
}
return success;
}
return httpResponse.getStatusCode() == Status.OK.getStatusCode();
}

    通过 register() 方法进行实例注册,通过 eurekaTransport.registrationClient 进行注册方法访问,访问的是AbstractJersey2EurekaHttpClient的 register 方法

boolean register() throws Throwable {
EurekaHttpResponse<Void> httpResponse;
httpResponse = eurekaTransport.registrationClient.register(instanceInfo);
....
}

    通过 jersey2 框架访问到了 eureka-core 项目下的 AbstractInstanceRegistry 的register方法

public void register(InstanceInfo registrant, int leaseDuration,
boolean isReplication) {
/**
* 通过服务名字从本地的 gMap 中获取一个服务实例信息
*/
Map<String, Lease<InstanceInfo>> gMap = registry.get(registrant.getAppName());
if (gMap == null) {
// 如果本地gMap中没有当前要注册的实例的话创建一个 gNewMap
// 也就是 registry 中的一个服务实例
final ConcurrentHashMap<String, Lease<InstanceInfo>> gNewMap =
new ConcurrentHashMap<String, Lease<InstanceInfo>>();
// 将当前服务名字为key并且服务名字,value就是当前要注册的 gNewMap
gMap = registry.putIfAbsent(registrant.getAppName(), gNewMap);
if (gMap == null) {
gMap = gNewMap;
}
}
// 说白了上面就是创建了一个gMap中的一个实例
gMap.put(registrant.getId(), lease);
}

2. 将当前实例放入到队列中

    这个注册队列和改变队列是在服务初始化的时候创建的,通过这个最近改变的队列可以监听最近改变的状态,新注册的队列和下线的队列主要是为了在页面中显示的,最近改变的队列就是为了在获取增量注册实例信息的时候使用的

/**
* 将当前实例放入到最近注册的队列
*/
recentRegisteredQueue.add(new Pair<Long, String>(System.currentTimeMillis(),
registrant.getAppName() + "(" + registrant.getId() + ")"));
// 设置类型为添加类型
registrant.setActionType(ActionType.ADDED);
/**
* 添加也属于改变所以创建一个最近的改变对象【RecentlyChangedItem】放入到最近的改变队列中,
* 时间戳是当前的系统时间
*/
recentlyChangedQueue.add(new RecentlyChangedItem(lease));

3. 无效本地缓存

/**
* 当有新实例过来的时候无效当前读写缓存
*/
invalidateCache(registrant.getAppName(), registrant.getVIPAddress(),
registrant.getSecureVipAddress());

    无效本地缓存操作就是通过 ResponseCacheImpl 的 invalidate 方法将其进行缓存中的一些key进行从缓存中移除,移除读写缓存中的key值

public void invalidate(Key... keys) {
for (Key key : keys) {
// 移除读写缓存中的key
readWriteCacheMap.invalidate(key);
Collection<Key> keysWithRegions = regionSpecificKeys.get(key);
if (null != keysWithRegions && !keysWithRegions.isEmpty()) {
for (Key keysWithRegion : keysWithRegions) {
readWriteCacheMap.invalidate(keysWithRegion);
}
}
}
}

小结

  1. 通过心跳发起注册
  2. 将当前实例放入到注册队列与最近改变队列中
  3. 无效服务端本地的缓存