前言

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

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. Java 混淆器

    在脑海中假想一下,在你苦苦经历 81 难,摸爬滚打研制的技术轮子,终于成型得以问世,遂打个 JAR 包投放于万网之中.可是没过几天,同样功能的轮子出现在你的眼前,关键是核心代码都一样,此时你的内心是否 ...

  2. 1006 Sign In and Sign Out (25 分)

    At the beginning of every day, the first person who signs in the computer room will unlock the door, ...

  3. Halo博客的搭建

    今日主题:搭建一个私人博客 好多朋友和我说,能不能弄一个简单的私人博客啊,我说行吧,今天给你们一份福利啦! 搭建一个私人博客,就可以在自己的电脑上写博客了 Halo Halo 是一款现代化的个人独立博 ...

  4. sql server 表连接类型

    一.数据库访问操作 1.SCAN(最影响性能点,优化切入点) 根据表的不同,分为Table Scan,Cluster Index Scan,Non-clustered Index Scan Table ...

  5. 【公告】请访问我Blog新站——superman2014 www.superman2014.com

    http://www.superman2014.com 欢迎光顾 本博客不在更新!!!!

  6. scratch 如何改变变量的作用域

    在新建变量的时候,有个选项是“适用于所有角色”还是“仅适用于当前角色”.通常称前者为全局变量,所有角色都可以访问到这个变量:后者,称为局部变量,只能在当前角色里访问到这个变量.例如,在使用克隆功能时, ...

  7. 100 Path Sum

    Given a binary tree and a sum, determine if the tree has a root-to-leaf path such that adding up all ...

  8. mysql中的PACK_KEYS

    原文  http://jackyrong.iteye.com/blog/2170222 在mysql的myisam引擎中,有一个是容易忽视的,叫压缩索引PACK_KEYS , myISAM使用前缀压缩 ...

  9. AJ学IOS(25)UI之触摸事件

    AJ分享,必须精品 iOS中的事件 在用户使用app过程中,会产生各种各样的事件;iOS中的事件可以分为3大类型: 响应者对象–UIResponder 在iOS中不是任何对象都能处理事件,只有继承了U ...

  10. 【Tool】在Windows系统上,下载和安装当前最新版本的IDEA 2020-4-14

    下载 & 安装 IDEA 下载部分: 官网地址:https://www.jetbrains.com/idea/ 直接点击鲜眼的DOWNLOAD 如果仅仅是想简单接触学习下Java语言,社区版的 ...