深入理解Eureka 主动下线(四)
程序员文章站
2022-07-07 14:42:32
...
程序入口
com.netflix.discovery.DiscoverClient
1@PreDestroy
2@Override
3public synchronized void shutdown() {
4 if (isShutdown.compareAndSet(false, true)) {
5 logger.info("Shutting down DiscoveryClient ...");
6
7 if (statusChangeListener != null && applicationInfoManager != null) {
8 applicationInfoManager.unregisterStatusChangeListener(statusChangeListener.getId());
9 }
10 // 取消定时任务(心跳,缓存刷新等)
11 cancelScheduledTasks();
12
13 // 如果app注册过,那么需要取消注册,也就说需要主动下线
14 if (applicationInfoManager != null && clientConfig.shouldRegisterWithEureka()) {
15 // 设置实例的状态为DOWN
16 applicationInfoManager.setInstanceStatus(InstanceStatus.DOWN);
17 // 执行下线
18 unregister();
19 }
20
21 if (eurekaTransport != null) {
22 eurekaTransport.shutdown();
23 }
24
25 heartbeatStalenessMonitor.shutdown();
26 registryStalenessMonitor.shutdown();
27
28 logger.info("Completed shut down of DiscoveryClient");
29 }
30}
在这个类被容器销毁的时候,会执行这个方法,执行主动下线的代码
unregister()
1void unregister() {
2 // 如果是非注册的实例,那么eurekaTransport可能是为空的,所以做一下判断
3 if(eurekaTransport != null && eurekaTransport.registrationClient != null) {
4 try {
5 logger.info("Unregistering ...");
6 // 发送HTTP请求,到服务端,请求下线 , 接口地址: "apps/" + appName + '/' + id;
7 EurekaHttpResponse<Void> httpResponse = eurekaTransport.registrationClient.cancel(instanceInfo.getAppName(), instanceInfo.getId());
8 logger.info(PREFIX + appPathIdentifier + " - deregister status: " + httpResponse.getStatusCode());
9 } catch (Exception e) {
10 logger.error(PREFIX + appPathIdentifier + " - de-registration failed" + e.getMessage(), e);
11 }
12 }
13}
Eureka-Server
InstanceResource
com.netflix.eureka.resources.InstanceResource
1@DELETE
2public Response cancelLease(
3 @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
4 //执行下线请求
5 boolean isSuccess = registry.cancel(app.getName(), id,
6 "true".equals(isReplication));
7
8 if (isSuccess) {
9 logger.debug("Found (Cancel): " + app.getName() + " - " + id);
10 return Response.ok().build();
11 } else {
12 logger.info("Not Found (Cancel): " + app.getName() + " - " + id);
13 return Response.status(Status.NOT_FOUND).build();
14 }
15}
InstanceRegistry
org.springframework.cloud.netflix.eureka.server.InstanceRegistry
1@Override
2public boolean cancel(String appName, String serverId, boolean isReplication) {
3 //发布取消事件
4 handleCancelation(appName, serverId, isReplication);
5 // 调用父类的取消方法
6 return super.cancel(appName, serverId, isReplication);
7}
8private void handleCancelation(String appName, String id, boolean isReplication) {
9 log("cancel " + appName + ", serverId " + id + ", isReplication " + isReplication);
10 publishEvent(new EurekaInstanceCanceledEvent(this, appName, id, isReplication));
11}
父类PeerAwareInstanceRegistryImpl
1@Override
2public boolean cancel(final String appName, final String id,
3 final boolean isReplication) {
4 // 执行父类的取消方法
5 if (super.cancel(appName, id, isReplication)) {
6 //集群同步信息
7 replicateToPeers(Action.Cancel, appName, id, null, null, isReplication);
8 // Eureka-Server的保护机制
9 synchronized (lock) {
10 if (this.expectedNumberOfRenewsPerMin > 0) {
11 // Since the client wants to cancel it, reduce the threshold (1 for 30 seconds, 2 for a minute)
12 this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin - 2;
13 this.numberOfRenewsPerMinThreshold =
14 (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
15 }
16 }
17 return true;
18 }
19 return false;
20}
PeerAwareInstanceRegistryImpl 的父类AbstractInstanceRegistry中是
取消下线的主要逻辑。
1@Override
2public boolean cancel(String appName, String id, boolean isReplication) {
3 return internalCancel(appName, id, isReplication);
4}
5
6/**
7 * {@link #cancel(String, String, boolean)} method is overridden by {@link PeerAwareInstanceRegistry}, so each
8 * cancel request is replicated to the peers. This is however not desired for expires which would be counted
9 * in the remote peers as valid cancellations, so self preservation mode would not kick-in.
10 */
11protected boolean internalCancel(String appName, String id, boolean isReplication) {
12 try {
13 // 读锁
14 read.lock();
15 // 添加取消次数给监控信息,这里是个枚举类,收集了取消次数
16 CANCEL.increment(isReplication);
17 // 从本地的CurrentHashMap中,获取当前实例对应的Lease信息
18 Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
19 Lease<InstanceInfo> leaseToCancel = null;
20 if (gMap != null) {
21 // 移除信息 , 如果客户端是集群模式,此处仅移除这个实例ID对应的信息
22 leaseToCancel = gMap.remove(id);
23 }
24 // 添加取消信息到取消队列,主要用于运维界面的信息统计
25 synchronized (recentCanceledQueue) {
26 recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
27 }
28 //移除这个实例ID对应的instance状态
29 InstanceStatus instanceStatus = overriddenInstanceStatusMap.remove(id);
30 if (instanceStatus != null) {
31 logger.debug("Removed instance id {} from the overridden map which has value {}", id, instanceStatus.name());
32 }
33 if (leaseToCancel == null) {
34 // 如果信息不存在,则说明这个实例从来没有注册过来,或者已经下线了。
35 CANCEL_NOT_FOUND.increment(isReplication);
36 logger.warn("DS: Registry: cancel failed because Lease is not registered for: {}/{}", appName, id);
37 return false;
38 } else {
39 // 更新Lease实例信息里面的evictionTimestamp这个时间戳,标明下线时间
40 leaseToCancel.cancel();
41 InstanceInfo instanceInfo = leaseToCancel.getHolder();
42 String vip = null;
43 String svip = null;
44 // 获取VIP,SVIP,然后把instance的变化加入实例变化队列中
45 if (instanceInfo != null) {
46 instanceInfo.setActionType(ActionType.DELETED);
47 recentlyChangedQueue.add(new RecentlyChangedItem(leaseToCancel));
48 instanceInfo.setLastUpdatedTimestamp();
49 vip = instanceInfo.getVIPAddress();
50 svip = instanceInfo.getSecureVipAddress();
51 }
52 // 显示的清楚缓存 , guava的API
53 invalidateCache(appName, vip, svip);
54 logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
55 return true;
56 }
57 } finally {
58 read.unlock();
59 }
60}
综上可以看到,首先是从本地的CurrentHashMap中获取当前appName对应的的Map信息,最后通过机器ID,获取要下线的机器
对应的Lease,修改Lease的evictionTimestamp , 也就是设置下线时间为当前时间点。
1public void cancel() {
2 if (evictionTimestamp <= 0) {
3 evictionTimestamp = System.currentTimeMillis();
4 }
5}
主动下线还是比较简单的
上一篇: HID设备的研究总结
下一篇: H5播放HLS之DPlayer播放
推荐阅读
-
深入理解在JS中通过四种设置事件处理程序的方法
-
深入理解android消息机制(四)——消息队列延时机制(很有趣)
-
深入理解Eureka 主动下线(四)
-
深入理解SQL的四种连接-左外连接、右外连接、内连接、全连接_MySQL
-
深入理解docker的四种网络方式
-
深入理解SQL的四种连接-左外连接、右外连接、内连接、全连接_MySQL
-
深入理解ajax数据请求的四大步骤
-
深入理解Nginx第四章:HTTP配置项的步骤整理 DigiCert Nginx Nginx Certificate Chain Nginx https Prox
-
深入理解ajax数据请求的四大步骤
-
深入理解MySQL进阶漂流记(四)