十三.SpringCloud源码剖析-Eureka Server服务下线

简介: 在《[Eureka Client取消注册](https://blog.csdn.net/u014494148/article/details/108319815)》中我们有分析到,当Eureka Client 客户端当服务关闭,触发客户端服务下线方法,客户端执行一系列下线逻辑后会向Eureka Server服务端发送服务下线请求,服务端处理下线的请求是在com.netflix.eureka.resources.InstanceResource#cancelLease方法中

系列文章目录

一.SpringCloud源码剖析-Eureka核心API

二.SpringCloud源码剖析-Eureka Client 初始化过程

三.SpringCloud源码剖析-Eureka服务注册

四.SpringCloud源码剖析-Eureka服务发现

五.SpringCloud源码剖析-Eureka Client服务续约

六.SpringCloud源码剖析-Eureka Client取消注册

七.SpringCloud源码剖析-Eureka Server的自动配置

八.SpringCloud源码剖析-Eureka Server初始化流程

九.SpringCloud源码剖析-Eureka Server服务注册流程

十.SpringCloud源码剖析-Eureka Server服务续约

十一.SpringCloud源码剖析-Eureka Server服务注册表拉取

十二.SpringCloud源码剖析-Eureka Server服务剔除

Eureka Server 服务下线

在《Eureka Client取消注册》中我们有分析到,当Eureka Client 客户端当服务关闭,触发客户端服务下线方法,客户端执行一系列下线逻辑后会向Eureka Server服务端发送服务下线请求,服务端处理下线的请求是在com.netflix.eureka.resources.InstanceResource#cancelLease方法中

    /**
    处理此特定实例的租赁取消
     * Handles cancellation of leases for this particular instance.
     *
     * @param isReplication
     *            a header parameter containing information whether this is
     *            replicated from other nodes.
     * @return response indicating whether the operation was a success or
     *         failure.
     */
    @DELETE
    public Response cancelLease(
            @HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication) {
   
   
        try {
   
   
        //调用 InstanceRegistry的 cancel 方法下线服务
            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();
            }
        } catch (Throwable e) {
   
   
            logger.error("Error (cancel): {} - {}", app.getName(), id, e);
            return Response.serverError().build();
        }

    }

InstanceRegistry的 cancel 方法

 public class InstanceRegistry extends PeerAwareInstanceRegistryImpl
        implements ApplicationContextAware {
   
   
     @Override
    public boolean cancel(String appName, String serverId, boolean isReplication) {
   
   
        //抛出EurekaInstanceCanceledEvent取消事件
        handleCancelation(appName, serverId, isReplication);
        //调用PeerAwareInstanceRegistryImpl#cancel
        return super.cancel(appName, serverId, isReplication);
    }
    //抛出EurekaInstanceCanceledEvent取消事件
    private void handleCancelation(String appName, String id, boolean isReplication) {
   
   
        log("cancel " + appName + ", serverId " + id + ", isReplication " + isReplication);
        publishEvent(new EurekaInstanceCanceledEvent(this, appName, id, isReplication));
    }

这里比较简单,抛出EurekaInstanceCanceledEvent时间后调用super(PeerAwareInstanceRegistryImpl)的下线方法


    /*
     * (non-Javadoc)
     *
     * @see com.netflix.eureka.registry.InstanceRegistry#cancel(java.lang.String,
     * java.lang.String, long, boolean)
     */
    @Override
    public boolean cancel(final String appName, final String id,
                          final boolean isReplication) {
   
   
         //调用super.cancel下线方法:com.netflix.eureka.registry.AbstractInstanceRegistry#cancel
        if (super.cancel(appName, id, isReplication)) {
   
   
            //复制到其他的Eureka节点
            replicateToPeers(Action.Cancel, appName, id, null, null, isReplication);
            synchronized (lock) {
   
   
                if (this.expectedNumberOfRenewsPerMin > 0) {
   
   
                //如果每分钟的预期续订次数大于0
                //由于客户想取消该阈值,因此降低阈值(1次 30秒,2次 1分钟)
                    // Since the client wants to cancel it, reduce the threshold (1 for 30 seconds, 2 for a minute)
                    //预期续订次数 = 预期续订次数 -2 (2次 1分钟 , 1次 30)
                    this.expectedNumberOfRenewsPerMin = this.expectedNumberOfRenewsPerMin - 2;
                    //续订次数阈值  = 预期续订次数  * 0.85
                    this.numberOfRenewsPerMinThreshold =
                            (int) (this.expectedNumberOfRenewsPerMin * serverConfig.getRenewalPercentThreshold());
                }
            }
            return true;
        }
        return false;
    }

这里继续调用super.cancel(AbstractInstanceRegistry#cancel),完成下线后重新计算了续约阈值,继续看AbstractInstanceRegistry#cancel方法

public abstract class AbstractInstanceRegistry implements InstanceRegistry {
   
   
    /**
        //取消实例注册
        //这通常是由客户端调用时关闭通知服务器从流量中除去实例
     * Cancels the registration of an instance.
     *
     * <p>
     * This is normally invoked by a client when it shuts down informing the
     * server to remove the instance from traffic.
     * </p>
     *
     * @param appName the application name of the application.
     * @param id the unique identifier of the instance.
     * @param isReplication true if this is a replication event from other nodes, false
     *                      otherwise.
     * @return true if the instance was removed from the {@link AbstractInstanceRegistry} successfully, false otherwise.
     */
    @Override
    public boolean cancel(String appName, String id, boolean isReplication) {
   
   
        return internalCancel(appName, id, isReplication);
    }




   /**
   内部取消服务注册
     * {@link #cancel(String, String, boolean)} method is overridden by {@link PeerAwareInstanceRegistry}, so each
     * cancel request is replicated to the peers. This is however not desired for expires which would be counted
     * in the remote peers as valid cancellations, so self preservation mode would not kick-in.
     */
    protected boolean internalCancel(String appName, String id, boolean isReplication) {
   
   
        try {
   
   
        //上锁
            read.lock();
            //服务取消数增加
            CANCEL.increment(isReplication);
            //获取当前提出的服务
            Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
            Lease<InstanceInfo> leaseToCancel = null;
            if (gMap != null) {
   
   
                //从服务注册的map中移除掉当前服务
                leaseToCancel = gMap.remove(id);
            }
            //添加到最近取消队列
            synchronized (recentCanceledQueue) {
   
   
                recentCanceledQueue.add(new Pair<Long, String>(System.currentTimeMillis(), appName + "(" + id + ")"));
            }
            //overriddenInstanceStatusMap 服务状态map中移除当前服务
            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 {
   
   
                //调用Lease.cancel方法

                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();
                }
                //使缓存无效,调用responseCache.invalidate让服务在缓存中失效
                invalidateCache(appName, vip, svip);
                logger.info("Cancelled instance {}/{} (replication={})", appName, id, isReplication);
                return true;
            }
        } finally {
   
   
            read.unlock();
        }
    }

AbstractInstanceRegistry的cancel方法中调用了internalCancel去取消服务的注册,其实在之间分析服务剔除的时候也嗲用这个方法,方法的逻辑大致是:

  • 1.从registry中移除服务,
  • 2.从overriddenInstanceStatusMap状态map中移除服务状态
  • 3.添加到最近取消队列
  • 4.调用Lease.cancel方法,将租约对象中的逐出时间修改为当前时间
  • 5.修改服务的InstanceInfo的状态为DELETE
  • 6.添加到最近修改队列
  • 7.更新服务最后修改时间
  • 8.使ReponseCache缓存无效

总结

我们发现,Eureka Server服务剔除和服务下线的流程后半截很相似,一个是通过定时任务来实现服务端过期的服务的剔除,一个是客户端发送请求到服务端剔除指定的服务,都是走服务下线逻辑
在这里插入图片描述

相关文章
|
3月前
|
存储 数据可视化 Java
基于MicrometerTracing门面和Zipkin实现集成springcloud2023的服务追踪
Sleuth将会停止维护,Sleuth最新版本也只支持springboot2。作为替代可以使用MicrometerTracing在微服务中作为服务追踪的工具。
194 1
|
13天前
|
监控 JavaScript 数据可视化
建筑施工一体化信息管理平台源码,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
智慧工地云平台是专为建筑施工领域打造的一体化信息管理平台,利用大数据、云计算、物联网等技术,实现施工区域各系统数据汇总与可视化管理。平台涵盖人员、设备、物料、环境等关键因素的实时监控与数据分析,提供远程指挥、决策支持等功能,提升工作效率,促进产业信息化发展。系统由PC端、APP移动端及项目、监管、数据屏三大平台组成,支持微服务架构,采用Java、Spring Cloud、Vue等技术开发。
|
2月前
|
JSON Java 测试技术
SpringCloud2023实战之接口服务测试工具SpringBootTest
SpringBootTest同时集成了JUnit Jupiter、AssertJ、Hamcrest测试辅助库,使得更容易编写但愿测试代码。
83 3
|
4月前
|
设计模式 Java 关系型数据库
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码解析
本文是“Java学习路线”专栏的导航文章,目标是为Java初学者和初中高级工程师提供一套完整的Java学习路线。
514 37
|
4月前
|
消息中间件 存储 Java
SpringCloud基础9——服务异步通信-高级篇
消息可靠性、死信交换机、惰性队列、MQ集群
SpringCloud基础9——服务异步通信-高级篇
|
4月前
|
Java API 对象存储
微服务魔法启动!Spring Cloud与Netflix OSS联手,零基础也能创造服务奇迹!
这段内容介绍了如何使用Spring Cloud和Netflix OSS构建微服务架构。首先,基于Spring Boot创建项目并添加Spring Cloud依赖项。接着配置Eureka服务器实现服务发现,然后创建REST控制器作为API入口。为提高服务稳定性,利用Hystrix实现断路器模式。最后,在启动类中启用Eureka客户端功能。此外,还可集成其他Netflix OSS组件以增强系统功能。通过这些步骤,开发者可以更高效地构建稳定且可扩展的微服务系统。
80 1
|
消息中间件 监控 搜索推荐
Spring全家桶--SpringCloud(中级)(三)
Spring全家桶--SpringCloud(中级)(三)
148 0
Spring全家桶--SpringCloud(中级)(三)
|
存储 消息中间件 监控
Spring全家桶--SpringCloud(中级)(二)
Spring全家桶--SpringCloud(中级)(二)
141 0
Spring全家桶--SpringCloud(中级)(二)
|
监控 负载均衡 Java
Spring全家桶--SpringCloud(中级)(一)
Spring全家桶--SpringCloud(中级)(一)
291 0
Spring全家桶--SpringCloud(中级)(一)
|
5天前
|
XML JavaScript Java
SpringBoot集成Shiro权限+Jwt认证
本文主要描述如何快速基于SpringBoot 2.5.X版本集成Shiro+JWT框架,让大家快速实现无状态登陆和接口权限认证主体框架,具体业务细节未实现,大家按照实际项目补充。
38 11