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

简介: EureakClient会定时向EureakServer发送续约心跳(默认30s/次) ,来告诉EurekaServer自己的健康状况,默认情况下3次续约失败(90s),EurekaServer考虑剔除续约失败的客户端服务

什么是服务续约

EureakClient会定时向EureakServer发送续约心跳(默认30s/次) ,来告诉EurekaServer自己的健康状况,默认情况下3次续约失败(90s),EurekaServer考虑剔除续约失败的客户端服务

初始化定时任务

在EuerakClient中依然是通过scheduler定时实现,在com.netflix.discovery.DiscoveryClient#initScheduledTasks中进行初始化,源码如下

/*** Initializes all scheduled tasks.*/privatevoidinitScheduledTasks() {
//如果开启服务注册if (clientConfig.shouldRegisterWithEureka()) {
//续约心跳  30s/次intrenewalIntervalInSecs=instanceInfo.getLeaseInfo().getRenewalIntervalInSecs();
intexpBackOffBound=clientConfig.getHeartbeatExecutorExponentialBackOffBound();
logger.info("Starting heartbeat executor: "+"renew interval is: {}", renewalIntervalInSecs);
// Heartbeat timer//心跳任务scheduler.schedule(
newTimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
newHeartbeatThread()   //HeartbeatThread心跳的线程            ),
renewalIntervalInSecs, TimeUnit.SECONDS);//心跳时间30s/次        ...省略...                    
/**//心跳任务,本身是一个线程对象* The heartbeat task that renews the lease in the given intervals.*/privateclassHeartbeatThreadimplementsRunnable {
publicvoidrun() {
//调用renew()方法续约if (renew()) {
//记录最后续约成功时间lastSuccessfulHeartbeatTimestamp=System.currentTimeMillis();
       }
    }
 }

服务续约线程HeartbeatThread

这里我们看到,通过scheduler定时执行HeartbeatThread线程,在HeartbeatThread中又调用了Discovery.renew()方法执行服务续约,源码如下

/**使用Rest请求像Eureak进行服务续约* Renew with the eureka service by making the appropriate REST call*/booleanrenew() {
//Http相应对象EurekaHttpResponse<InstanceInfo>httpResponse;
try {
//使用eurekaTransport得到http客户端,发起心跳请求httpResponse=eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null);
logger.debug(PREFIX+"{} - Heartbeat status: {}", appPathIdentifier, httpResponse.getStatusCode());
if (httpResponse.getStatusCode() ==404) {
REREGISTER_COUNTER.increment();
logger.info(PREFIX+"{} - Re-registering apps/{}", appPathIdentifier, instanceInfo.getAppName());
longtimestamp=instanceInfo.setIsDirtyWithTime();
//如果续约返回404,尝试重新发起服务注册,并设置Dirtybooleansuccess=register();
if (success) {
instanceInfo.unsetIsDirty(timestamp);
            }
returnsuccess;
        }
returnhttpResponse.getStatusCode() ==200;
    } catch (Throwablee) {
logger.error(PREFIX+"{} - was unable to send heartbeat!", appPathIdentifier, e);
returnfalse;
    }
}

这里依然使用的是eurekaTransport得到一个EurekaHttpClient实例,然后通过EurekaHttpClientDecorator装饰器执行sendHeartBeat方法,源码如下

publicabstractclassEurekaHttpClientDecoratorimplementsEurekaHttpClient {
    ...省略...
//发送心跳@OverridepublicEurekaHttpResponse<InstanceInfo>sendHeartBeat(finalStringappName, //服务名finalStringid,//服务IDfinalInstanceInfoinfo,//服务注册对象finalInstanceStatusoverriddenStatus) {  //服务注册状态returnexecute(newRequestExecutor<InstanceInfo>() {
@OverridepublicEurekaHttpResponse<InstanceInfo>execute(EurekaHttpClientdelegate) {
//发送心跳returndelegate.sendHeartBeat(appName, id, info, overriddenStatus);
            }
@OverridepublicRequestTypegetRequestType() {
returnRequestType.SendHeartBeat;
            }
        });
    }

通过EurekaHttpClientDecorator 装饰器会先后会执行RetryableEurekaHttpClient(失败重试),RedirectingEurekaHttpClient(重定向到不同的EurekaServer)MetricsCollectingEurekaHttpClient(统计执行情况)针对于各种情况的Http客户端,然后

最终通过AbstractJerseyEurekaHttpClient(JerseyApplicationClient),使用jerseyClient发起心跳请求

publicabstractclassAbstractJerseyEurekaHttpClientimplementsEurekaHttpClient {
@OverridepublicEurekaHttpResponse<InstanceInfo>sendHeartBeat(StringappName, Stringid, InstanceInfoinfo, InstanceStatusoverriddenStatus) {
StringurlPath="apps/"+appName+'/'+id;
ClientResponseresponse=null;
try {
//webResource是对Web请求的封装WebResourcewebResource=jerseyClient.resource(serviceUrl)
                .path(urlPath)
//服务状态 UP                .queryParam("status", info.getStatus().toString())
//最后刷新时间                .queryParam("lastDirtyTimestamp", info.getLastDirtyTimestamp().toString());
if (overriddenStatus!=null) {
webResource=webResource.queryParam("overriddenstatus", overriddenStatus.name());
            }
BuilderrequestBuilder=webResource.getRequestBuilder();
addExtraHeaders(requestBuilder);
//发送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));
            }
returneurekaResponseBuilder.build();
        } finally {
if (logger.isDebugEnabled()) {
logger.debug("Jersey HTTP PUT {}/{}; statusCode={}", serviceUrl, urlPath, response==null?"N/A" : response.getStatus());
            }
if (response!=null) {
response.close();
            }
        }
    }

这里EureakClient会向EureakServer发送自己的服务状态,以及服务最后续约时间,使用的是PUT发起Rest请求。

总结

  1. DiscoveryClient.initScheduledTasks初始化续约心跳定时任务,30s/次执行HeartbeatThread线程发送续约请求
  2. HeartbeatThread调用DiscoveryClient.renew()续约
  3. renew方法中使用eurekaTransport得到EureakaHttpClient实例EurekaHttpClientDecorator 装饰器执行请求
  4. EurekaHttpClientDecorator 调用JerseyApplicationClient向EureakServer发送Rest请求,把服务状态和最后的续约时间当做参数
目录
相关文章
|
3月前
|
负载均衡 算法 Java
【SpringCloud(4)】OpenFeign客户端:OpenFeign服务绑定;调用服务接口;Feign和OpenFeign
Feign是一个WebService客户端。使用Feign能让编写WebService客户端更加简单。 它的使用方法是定义一个服务接口然后再上面添加注解。Feign也支持可拔插式的编码器和解码器。SpringCloud对Feign进行了封装,十七支持了SpringMVC标准注解和HttpMessageConverters。 Feign可用于Eureka和Ribbon组合使用以支持负载均衡
727 138
|
人工智能 Java Serverless
【MCP教程系列】搭建基于 Spring AI 的 SSE 模式 MCP 服务并自定义部署至阿里云百炼
本文详细介绍了如何基于Spring AI搭建支持SSE模式的MCP服务,并成功集成至阿里云百炼大模型平台。通过四个步骤实现从零到Agent的构建,包括项目创建、工具开发、服务测试与部署。文章还提供了具体代码示例和操作截图,帮助读者快速上手。最终,将自定义SSE MCP服务集成到百炼平台,完成智能体应用的创建与测试。适合希望了解SSE实时交互及大模型集成的开发者参考。
13173 60
|
5月前
|
监控 Java API
Spring Boot 3.2 结合 Spring Cloud 微服务架构实操指南 现代分布式应用系统构建实战教程
Spring Boot 3.2 + Spring Cloud 2023.0 微服务架构实践摘要 本文基于Spring Boot 3.2.5和Spring Cloud 2023.0.1最新稳定版本,演示现代微服务架构的构建过程。主要内容包括: 技术栈选择:采用Spring Cloud Netflix Eureka 4.1.0作为服务注册中心,Resilience4j 2.1.0替代Hystrix实现熔断机制,配合OpenFeign和Gateway等组件。 核心实操步骤: 搭建Eureka注册中心服务 构建商品
926 3
|
3月前
|
负载均衡 Java API
《深入理解Spring》Spring Cloud 构建分布式系统的微服务全家桶
Spring Cloud为微服务架构提供一站式解决方案,涵盖服务注册、配置管理、负载均衡、熔断限流等核心功能,助力开发者构建高可用、易扩展的分布式系统,并持续向云原生演进。
|
5月前
|
设计模式 Java 开发者
如何快速上手【Spring AOP】?从动态代理到源码剖析(下篇)
Spring AOP的实现本质上依赖于代理模式这一经典设计模式。代理模式通过引入代理对象作为目标对象的中间层,实现了对目标对象访问的控制与增强,其核心价值在于解耦核心业务逻辑与横切关注点。在框架设计中,这种模式广泛用于实现功能扩展(如远程调用、延迟加载)、行为拦截(如权限校验、异常处理)等场景,为系统提供了更高的灵活性和可维护性。
|
9月前
|
前端开发 Java 物联网
智慧班牌源码,采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署
智慧班牌系统是一款基于信息化与物联网技术的校园管理工具,集成电子屏显示、人脸识别及数据交互功能,实现班级信息展示、智能考勤与家校互通。系统采用Java + Spring Boot后端框架,搭配Vue2前端技术,支持SaaS云部署与私有化定制。核心功能涵盖信息发布、考勤管理、教务处理及数据分析,助力校园文化建设与教学优化。其综合性和可扩展性有效打破数据孤岛,提升交互体验并降低管理成本,适用于日常教学、考试管理和应急场景,为智慧校园建设提供全面解决方案。
559 70
|
6月前
|
Prometheus 监控 Cloud Native
Docker 部署 Prometheus 和 Grafana 监控 Spring Boot 服务
Docker 部署 Prometheus 和 Grafana 监控 Spring Boot 服务实现步骤
640 0

热门文章

最新文章