Spring Cloud学习--Spring Cloud Eureka服务下线

    前两篇博客我们介绍了Spring Cloud Eureka的服务注册与续约保活实现机制,这篇博客我们简单来介绍一下服务下线的实现机制。Eureka Server对外暴露下线接口,Eureka Client在服务关闭时通过远程调用接口来实现服务下线操作。

Eureka Client 服务下线

    在Eureka Client关闭时会调用DiscoveryClient的shutdown方法,在shutdown方法中会完成一系列的线程关闭操作,并通过http远程调用Eureka Server对外暴露的下线接口进行服务下线操作。

@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中通过http请求远程调用Eureka Server的服务下线接口进行服务下线操作。

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);
            }
        }
    }

Eureka Server 服务下线

    Eureka Server对外提供DELETE方法cancelLease来接收服务的下线处理操作,具体实现在InstanceResource类中。

@DELETE
    public Response cancelLease(
            @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
		//进行服务下线操作
        boolean isSuccess = registry.cancel(app.getName(), id,
                "true".equals(isReplication));

        if (isSuccess) {
            logger.debug("Found (Cancel): " + app.getName() + " - " + id);
            return Response.ok().build();
        } else {
            logger.info("Not Found (Cancel): " + app.getName() + " - " + id);
            return Response.status(Status.NOT_FOUND).build();
        }
    }

在InstanceRegistry中调用cancel方法首先会通知本地服务下线,然后调用父类PeerAwareInstanceRegistryImpl的cancel实现服务下线操作。

@Override
	public boolean cancel(String appName, String serverId, boolean isReplication) {
		//利用spring 事件机制广播服务下线操作
		handleCancelation(appName, serverId, isReplication);
		//父类中完成服务下线操作
		return super.cancel(appName, serverId, isReplication);
	}
	

在PeerAwareInstanceRegistryImpl的cancel中首先调用父类AbstractInstanceRegistry的cancel完成服务下线的操作,然后通知其他服务服务进行下线操作。

public boolean cancel(final String appName, final String id,
                          final boolean isReplication) {
		//调用父类的服务下线实现
        if (super.cancel(appName, id, isReplication)) {
			//通知服务下线
            replicateToPeers(Action.Cancel, appName, id, null, null, isReplication);
            synchronized (lock) {
                if (this.expectedNumberOfRenewsPerMin > 0) {
                    // Since the client wants to cancel it, reduce the threshold (1 for 30 seconds, 2 for a minute)
                    this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin - 2;
                    this.numberOfRenewsPerMinThreshold =
                            (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
                }
            }
            return true;
        }
        return false;
    }

在抽象类AbstractInstanceRegistry中调用cancel完成服务实例信息的删除操作。

@Override
    public boolean cancel(String appName, String id, boolean isReplication) {
        return internalCancel(appName, id, isReplication);
    }
	
	 protected boolean internalCancel(String appName, String id, boolean isReplication) {
        try {
            read.lock();
            CANCEL.increment(isReplication);
			//根据appName获取所有服务名
            Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
            Lease<InstanceInfo> leaseToCancel = null;
            if (gMap != null) {
				//根据id删除服务实例
                leaseToCancel = gMap.remove(id);
            }
            synchronized (recentCanceledQueue) {
                recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
            }
			//根据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);
                    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();
        }
    }
总结:服务下线实现机制分析起来也是比较简单的,Eureka Server通过对外暴露DELETE接口,Eureka Client通过主动调用接口来完成服务下线操作。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值