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

Renew(服务续约)操作由Service Provider定期调用,类似于heartbeat。目的是隔一段时间Service Provider调用接口,告诉Eureka Server它还活着没挂,不要把它T了。通俗的说就是它们两之间的心跳检测,避免服务提供者被剔除掉。

Renew源码分析

服务提供者实现细节

服务提供者发发起服务续约的时序图

在com.netflix.discovery.DiscoveryClient.initScheduledTasks()中的1272行,TimedSupervisorTask会定时发起服务续约,代码如下所示:

// Heartbeat timer
scheduler.schedule(
new TimedSupervisorTask(
"heartbeat",
scheduler,
heartbeatExecutor,
renewalIntervalInSecs,
TimeUnit.SECONDS,
expBackOffBound,
new HeartbeatThread()
),
renewalIntervalInSecs, TimeUnit.SECONDS);

2.在com.netflix.discovery.DiscoveryClient中的1393行,有一个HeartbeatThread线程发起续约操作

private class HeartbeatThread implements Runnable {
public void run() {
//调用eureka-client中的renew
if (renew()) {
lastSuccessfulHeartbeatTimestamp = System.currentTimeMillis();
}
}
}

renew()调用eureka-client-1.4.11.jarcom.netflix.discovery.DiscoveryClient中829行renew()发起PUT Reset请求,

调用com.netflix.eureka.resources.InstanceResource中的renewLease()续约。

Netflix中的Eureka Core实现细节

NetFlix中Eureka Core中的服务续约时序图,如下图所示。

打开com.netflix.eureka.resources.InstanceResource中的106行的renewLease()方法,代码如下:

private final PeerAwareInstanceRegistry registry
@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);
//其余省略
}

点开registry.renew(app.getName(), id, isFromReplicaNode);我们可以看到,调用了org.springframework.cloud.netflix.eureka.server.InstanceRegistry中的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.getHostName().equals(serverId)) {
instance = info;
break;
}
}
publishEvent(new EurekaInstanceRenewedEvent(this, appName, serverId,
instance, isReplication));
break;
}
}
//调用com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl中的renew方法
return super.renew(appName, serverId, isReplication);
}

super.renew()看到调用了父类中的com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl420行的renew()方法

public boolean renew(final String appName, final String id, final boolean isReplication) {
//服务续约成功,
if (super.renew(appName, id, isReplication)) {
//然后replicateToPeers同步其它Eureka Server中的数据
replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication);
return true;
}
return false;
}

从上面代码中super.renew(appName, id, isReplication)可以看出调用的是com.netflix.eureka.registry.AbstractInstanceRegistry中345行的renew()方法,

    /**
* Marks the given instance of the given app name as renewed, and also marks whether it originated from
* replication.
*
* @see com.netflix.eureka.lease.LeaseManager#renew(java.lang.String, java.lang.String, boolean)
*/
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)) {
Object[] args = {
instanceInfo.getStatus().name(),
instanceInfo.getOverriddenStatus().name(),
instanceInfo.getId()
};
logger.info(
"The instance status {} is different from overridden instance status {} for instance {}. "
+ "Hence setting the status to overridden status", args);
instanceInfo.setStatus(overriddenInstanceStatus);
}
}
renewsLastMin.increment();
leaseToRenew.renew();
return true;
}
}

其中 leaseToRenew.renew()是调用com.netflix.eureka.lease.Lease中的62行的renew()方法

public void renew() {
lastUpdateTimestamp = System.currentTimeMillis() + duration;
}

replicateToPeers(Action.Heartbeat, appName, id, null, null, isReplication);

调用自身的replicateToPeers()方法,在com.netflix.eureka.registry.PeerAwareInstanceRegistryImpl中的618行,主要接口实现方式和register基本一致:首先更新自身Eureka Server中服务的状态,再同步到其它Eureka Server中。

private void replicateToPeers(Action action, String appName, String id,
InstanceInfo info /* optional */,
InstanceStatus newStatus /* optional */, boolean isReplication) {
Stopwatch tracer = action.getTimer().start();
try {
if (isReplication) {
numberOfReplicationsLastMin.increment();
}
// If it is a replication already, do not replicate again as this will create a poison replication
if (peerEurekaNodes == Collections.EMPTY_LIST || isReplication) {
return;
}
// 同步把续约信息同步到其它的Eureka Server中
for (final PeerEurekaNode node : peerEurekaNodes.getPeerEurekaNodes()) {
// If the url represents this host, do not replicate to yourself.
if (peerEurekaNodes.isThisMyUrl(node.getServiceUrl())) {
continue;
}
//根据action做相应操作的同步
replicateInstanceActionsToPeers(action, appName, id, info, newStatus, node);
}
} finally {
tracer.stop();
}
}

最新文章

  1. ASM FailGroup验证
  2. .VDI manual Technical Logistics - Volume 2: Industrial Trucks
  3. 微信小程序 教程及示例
  4. Android自学笔记:环境配置
  5. 【ASP.NET】TreeView控件学习
  6. 【HDOJ】1225 Football Score
  7. python scrapy 基础
  8. cocostudio中button
  9. node.js零基础详细教程(7.5):mongo可视化工具webstorm插件、nodejs自动重启模块Node Supervisor(修改nodejs后不用再手动命令行启动服务了)
  10. bzoj 4596 [Shoi2016]黑暗前的幻想乡 矩阵树定理+容斥
  11. MT4用EA测试历史数据时日志出现:stopped because of stop out
  12. (字典序问题) nyoj1542-最小字符串
  13. 循环神经网络(Recurrent Neural Networks, RNN)介绍
  14. JSP 修改不能编辑
  15. c# 文件笔记
  16. Django的跨域请求
  17. Revit API风管对齐
  18. Android开发--ZZ:Android APK反编译详解(附图)
  19. stm32f10x单片机进阶--spi使用
  20. 如何导入XML数据 (python3.6.6区别于python2 环境)

热门文章

  1. nginx的平滑升级,不间断服务
  2. 第45天:2017webstrom下载破解汉化
  3. 【.Net】C# 反编译工具之dnSpy
  4. HDU 4869 Turn the pokers(思维+逆元)
  5. 【hdu3555】Bomb 数位dp
  6. 【刷题】BZOJ 1002 [FJOI2007]轮状病毒
  7. 2018牛客多校第六场 I.Team Rocket
  8. UVA.1584 环状序列
  9. Vue推荐资料
  10. Semphore信号量的使用