服务实例下线
shutdown
DiscoveryClient
中的shutdown()
方法是服务实例关闭方法,会关闭instanceInfoReplicator
、心跳线程池、缓存刷新线程池和定时任务线程池,会调用服务下线方法unregister()
,关闭心跳监控和注册监控。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
@PreDestroy
@Override
public synchronized void shutdown() {
if (isShutdown.compareAndSet(false, true)) {
logger.info("Shutting down DiscoveryClient ...");
if (statusChangeListener != null && applicationInfoManager != null) {
applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());
}
cancelScheduledTasks();
// If APPINFO was registered
if (applicationInfoManager != null && clientConfig.shouldRegisterWithEureka()) {
applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
unregister();
}
if (eurekaTransport != null) {
eurekaTransport.shutdown();
}
heartbeatStalenessMonitor.shutdown();
registryStalenessMonitor.shutdown();
logger.info("Completed shut down of DiscoveryClient");
}
}
unregister()
DiscoveryClient
中的unregister()
,取消注册,调用EurekaHttpClient
的cancel()
方法,http://localhost:8080/v2/apps/ServiceA/i-00000-1,delete请求。
1
2
3
4
5
6
7
8
9
10
11
12
void unregister() {
// It can be null if shouldRegisterWithEureka == false
if(eurekaTransport != null && eurekaTransport.registrationClient != null) {
try {
logger.info("Unregistering ...");
EurekaHttpResponse<Void> httpResponse = eurekaTransport.registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
logger.info(PREFIX + appPathIdentifier + " - deregister status: " + httpResponse.getStatusCode());
} catch (Exception e) {
logger.error(PREFIX + appPathIdentifier + " - de-registration failed" + e.getMessage(), e);
}
}
}
server端处理
ApplicationsResource.getApplicationResource()->ApplicationResource.getInstanceInfo()->InstanceResource.cancelLease()->AbstractInstanceRegistry.cancel()->AbstractInstanceRegistry.internalCancel()
。registry
移除注册表的实例信息。移除的实例放入recentChangedQueue
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
45
46
47
protected boolean internalCancel(String appName, String id, boolean isReplication) {
try {
//读锁
read.lock();
CANCEL.increment(isReplication);
//从注册表获取服务实例的map
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> leaseToCancel = null;
if (gMap != null) {
//服务实例map移除此id的实例
leaseToCancel = gMap.remove(id);
}
//移除的服务实例放入取消queue
synchronized (recentCanceledQueue) {
recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
}
InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
if (instanceStatus != null) {
logger.debug("Removed instance id {} from the overridden map which has value {}", id, instanceStatus.name());
}
if (leaseToCancel == null) {
CANCEL_NOT_FOUND.increment(isReplication);
logger.warn("DS: Registry: cancel failed because Lease is not registered for: {}/{}", appName, id);
return false;
} else {
//修改取消时间戳
leaseToCancel.cancel();
InstanceInfo instanceInfo = leaseToCancel.getHolder();
String vip = null;
String svip = null;
if (instanceInfo != null) {
instanceInfo.setActionType(ActionType.DELETED);
//取消的实例放入最近变化queue,用于eureka client增量拉取
recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
instanceInfo.setLastUpdatedTimestamp();
vip = instanceInfo.getVIPAddress();
svip = instanceInfo.getSecureVipAddress();
}
//清理缓存中实例
invalidateCache(appName, vip, svip);
logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
return true;
}
} finally {
read.unlock();
}
}
总结
- 在注册中心,将服务实例从注册表中移除,下线的服务放入
recentChangedQueue
中; - 每个eureka client务都会定时拉取增量注册表,此时可以从
recentChangedQueue
中感知到下线的服务实例,然后在自己本地缓存中删除那个下线的服务实例;