服务续约执行简要流程图

  下面这张图大致描述了服务续约从Client端到Server端的大致流程,详情如下:


服务续约Client源码分析

  我们先来看看服务续约定时任务的初始化。那我们的服务续约定时任务什么时候会被初始化呢,那肯定是我们启用我们Eureka Client的时候,当我们启动Client时,Eureka会先处理相关的配置,然后初始化我们Client的相关信息,我们的定时任务也就是此时进行的初始化,具体来说我们的服务续约定时任务就是在DiscoveryClient这个类中initScheduledTasks方法中被初始化的。代码如下:

private final ScheduledExecutorService scheduler;
private void initScheduledTasks() {
…省略其他代码
// 初始化定时服务续约任务
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);
…省略其他代码
}

  由此可见,我们的定时任务其实是Client进行初始化完成的,并且还是使用ScheduledExecutorService线程池来完成我们的定时任务。接下来我们看看HeartbeatThread类相关的源码:

private class HeartbeatThread implements Runnable {
public void run() {
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}

  HeartbeatThread类中run方法调用了renew方法,我们接着看下去:

/**
* Renew with the eureka service by making the appropriate REST call
*/
boolean renew() {
EurekaHttpResponse<InstanceInfo> httpResponse;
try {
// 调用Eureka Server提供的服务续约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());
long timestamp = instanceInfo.setIsDirtyWithTime();
boolean success = register();
if (success) {
instanceInfo.unsetIsDirty(timestamp);
}
return success;
}
return httpResponse.getStatusCode() == 200;
} catch (Throwable e) {
logger.error(PREFIX + "{} - was unable to send heartbeat!", appPathIdentifier, e);
return false;
}
}

   通过eurekaTransport.registrationClient.sendHeartBeat(instanceInfo.getAppName(), instanceInfo.getId(), instanceInfo, null) 这行代码,我们则可以知道Client端的续约其实就是用调用Server提供的接口,然后Server端实现了服务续约的操作。


服务续约Server源码实现

  由上面可见,我们Client的续约操作是直接调用的Server端提供的接口,以此来实现的具体操作,那我们接下来就看看Server是如何进行服务续约操作的,源码如下:

@PUT
public Response renewLease(
@HeaderParam(PeerEurekaNode.HEADER_REPLICATION) String isReplication,
@QueryParam("overriddenstatus") String overriddenStatus,
@QueryParam("status") String status,
@QueryParam("lastDirtyTimestamp") String lastDirtyTimestamp) {
boolean isFromReplicaNode = "true".equals(isReplication);
// 进行服务续约操作
boolean isSuccess = registry.renew(app.getName(), id, isFromReplicaNode); // Not found in the registry, immediately ask for a register
if (!isSuccess) {
logger.warn("Not Found (Renew): {} - {}", app.getName(), id);
return Response.status(Status.NOT_FOUND).build();
}
// Check if we need to sync based on dirty time stamp, the client
// instance might have changed some value
Response response = null;
if (lastDirtyTimestamp != null && serverConfig.shouldSyncWhenTimestampDiffers()) {
response = this.validateDirtyTimestamp(Long.valueOf(lastDirtyTimestamp), isFromReplicaNode);
// Store the overridden status since the validation found out the node that replicates wins
if (response.getStatus() == Response.Status.NOT_FOUND.getStatusCode()
&& (overriddenStatus != null)
&& !(InstanceStatus.UNKNOWN.name().equals(overriddenStatus))
&& isFromReplicaNode) {
registry.storeOverriddenStatusIfRequired(app.getAppName(), id, InstanceStatus.valueOf(overriddenStatus));
}
} else {
response = Response.ok().build();
}
logger.debug("Found (Renew): {} - {}; reply status={}", app.getName(), id, response.getStatus());
return response;
}

  由registry.renew(app.getName(), id, isFromReplicaNode);这里我们可以看到调用了renew()方法来实现续约操作,那我们就看下这个方法的实现:

Override
public boolean renew(final String appName, final String serverId,
boolean isReplication) {
log("renew " + appName + " serverId " + serverId + ", isReplication {}"
+ isReplication);
List<Application> applications = getSortedApplications();
for (Application input : applications) {
if (input.getName().equals(appName)) {
InstanceInfo instance = null;
for (InstanceInfo info : input.getInstances()) {
if (info.getId().equals(serverId)) {
instance = info;
break;
}
}
publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId,
instance, isReplication));
break;
}
}
return super.renew(appName, serverId, isReplication);
}

  我们可以看到方法最后调用了父类的renew()方法,我们继续看下去:

public boolean renew(final String appName, final String id, final boolean isReplication) {
if (super.renew(appName, id, isReplication)) {
// 向其他server发送服务续约消息
replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication);
return true;
}
return false;
}
 public boolean renew(String appName, String id, boolean isReplication) {
RENEW.increment(isReplication);
Map<String, Lease<InstanceInfo>> gMap = registry.get(appName);
Lease<InstanceInfo> leaseToRenew = null;
if (gMap != null) {
leaseToRenew = gMap.get(id);
}
if (leaseToRenew == null) {
RENEW_NOT_FOUND.increment(isReplication);
logger.warn("DS: Registry: lease doesn't exist, registering resource: {} - {}", appName, id);
return false;
} else {
InstanceInfo instanceInfo = leaseToRenew.getHolder();
if (instanceInfo != null) {
// touchASGCache(instanceInfo.getASGName());
InstanceStatus overriddenInstanceStatus = this.getOverriddenInstanceStatus(
instanceInfo, leaseToRenew, isReplication);
if (overriddenInstanceStatus == InstanceStatus.UNKNOWN) {
logger.info("Instance status UNKNOWN possibly due to deleted override for instance {}"
+ "; re-register required", instanceInfo.getId());
RENEW_NOT_FOUND.increment(isReplication);
return false;
}
if (!instanceInfo.getStatus().equals(overriddenInstanceStatus)) {
logger.info(
"The instance status {} is different from overridden instance status {} for instance {}. "
+ "Hence setting the status to overridden status", instanceInfo.getStatus().name(),
instanceInfo.getOverriddenStatus().name(),
instanceInfo.getId());
instanceInfo.setStatusWithoutDirty(overriddenInstanceStatus); }
}
renewsLastMin.increment();
leaseToRenew.renew();
return true;
}
}
public void renew() {
// 更新上次更新时间
lastUpdateTimestamp = System.currentTimeMillis() + duration;
}

  由上面的代码可见,Eureka Server服务续约的流程可大致分为以下几步:

    1.更新实例状态

    2.更新实例上次更新时间

    3.向其他Server发送服务续约消息

Eureka系列(五) 服务续约流程具体实现的更多相关文章

  1. Eureka服务续约(Renew)源码分析

    主要对Eureka的Renew(服务续约),从服务提供者发起续约请求开始分析,通过阅读源码和画时序图的方式,展示Eureka服务续约的整个生命周期.服务续约主要是把服务续约的信息更新到自身的Eurek ...

  2. 【一起学源码-微服务】Nexflix Eureka 源码九:服务续约源码分析

    前言 前情回顾 上一讲 我们讲解了服务发现的相关逻辑,所谓服务发现 其实就是注册表抓取,服务实例默认每隔30s去注册中心抓取一下注册表增量数据,然后合并本地注册表数据,最后有个hash对比的操作. 本 ...

  3. Eureka系列(八)服务剔除具体实现

    服务下线的大致流程图   下面这张图很简单地描述了服务剔除的大致流程: 服务剔除实现源码分析   首先我们得了解下服务剔除这个定时任务是什么被初始化启动的,在百度搜索中,在我们Eureka Serve ...

  4. Eureka系列(七) 服务下线Server端具体实现

    服务下线的大致流程图   下面这张图很简单地描述了Server端服务下线的大致流程: 服务下线Server端实现源码分析   Eureka服务实现是通过Server端InstanceResource ...

  5. eureka源码--服务的注册、服务续约、服务发现、服务下线、服务剔除、定时任务以及自定义注册中心的思路

    微服务注册后,在注册中心的注册表结构是一个map: ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>& ...

  6. Eureka系列(二) 服务注册Server端具体实现

    服务注册 Server端流程   我们先看下面这张图片,这张图片简单描述了下我们EurekaClient 在调用EurekaServer 提供的服务注册Http接口,Server端实现接口执行的大致流 ...

  7. eureka client服务续约源码分析

    必备知识: 1.定时任务 ScheduledExecutorService public class demo { public static void main(String[] args){ Sc ...

  8. Eureka 系列(08)心跳续约与自动过期

    Eureka 系列(08)心跳续约与自动过期 [TOC] Spring Cloud 系列目录 - Eureka 篇 在上一篇 Eureka 系列(07)服务注册与主动下线 中对服务的注册与下线进行了分 ...

  9. Eureka系列(三)获取服务Client端具体实现

    获取服务Client 端流程   我们先看下面这张图片,这张图片简单描述了下我们Client是如何获取到Server已续约实例信息的流程:  从图片中我们可以知晓大致流程就是Client会自己开启一个 ...

随机推荐

  1. 【老孟Flutter】自定义文本步进组件

    交流 老孟Flutter博客(330个控件用法+实战入门系列文章):http://laomengit.com 欢迎加入Flutter交流群(微信:laomengit).关注公众号[老孟Flutter] ...

  2. MindManager主题标记功能怎么使用

    我们在使用MindManager制作思维导图的过程中,经常需要对主题的类别,优先程度等进行整理,毫无疑问,这是一项繁琐却又不得不做的工作.今天小编为大家带来了MindManager主题整理的一些小技巧 ...

  3. 【C++】数组中的第k个最小元素

    分治思想求解的问题,但是比较特殊,只有分解问题和求解小问题,不需要合并 每次也只需要经过判断,分解一半,所以比其他分解两边的效率高 最坏情况时间复杂度为O(n^2),期望时间复杂度为O(n) 找基准值 ...

  4. 将input 的文本框改为不可编辑状态

    <input type="text" id = "textid" name="名称" value="值" size ...

  5. 太干了!一张图整理了 Python 所有内置异常

    在编写程序时,可能会经常报出一些异常,很大一方面原因是自己的疏忽大意导致程序给出错误信息,另一方面是因为有些异常是程序运行时不可避免的,比如在爬虫时可能有几个网页的结构不一致,这时两种结构的网页用同一 ...

  6. chrome浏览器查看当前页面cookie

    方法一:点进去设置--高级--网站设置--权限cookie--查找所有cookie和网站数据,就可以看到所有的cookie信息了,举例: 方法二:键盘F12,找到network--点击Doc(如果没有 ...

  7. 2020.10.17 JZOJ 提高B组T2 导弹拦截

    2020.10.17 JZOJ 提高B组T2 导弹拦截 题目 Description 某国为了防御敌国的导弹袭击,发展出一种导弹拦截系统. 敌国的导弹形成了立体打击,每个导弹可以抽象成一个三维空间中的 ...

  8. 什么是SSL双向认证,与单向认证证书有什么区别?

    SSL/TLS证书是用于用户浏览器和网站服务器之间的数据传输加密,实现互联网传输安全保护,大多数情况下指的是服务器证书.服务器证书是用于向浏览器客户端验证服务器,这种是属于单向认证的SSL证书.但是, ...

  9. `prometheus-net.DotNetRuntime` 获取 CLR 运行指标原理解析

    prometheus-net.DotNetRuntime 介绍 Intro 前面集成 Prometheus 的文章中简单提到过,prometheus-net.DotNetRuntime 可以获取到一些 ...

  10. C#数据结构-二叉树-链式存储结构

    对比上一篇文章"顺序存储二叉树",链式存储二叉树的优点是节省空间. 二叉树的性质: 1.在二叉树的第i层上至多有2i-1个节点(i>=1). 2.深度为k的二叉树至多有2k- ...