Spring Cloud源码分析之Eureka篇第七章:续约_eureka续约源码分析-程序员宅基地

技术标签: springcloud  spring cloud  Spring Cloud深入了解  

在文章《Spring Cloud源码分析之Eureka篇第四章:服务注册是如何发起的 》的分析中,我们知道了作为Eureka Client的应用启动时,在com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法中,会做以下几件事:

  1. 周期性更新服务列表;
  2. 周期性服务续约;
  3. 服务注册逻辑;

本章学习的是周期性服务续约的相关代码,对应用如何将自身信息注册到Eureka进行深入了解,原文地址:https://blog.csdn.net/boling_cavalry/article/details/82861618

概览

以下图片来自Netflix官方,图中显示Eureka Client会发起Renew向注册中心做周期性续约,这样其他Eureka client通过Get Registry请求就能获取到新注册应用的相关信息:
在这里插入图片描述

关于源码版本

本次分析的Spring Cloud版本为Edgware.RELEASE,对应的eureka-client版本为1.7.0;

来自官方文档的指导信息

最准确的说明信息来自Netflix的官方文档,地址:https://github.com/Netflix/eureka/wiki/Understanding-eureka-client-server-communication#renew
学习源码之前先看文档可以确定大方向,不会因为陷入源码细节导致偏离学习目标,如下图所示:
在这里插入图片描述

我的理解:

  1. Eureka client每隔三十秒发送一次心跳到Eureka server,这就是续约;
  2. Eureka client续约的目的是告诉Eureka server自己还活着;
  3. Eureka server若90秒内未收到心跳,就从自己的服务列表中剔除该Eureka client;
  4. 建议不要改变心跳间隔,因为Eureka server是通过心跳来判断Eureka client是否正常;

源码分析

  1. 首先回顾com.netflix.discovery.DiscoveryClient类的initScheduledTasks方法,Eureka client在启动的时侯都会执行此方法,如下方所示,已经略去了周期性更新服务列表相关的代码:
private void initScheduledTasks() {
    
        //略去获取服务列表相关代码
        ...

        if (clientConfig.shouldRegisterWithEureka()) {
    
            //心跳间隔
            int renewalIntervalInSecs = instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
            //周期性任务执行超时的时候,会将超时时间翻倍,但不会超过最大时间,这个最大时间是由expBackOffBound决定的
            int expBackOffBound = clientConfig.getHeartbeatExecutorExponentialBackOffBound();
            logger.info("Starting heartbeat executor: " + "renew interval is: " + renewalIntervalInSecs);

            //启动心跳的周期性任务
            scheduler.schedule(
                    new TimedSupervisorTask(
                            "heartbeat",
                            scheduler,
                            heartbeatExecutor,
                            renewalIntervalInSecs,
                            TimeUnit.SECONDS,
                            expBackOffBound,
                            new HeartbeatThread()
                    ),
                    renewalIntervalInSecs, TimeUnit.SECONDS);
                    
        //略去服务注册相关代码
        ...
  1. 上述代码可以看出,续租逻辑在HeartbeatThread实例中,交给TimedSupervisorTask实例进行周期性调用,有关TimedSupervisorTask的功能和细节,请参考《Eureka的TimedSupervisorTask类(自动调节间隔的周期性任务)》

  2. HeartbeatThread类中,通过调用renew方法实现续租,如下代码所示,方法注释已说明是Restfult请求来实现的,对应Eureka server的返回信息httpResponse,除了检查返回码是否等于200就没有任何作用了,想想也是如此,30秒一次的心跳,不论是请求还是响应都应该尽量简洁,降低服务器和网络的压力:

/**
     * Renew with the eureka service by making the appropriate REST call
     */
    boolean renew() {
    
        EurekaHttpResponse<InstanceInfo> httpResponse;
        try {
    
            //发Restful请求,即心跳
            httpResponse = eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
            logger.debug("{} - Heartbeat status: {}", PREFIX + appPathIdentifier, httpResponse.getStatusCode());
            //404错误会触发注册逻辑
            if (httpResponse.getStatusCode() == 404) {
    
                REREGISTER_COUNTER.increment();
                logger.info("{} - Re-registering apps/{}", PREFIX + appPathIdentifier, instanceInfo.getAppName());
                return register();
            }
            //返回码200表示心跳成功
            return httpResponse.getStatusCode() == 200;
        } catch (Throwable e) {
    
            logger.error("{} - was unable to send heartbeat!", PREFIX + appPathIdentifier, e);
            return false;
        }
    }
  1. 继续展开上面代码段中的 eurekaTransport.registrationClient.sendHeartBeat方法,源码在EurekaHttpClientDecorator类中:
@Override
    public EurekaHttpResponse<InstanceInfo> sendHeartBeat(final String appName,
                                                          final String id,
                                                          final InstanceInfo info,
                                                          final InstanceStatus overriddenStatus) {
    
        return execute(new RequestExecutor<InstanceInfo>() {
    
            @Override
            public EurekaHttpResponse<InstanceInfo> execute(EurekaHttpClient delegate) {
    
                //网络处理委托给代理类完成
                return delegate.sendHeartBeat(appName, id, info, overriddenStatus);
            }

            @Override
            public RequestType getRequestType() {
    
                //请求类型为心跳
                return RequestType.SendHeartBeat;
            }
        });
    }
  1. 继续展开delegate.sendHeartBeat,多层调用一路展开,最终由JerseyApplicationClient类来完成操作,对应源码在父类AbstractJerseyEurekaHttpClient中,如下所示,主要工作是利用jersey库的Restful Api将自身的信息PUT到Eureka server,注意:这里不是POST,也不是GET,而是PUT:
    @Override
    public EurekaHttpResponse<InstanceInfo> sendHeartBeat(String appName, String id, InstanceInfo info, InstanceStatus overriddenStatus) {
    
        String urlPath = "apps/" + appName + '/' + id;
        ClientResponse response = null;
        try {
    
            //请求参数有两个:Eureka client自身状态、自身关键信息(状态、元数据等)最后一次变化的时间
            WebResource webResource = jerseyClient.resource(serviceUrl)
                    .path(urlPath)
                    .queryParam("status", info.getStatus().toString())
                    .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());
            if (overriddenStatus != null) {
    
                webResource = webResource.queryParam("overriddenstatus", overriddenStatus.name());
            }
            Builder requestBuilder = webResource.getRequestBuilder();
            addExtraHeaders(requestBuilder);
            //注意:这里不是POST,也不是GET,而是PUT
            response = requestBuilder.put(ClientResponse.class);
            EurekaHttpResponseBuilder<InstanceInfo> eurekaResponseBuilder = anEurekaHttpResponse(response.getStatus(), InstanceInfo.class).headers(headersOf(response));
            if (response.hasEntity()) {
    
                eurekaResponseBuilder.entity(response.getEntity(InstanceInfo.class));
            }
            return eurekaResponseBuilder.build();
        } finally {
    
            if (logger.isDebugEnabled()) {
    
                logger.debug("Jersey HTTP PUT {}/{}; statusCode={}", serviceUrl, urlPath, response == null ? "N/A" : response.getStatus());
            }
            if (response != null) {
    
                response.close();
            }
        }
    }

至此,Eureka client向服务续租的源码就分析完毕了,过程相对简单,DiscoveryClient、TimedSupervisorTask、JerseyApplicationClient等实例各司其职,定时发送PUT请求到Eureka server;

欢迎关注我的公众号:程序员欣宸

在这里插入图片描述

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/boling_cavalry/article/details/82915355

智能推荐

9个做短视频自媒体的神器,抓紧收藏,新手小白一天轻松200多_九天自媒体神器-程序员宅基地

文章浏览阅读844次。今天大周给大家分享几个短视频自媒体神器,用好了可以每天轻松200多,话不多说,直接上干货。一、剪映一款新手入门的剪辑神器,界面简洁,一学就会,能满足你的各种剪辑要求。二、西瓜视频在西瓜视频上发布内容,有播放就有收益,播放单价高,没有新手期,注册账号完成实名后可以直接发布内容。三、水印宝一款能去除视频水印的神器,腾讯视频、爱奇艺、优酷等软件下载的视频是有水印的,创作时不去除是会对作品有影响的,用软件直接去除就可以了。四、bigshot可以帮你制作一个炫酷的片头吸引粉丝用户的眼球,还有各种海量的_九天自媒体神器

HiFi-GAN: Generative Adversarial Networks for Efficient and High Fidelity Speech Synthesis-程序员宅基地

文章浏览阅读1.4k次。会议:2020 NIPS单位:韩国KAKAO作者:Jungil Kong, Jaehyeon Kim文章主页开源代码文章目录abstractHiFi-GAN生成器结构MSD: multi-scale discriminatorMPD:multi-period discriminator目标函数experimentsablation studyunseen speaker的泛化abstractmotivation:在推理时间 & 生成高保真音质方面均作出改进观点:modeling _hifi-gan: generative adversarial networks for efficient and high fidelity sp

用栈实现回文字符串的判断_用栈判断字符串是否为回文-程序员宅基地

文章浏览阅读1.8w次,点赞48次,收藏185次。用栈实现回文字符串的判断栈是一种后进先出的数据结构,它只能在一段进行插入和删除操作。例如一个字符串"12321",像这种 ,无论正读反读均相同的字符序列,就叫做回文字符串。首先,我们需要读取这行字符串,并求出字符串的长度。 char a[101]; int len; gets(a); len=strlen(a);如果一个字符串是回文的话,那么它必须是中间对称的,我们需要求中点,即: mid=len/2-1;接下来将会涉及到栈的应用;我们先将mid之前的字符全部入栈。因为这里的栈是用_用栈判断字符串是否为回文

Java基础面试题整理-程序员宅基地

文章浏览阅读1.9k次,点赞2次,收藏7次。1.说下volatile吧,了解多少说多少?volatile是JVM(Java Memory Model)中用于保证可见性和有序性的轻量级同步机制。它主要是有两个作用,一是保证被修饰共享变量的可见性,也就是多个线程操作读写时,能被其他线程感知到,在读取变量时会强制将主内存中的变量值读取到自己的工作内存中,写入变量时又会强制自己的新值刷新回主内存;另外一个重要作用在于阻止指令重排序。我们所熟知的双检测单例中,instance必须要用volatile修饰,原因是new SingleTon时,一般说有三个步骤

[转]Exchange Server 2013部署系列之一:部署环境介绍-程序员宅基地

文章浏览阅读71次。Exchange Server 2013部署系列之一:部署环境介绍源地址:http://543925535.blog.51cto.com/639838/d-6==========================================================================Exchange Server 2013的最低部署要求的Windows Serve...

LTE信道及协议栈分析-程序员宅基地

文章浏览阅读418次,点赞4次,收藏7次。无线帧结构、LTE信道及协议栈分析(物理层)、PCM的主要步骤

随便推点

httos双向认证配置_NGINX 配置本地HTTPS(双向认证)-程序员宅基地

文章浏览阅读1.8k次。一、SSL协议加密方式SSL协议即用到了对称加密也用到了非对称加密(公钥加密),在建立传输链路时,SSL首先对对称加密的密钥使用公钥进行非对称加密,链路建立好之后,SSL对传输内容使用对称加密。1.对称加密速度高,可加密内容较大,用来加密会话过程中的消息。2.公钥加密加密速度较慢,但能提供更好的身份认证技术,用来加密对称加密的密钥。单向认证Https在建立Socket连接之前,需要进行握手,具体过..._httos″81278 ″osh

通过Zadig给蓝牙适配器安装驱动后,无法恢复的解决办法_csr8510 a10蓝牙驱动-程序员宅基地

文章浏览阅读3.2k次,点赞2次,收藏4次。通过Zadig给蓝牙适配器安装驱动后,无法恢复的解决办法问题描述蓝牙适配器型号是CSR 8510 A10, 在WIN10里可识别为蓝牙适配器。用Zadig给蓝牙适配器安装WinUSB驱动后,蓝牙适配器无法被Win10正常识别,回退驱动是灰色,删除卸载设备和驱动、重启电脑都没有用。解决办法进入设备管理器找到通用串行总线设备里的CSR8510 A10(如果不在这里你就一个个找csr开头的设备,然后通过蓝牙适配器的插拔,定位到是不是你选的驱动)右击选择更新驱动程序选择浏览我的计_csr8510 a10蓝牙驱动

OpenStreetMap初探(六)——获取地图数据_open street map 没有建筑层数-程序员宅基地

文章浏览阅读2.5k次。http://blog.csdn.net/scy411082514/article/details/7543929OSM作为开源地图,原始地图数据是完全开发的(并且更新速度很快),获取地图数据的方法可非常多。1、openstreetmap.org 网站本身提供地图数据的下载服务(范围不能过大),使用也很方便,点击地图上端的“输出”,然后选择范围和输出格式即可。2、Kos_open street map 没有建筑层数

微博爬虫及简单数据分析_python 微博数据分析-程序员宅基地

文章浏览阅读2.8k次,点赞20次,收藏63次。刚开始学python,选了这个题目,把代码放上来留念,没有用到很流行的框架,所以代码量挺大GUI用wxpython写的# _*_ coding: UTF-8 _*_import osimport reimport requestsimport sysimport wximport tracebackfrom datetime import datetimefrom dateti..._python 微博数据分析

多目标排序在快手短视频推荐中的实践-程序员宅基地

文章浏览阅读3.5k次,点赞2次,收藏12次。分享嘉宾:郑东博士快手 推荐算法技术总监编辑整理:于洋出品平台:DataFunTalk导读:快手是中国领先的短视频和直播社区,拥有超过3亿的DAU和丰富的社交数据。快手秉承的价值观是真实..._快手 融合公式

由磁场数据和加速度数据计算初始姿态角_ros2 角速度和加速度计算姿态角-程序员宅基地

文章浏览阅读8k次,点赞17次,收藏77次。由磁场数据和加速度数据计算初始姿态角_ros2 角速度和加速度计算姿态角

推荐文章

热门文章

相关标签