前言

带着问题学习,事半功倍。本文将对如下几个问题进行总结说明:

1、EurekaServer端服务注册的流程和设计模式

2、Eureka服务续约的bug

3、EurekaClient的启动流程

4、client启动后是往一个server注册还是多个server遍历注册

5、EurekaServer的三级缓存

6、一个EurekaClient宕机后,其他EurekaClient最晚多长时间后才会不再往这个宕机的服务发起请求?

Eureka在Spring Cloud组件全家桶中,处于很核心的位置,从去年格林尼治版的更新说明中就能知道,更新日志截图如下,netflix的其他组件均进入维护状态,不再添加新特性,但Eureka不包括在内。个人观点,一方面Eureka的功能实现相对比较复杂,不好随便改动,再就是位置关键,改动后影响范围广。

下面进入正文。注:Spring Cloud版本Hoxton SR1,eureka-core 1.9.13

正文 

一、EurekaServer端服务注册的流程和设计模式

服务端的入口类如下所示,不带s的类中是对单个服务/实例的操作,带s的是集合操作。服务注册入口在ApplicationResource中。

服务注册方法是ApplicationResource#addInstance,可以看到经过一些必要的判断后调用了注册方法,注意因为该请求是从客户端发起的,isReplication为空,所以register方法的第二个参数是false。

         registry.register(info, "true".equals(isReplication));
return Response.status(204).build(); // 204 to be backwards compatible
}

追踪进入PeerAwareInstanceRegistryImpl类的register方法,如下,该方法先调用了父类的注册方法,然后调的往其他服务扩散注册信息的方法replicateToPeers。

     @Override
public void register(final InstanceInfo info, final boolean isReplication) {
int leaseDuration = Lease.DEFAULT_DURATION_IN_SECS;
if (info.getLeaseInfo() != null && info.getLeaseInfo().getDurationInSecs() > 0) {
leaseDuration = info.getLeaseInfo().getDurationInSecs();
}
super.register(info, leaseDuration, isReplication);
replicateToPeers(Action.Register, info.getAppName(), info.getId(), info, null, isReplication);
}

继续跟进到父类AbstractInstanceRegistry,在父类的register方法中完成了对真实服务列表ConcurrentHashMap<String, Map<String, Lease<InstanceInfo>>> registry的维护 (方法太长就不贴出来了)。

至此完成了服务注册,共涉及到三个类:ApplicationResource、PeerAwareInstanceRegistryImpl、AbstractInstanceRegistry。前两个类的register方法都是做了一些自己的事情外加调用父类的register,一个典型的责任链模式应用,一个类只负责自己的事情,然后调用上一层的方法,如果需加一个功能,只需要在对应位置加一层继承关系即可,对原有功能无侵入。

二、Eureka服务续约的bug

打开Lease租债器类,看到renew方法和isExpired方法:

 public void renew() {
lastUpdateTimestamp = System.currentTimeMillis() + duration; }
 public boolean isExpired(long additionalLeaseMs) {
return (evictionTimestamp > 0 || System.currentTimeMillis() > (lastUpdateTimestamp + duration + additionalLeaseMs));
}

续约方法每次调用都将最后修改时间变为当前时间+有效期(默认90s),而判断是否失效的方法比较的是当前时间和最后修改时间+有效期,这就导致有效期加了两次,即一个服务过了两倍的有效期时间之后才会被服务端判定为到期。其实这个事情在isExpired方法的注释中可以看到说明:

     /**
* Checks if the lease of a given {@link com.netflix.appinfo.InstanceInfo} has expired or not.
*
* Note that due to renew() doing the 'wrong" thing and setting lastUpdateTimestamp to +duration more than
* what it should be, the expiry will actually be 2 * duration. This is a minor bug and should only affect
* instances that ungracefully shutdown. Due to possible wide ranging impact to existing usage, this will
* not be fixed.
*
* @param additionalLeaseMs any additional lease time to add to the lease evaluation in ms.
*/

三、EurekaClient的启动流程

Eureka客户端只需要引入依赖加上配置,便可以自动实现服务注册。客户端的功能主要包括三部分:启动时的服务注册、服务定时续约、服务列表缓存定时更新。

客户端启动的逻辑都在DiscoveryClient的构造方法中,com.netflix.discovery.DiscoveryClient#DiscoveryClient,方法代码太长,就不粘贴代码了,只描述下流程:

初始化两个ThreadPoolExecutor:服务续约和更新缓存;

从server拉取注册信息com.netflix.discovery.DiscoveryClient#fetchRegistry;

com.netflix.discovery.DiscoveryClient#register服务注册;

启动定时器。

四、client启动后是往一个server注册还是多个server遍历注册

客户端在执行register方法注册服务时,采用装饰器模式对httpClient进行处理,其中有一个是RetryableEurekaHttpClient,在该类的execute方法中对客户端配置文件中配置的serviceUrl进行了遍历,如果第一个注册请求处理成功了,则不再重试,否则遍历serviceUrl重试。具体可见com.netflix.discovery.shared.transport.decorator.RetryableEurekaHttpClient#execute方法。

五、EurekaServer的三级缓存

eureka的服务端为了提高服务列表维护和读取的一致性与可用性,对服务列表的查看设置了三级缓存,入口为com.netflix.eureka.resources.ApplicationsResource#getContainers。在该方法中调用了

ResponseCacheImpl#getGZIP方法获取缓存,如下:

 public byte[] getGZIP(Key key) {
Value payload = getValue(key, shouldUseReadOnlyResponseCache);
if (payload == null) {
return null;
}
return payload.getGzipped();
}

继续跟进getValue方法:

 Value getValue(final Key key, boolean useReadOnlyCache) {
Value payload = null;
try {
if (useReadOnlyCache) {
final Value currentPayload = readOnlyCacheMap.get(key);
if (currentPayload != null) {
payload = currentPayload;
} else {
payload = readWriteCacheMap.get(key);
readOnlyCacheMap.put(key, payload);
}
} else {
payload = readWriteCacheMap.get(key);
}
} catch (Throwable t) {
logger.error("Cannot get value for key : {}", key, t);
}
return payload;
}

可以看到,这里有两个map:readOnlyCacheMap(只读缓存)、readWriteCacheMap(读写缓存),再加上AbstractInstanceRegistry#registry真实数据,总共三级map缓存。

它们使用的规则如下:只读缓存每隔30s定时从读写缓存中更新最新数据,读写缓存与真实数据是同步的,它的存在是为了减少对真实数据的读取。额外要注意,在eureka server中,读取操作用的写锁,而注册修改下线操作用的读锁。

通过三级缓存,Eureka在并发吞吐量的基础上做到了最大程度的数据一致性。这种设计思路值得学习。

六、一个EurekaClient宕机后,其他EurekaClient最晚多长时间后才会不再往这个宕机的服务发起请求?

先说下上面三级缓存场景可能产生的延迟:如果在服务端的真实服务列表中,一个服务已经被剔除了,此时最多过多长时间其他客户端才能得知到此消息?

客户端每隔30s去服务端拉取一次缓存 + 服务端只读缓存每30s同步一次读写缓存的数据,即最长需要60s后客户端才能得到最新的服务端列表数据。

再来看宕机的情况,即如果一个服务宕机,其他服务最多会经过多长时间才不会再往这个服务发送请求?

先看服务端,因为有上面第二项说的bug存在,默认服务端经过90s*2才会剔除该宕机服务,该剔除的定时器每60s执行一次,再加上上面说的客户端更新缓存的60s延迟,再加上ribbon的60s缓存,所以总计是:

90*2 + 60 + 60 + 60 = 360s,即最长可能需要6分钟。

小结

Eureka的定时器真TM多。。。

Eureka重点原理解析的更多相关文章

  1. Tengine HTTPS原理解析、实践与调试【转】

    本文邀请阿里云CDN HTTPS技术专家金九,分享Tengine的一些HTTPS实践经验.内容主要有四个方面:HTTPS趋势.HTTPS基础.HTTPS实践.HTTPS调试. 一.HTTPS趋势 这一 ...

  2. 【转】C# URL短地址压缩算法及短网址原理解析

    这篇文章主要介绍了C# URL短地址压缩算法及短网址原理解析,本文重点给出了算法代码,需要的朋友可以参考下 短网址应用已经在全国各大微博上开始流行了起来.例如QQ微博的url.cn,新郎的sinaur ...

  3. 基于OpenCV进行图像拼接原理解析和编码实现(提纲 代码和具体内容在课件中)

    一.背景 1.1概念定义 我们这里想要实现的图像拼接,既不是如题图1和2这样的"图片艺术拼接",也不是如图3这样的"显示拼接",而是实现类似"BaiD ...

  4. ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析

    ThreadLocal系列(三)-TransmittableThreadLocal的使用及原理解析 上一篇:ThreadLocal系列(二)-InheritableThreadLocal的使用及原理解 ...

  5. 【转载】Java类加载原理解析

    Java类加载原理解析 原文出处:http://www.blogjava.net/zhuxing/archive/2008/08/08/220841.html 1       基本信息 摘要: 每个j ...

  6. ButterKnife 原理解析

    一.使用方法 1.添加依赖. implementation 'com.jakewharton:butterknife:8.8.1' annotationProcessor 'com.jakewhart ...

  7. C#版清晰易懂TCP通信原理解析(附demo)

    [转] C#版清晰易懂TCP通信原理解析(附demo) (点击上方蓝字,可快速关注我们) 来源:周见智 cnblogs.com/xiaozhi_5638/p/4244797.html 对.NET中网络 ...

  8. java线程池原理解析

    五一假期大雄看了一本<java并发编程艺术>,了解了线程池的基本工作流程,竟然发现线程池工作原理和互联网公司运作模式十分相似. 线程池处理流程 原理解析 互联网公司与线程池的关系 这里用一 ...

  9. Spring Cloud系列(三):Eureka源码解析之服务端

    一.自动装配 1.根据自动装配原理(详见:Spring Boot系列(二):Spring Boot自动装配原理解析),找到spring-cloud-starter-netflix-eureka-ser ...

随机推荐

  1. 1047: 【入门】正整数N转换成一个二进制数

    1047: [入门]正整数N转换成一个二进制数 时间限制: 1 Sec 内存限制: 16 MB 提交: 9786 解决: 6447 [提交] [状态] [讨论版] [命题人:外部导入] 题目描述 输入 ...

  2. 1519: 【USACO】超级书架

    1519: [USACO]超级书架 时间限制: 1 Sec 内存限制: 64 MB 提交: 1735 解决: 891 [提交] [状态] [讨论版] [命题人:外部导入] 题目描述 Farmer Jo ...

  3. 15分钟从零开始搭建支持10w+用户的生产环境(一)

    前言 这是一个基于中小型企业或团队的架构设计. 不考虑大厂.有充分的理由相信,大厂有绝对的实力来搭建一个相当复杂的环境. 中小型企业或团队是个什么样子? 开发团队人员配置不全,部分人员身兼开发过程上下 ...

  4. 在MAC上如何使用SQL Server

    由于小编在这学期要学习数据库原理这门课程,需要用到SQL Server,然而大家都知道SQL Server目前是只能在Windows上使用,我们在mac电脑上如何使用呢?我们可以借助目前比较火的Doc ...

  5. GO代码风格指南 Uber Go (转载)

    原文地址:https://github.com/uber-go/guide/blob/master/style.md 译文出处:https://github.com/uber-go/guide 本文永 ...

  6. Java成长记录第二集--基础重点

    第一篇写的博客给自己的学习路线立了个flag后,感觉现在学习的积极性大增,这也离不开那几位老铁们的互相鼓励.废话不多说,现在给出自己总结的Java基础部分所要重点注意的内容,对以后的开发工作也是很常用 ...

  7. python这门语言为什么要起这个名字

    我只是一只可爱的小虫 前言 文的文字及图片来源于网络,仅供学习.交流使用,不具有任何商业用途,版权归原作者所有,如有问题请及时联系我们以作处理. 作者:Liz喵 PS:如有需要Python学习资料的小 ...

  8. python画图——雪花(科赫曲线)

    科赫曲线是一种分形,其形态非常像雪花,因此又被称作科赫雪花.雪花曲线. 下面是用python的turtle包让我们来实时画一个 import turtledef koch(t,n): #定义一个函数 ...

  9. C - Ivan the Fool and the Probability Theory---div2

    题目连接:https://codeforces.com/contest/1248/problem/C 思路: 注意上下两排的关系,如果说上面那一排有两个方格连续,那么他相邻的两排必定和他相反,如果说当 ...

  10. [转]sql二次注入

    01 二次注入原理 二次注入可以理解为,攻击者构造的恶意数据存储在数据库后,恶意数据被读取并进入到SQL查询语句所导致的注入.防御者可能在用户输入恶意数据时对其中的特殊字符进行了转义处理,但在恶意数据 ...