当前位置:网站首页>我也是醉了,Eureka 延迟注册还有这个坑
我也是醉了,Eureka 延迟注册还有这个坑
2022-07-27 17:50:00 【YYniannian】
Eureka 有个延迟注册的功能,也就是在服务启动成功之后不立刻注册到 Eureka Server,而是延迟一段时间再去注册,这样做的主要目的是因为虽然服务启动成功了,可能还有一些框架或者业务的代码没有初始化完成,可能会导致调用的报错,所以需要延迟注册。
但是发现,然并卵啊,好像这个延迟注册并没有生效,也是开始了排查之路。
延迟注册
首先,延迟注册的功能主要依赖这两个参数,eureka.client.initial-instance-info-replication-interval-seconds代表第一次初始化延迟注册的时间间隔,eureka.client.instance-info-replication-interval-seconds则代表后续同步注册的时间间隔。
eureka.client.initial-instance-info-replication-interval-seconds=40 //默认40秒
eureka.client.instance-info-replication-interval-seconds=30 //默认30秒
复制代码我们从源码先来看是怎么做到延迟注册的,先看 DiscoveryClient 的 initScheduledTasks ,这里创建了同步注册到 Eureka Server 的定时任务。

之后调用 start 方法创建定时任务,并且延迟 40 秒执行,也就是我们达到的延迟注册的效果。


默认的第一次注册,也就是延迟注册的时间是 40 秒,之后每 30 秒会同步注册信息。

但是,即便我们配置了这俩属性,发现好像没什么卵用,接下来我们要排查下到底是为啥捏?
第一个问题
我发现在 InstanceInfoReplica 中存在这样一段终止当前线程池任务,并且直接调用 run 方法的存在,猜测失效就是他直接调用导致延迟任务没有生效,因为这个方法的直接调用导致延迟注册压根就没效果嘛。

看起来他存在两个调用,第一个是registerHealthCheck,当存在这个健康检查什么玩意儿的时候就会去调用onDemandUpdate。

经过排查我们发现,只要配置了eureka.client.healthcheck.enabled=true,就会创建 HealthCheckHandler的实例出来,默认情况下他是false的,所以应该是对我们没有影响的。

这里需要特别说明一下 eureka.client.healthcheck.enabled 的作用,默认 Eureka 根据心跳来决定应用的状态,如果是这个属性配置成 true的话,则是会根据 Spring Boot Actuator 来决定,而不是心跳了。
比如我们可以实现 HealthIndicator接口,自己写一个Controller来动态改变服务的状态
@RestController
public class ControllerTest {
@Autowired
private HealthChecker healthChecker;
@RequestMapping("/change")
public String test(Boolean flag) {
healthChecker.setUp(new AtomicBoolean(flag));
return "success";
}
}
复制代码实现HealthChecker,这样会发现启动、下线服务 Eureka Server 的状态不会变成 Down,只有通过调用接口手动改变应用状态 Server 的状态才会发生改变,大家可以自行测试。
@Component
public class HealthChecker extends EurekaHealthIndicator implements HealthIndicator {
private AtomicBoolean up = new AtomicBoolean(true);
public HealthChecker(EurekaClient eurekaClient, EurekaInstanceConfig instanceConfig, EurekaClientConfig clientConfig) {
super(eurekaClient, instanceConfig, clientConfig);
}
@Override
public Health health() {
if(up.get()){
return Health.up().build();
}else{
return Health.down().build();
}
}
复制代码第二个问题
第一个问题我们找到了,发现他不是导致我们问题的根因,于是继续排查。
发现第二个调用,在DiscoveryClient注册了状态事件变更的监听,如果状态发生变更,也会去调用 onDemandUpdate ,影响延迟注册的效果。
这里存在一个配置项onDemandUpdateStatusChange,默认是true,所以应该是他没错了。

进入StatusChangeListener,找到了一个调用。

就是通过setInstanceStatus方法触发的事件通知。

这里存在 6 个调用,一一排查,通过源码找啊找,最终定位到服务启动自动装配的地方,在这里去修改服务状态为 UP,然后触发事件通知,启动 start 方法调用register方法。
继续调用,修改应用为上线UP状态。

由此我们知道,只要服务启动成功,就会触发事件通知,所以这个基本上是启动成功立刻就会去注册到 Eureka Server,这就会导致延迟注册的失效,从启动日志也能直观的看到这个效果。

验证
为了验证我的猜想,我把这两个配置同时配置成false,并且把延迟注册的时间调整到非常大。
eureka.client.healthcheck.enabled=false
eureka.client.onDemandUpdateStatusChange=false
eureka.client.initial-instance-info-replication-interval-seconds=9999999 //默认40秒
eureka.client.instance-info-replication-interval-seconds=999999 //默认30秒
复制代码但是,但是!!!
发现过了几十秒之后,还是注册到 Server 了,真的是醉了。。。
那就继续看吧。
再看下注册方法,可能不止一个地方存在调用,我们发现果然如此,有 3 个地方都调用了注册方法。

第一个调用在DiscoveryClient注入的时候,这个看了下,clientConfig.shouldEnforceRegistrationAtInit()默认是false,方法不会进来,不管他了。

那么继续看第二个调用,第二个调用你看renew方法,这一看我们就知道了,这不就是心跳吗?!
发送心跳如果返回NOT_FOUND,就会去注册了啊。


感觉已经接近真相了,去找下 Server 心跳的源码,根据调用的路径找到源码位于InstanceResource中。
可以看到第一次注册的时候从注册表拿到的实例信息是空的,所以直接返回了 false,就会返回 NOT FOUND 了。

看registry.renew方法,最终会调用到AbstractInstanceRegistry中,初始化的时候注册表registry肯定没有当前实例的信息,所以拿到是空的,返回了false,最终就返回了NOT_FOUND。

因此,虽然我们把这两个参数都设置成了false,但是由于心跳默认 30 秒一次,所以最终我们发现配置的超级大的延迟注册的时间并没有完全生效。
总结
OK,到此,延迟注册不生效的原因找到了,我们做一个总结。
默认情况下,配置了延迟注册的时间并不会生效,因为事件监听默认是true,服务启动之后就会立刻注册到 Eureka Server。
如果需要延迟注册生效,必须 eureka.client.healthcheck.enabled 、eureka.client.onDemandUpdateStatusChange 都为false。
即便我们把所有途径都封死了,但是发送心跳的线程仍然会去注册,所以这个延迟注册的时间最多也不会超过 30 秒,即便配置的延迟时间超过 30 秒。
边栏推荐
- C191: password compilation
- 多点双向重发布及路由策略的简单应用
- ms721负载测试
- Program design Comprehensive Experiment III
- MongoDB 学习笔记: BSON 结构分析
- ‘vite‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件
- Product Manager: check where there is an error prompt of "system exception" on the offline
- Function priority
- [redis] several deployment methods of redis
- Sword finger offer 25. merge two sorted linked lists
猜你喜欢

总线Bus是什么意思

Introduction to basic cesium controls

图解LeetCode——剑指 Offer II 115. 重建序列(难度:中等)

PyQt5快速开发与实战 4.3 QLabel and 4.4 文本框类控件
![[论文阅读] Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation](/img/a9/690f52b5c4afba684f0add2434888c.png)
[论文阅读] Rich Feature Hierarchies for Accurate Object Detection and Semantic Segmentation

Compiling ncnn with vs

信道容量、信道带宽基本概念的理解

If you want to switch to software testing, you should pass these three tests first, including a 3000 word super full test learning guide

codeforces每日5题(均1500)-第二十四天

图解LeetCode——592. 分数加减运算(难度:中等)
随机推荐
发布2年后涨价100美元,Meta Quest 2的逆生长
Clickhouse 实现 MaterializedPostgreSQL
'vite' is not an internal or external command, nor is it a runnable program or batch file
速卖通:按关键字搜索商品 API
内置函数时间日期函数
ES6--解构赋值
Membership card head assembly usage document
PMP practice once a day | don't get lost in the exam -7.27 (including agility + multiple choices)
图解LeetCode——592. 分数加减运算(难度:中等)
LG Group announced that it would donate 3million yuan in cash, 1.2 million masks and 10000 sets of protective clothing to Hubei
System information function of MySQL function summary
PyQt5快速开发与实战 4.7 QSpinBox(计数器) and 4.8 QSlider(滑动条)
Introduction to basic cesium controls
[C #] positive sequence, reverse sequence, maximum value, minimum value and average value
产品经理:排查下线上哪里冒出个“系统异常”的错误提示
2019年中国智能机市场:华为拿下近4成份额,稳坐国内第一
YY English learning about fish
《安富莱嵌入式周报》第275期:2022.07.18--2022.07.24
Can software testing be learned in 2022? Don't learn, software testing positions are saturated
Rodin installs the SMT solvers plug-in