我也是醉了,Eureka 延迟注册还有这个坑!

网站建设3年前发布
31 0 0

Eureka 有个延迟注册的功能,也就是在服务启动成功之后不立刻注册到 Eureka Server,而是延迟一段时间再去注册,这样做的主要目的是因为虽然服务启动成功了,可能还有一些框架或者业务的代码没有初始化完成,可能会导致调用的报错,所以需要延迟注册。,但是发现,然并卵啊,好像这个延迟注册并没有生效,也是开始了排查之路。,首先,延迟注册的功能主要依赖这两个参数,eureka.client.initial-instance-info-replication-interval-seconds​代表第一次初始化延迟注册的时间间隔,eureka.client.instance-info-replication-interval-seconds则代表后续同步注册的时间间隔。,我们从源码先来看是怎么做到延迟注册的,先看 DiscoveryClient​ 的 initScheduledTasks ,这里创建了同步注册到 Eureka Server 的定时任务。,20230306102113350e1eb8846c6cca9d7821de3cc2c92519ffae540,之后调用 start 方法创建定时任务,并且延迟 40 秒执行,也就是我们达到的延迟注册的效果。,20230306102113f1c92c800c7db4e60c3455e303947cc9fc03c0929,2023030610211408b1eb658b679d48ff9364df3a98a71bdac276161,默认的第一次注册,也就是延迟注册的时间是 40 秒,之后每 30 秒会同步注册信息。,2023030610211505934ae222040e4402d36724ac360761974858195,但是,即便我们配置了这俩属性,发现好像没什么卵用,接下来我们要排查下到底是为啥捏?,我发现在 InstanceInfoReplica 中存在这样一段终止当前线程池任务,并且直接调用 run 方法的存在,猜测失效就是他直接调用导致延迟任务没有生效,因为这个方法的直接调用导致延迟注册压根就没效果嘛。,202303061021160592e5354185cf5318d2804c61945e0c97dca6813,看起来他存在两个调用,第一个是registerHealthCheck​,当存在这个健康检查什么玩意儿的时候就会去调用onDemandUpdate。,20230306102116b3eb8c339f811a6f8f3468a54ffd364d988303296,经过排查我们发现,只要配置了eureka.client.healthcheck.enabled=true​,就会创建 HealthCheckHandler​的实例出来,默认情况下他是false的,所以应该是对我们没有影响的。,202303061021170743db19491ded62360352bf086bbeddd1c017701,这里需要特别说明一下 eureka.client.healthcheck.enabled ​的作用,默认 Eureka 根据心跳来决定应用的状态,如果是这个属性配置成 true的话,则是会根据 Spring Boot Actuator 来决定,而不是心跳了。,比如我们可以实现 HealthIndicator​接口,自己写一个Controller来动态改变服务的状态,实现HealthChecker,这样会发现启动、下线服务 Eureka Server 的状态不会变成 Down,只有通过调用接口手动改变应用状态 Server 的状态才会发生改变,大家可以自行测试。,第一个问题我们找到了,发现他不是导致我们问题的根因,于是继续排查。,发现第二个调用,在DiscoveryClient注册了状态事件变更的监听,如果状态发生变更,也会去调用 onDemandUpdate ,影响延迟注册的效果。,这里存在一个配置项onDemandUpdateStatusChange​,默认是true,所以应该是他没错了。,2023030610315562df4232527d278c1fa045508434c2ed4a87fb308,进入StatusChangeListener,找到了一个调用。,20230306102118f63f044690101dee109993fc597d8212b4ef40566,就是通过setInstanceStatus方法触发的事件通知。,2023030610211945446eb01f28d9ca0831126eafd4179faf993a966,这里存在 6 个调用,一一排查,通过源码找啊找,最终定位到服务启动自动装配的地方,在这里去修改服务状态为 UP​,然后触发事件通知,启动 start​ 方法调用register方法。,20230306103155242172975b6fddf95be443bf4a0a3fa0065e19159,继续调用,修改应用为上线UP状态。,20230306102119549342d63873e0ea22f8616592e3dfd3c3902a979,由此我们知道,只要服务启动成功,就会触发事件通知,所以这个基本上是启动成功立刻就会去注册到 Eureka Server,这就会导致延迟注册的失效,从启动日志也能直观的看到这个效果。,2023030610212057df9a085f623b33dce018ed9de82431838949942,为了验证我的猜想,我把这两个配置同时配置成false,并且把延迟注册的时间调整到非常大。,但是,但是!!!,发现过了几十秒之后,还是注册到 Server 了,真的是醉了。。。,那就继续看吧。,再看下注册方法,可能不止一个地方存在调用,我们发现果然如此,有 3 个地方都调用了注册方法。,20230306102122e9dec37778ead4dca7857799c75c7eba52b749758,第一个调用在DiscoveryClient​注入的时候,这个看了下,clientConfig.shouldEnforceRegistrationAtInit()​默认是false,方法不会进来,不管他了。,20230306102123069f58e10b69e2e647b950d1f4ddf0c3774f98338,那么继续看第二个调用,第二个调用你看renew方法,这一看我们就知道了,这不就是心跳吗?!,发送心跳如果返回NOT_FOUND,就会去注册了啊。,20230306103158983c6ff8344cf4db45d5322482e928c54df80e841,20230306102123d1c4ecf51c8d9790047010c2b18ce821820e30580,感觉已经接近真相了,去找下 Server 心跳的源码,根据调用的路径找到源码位于InstanceResource中。,可以看到第一次注册的时候从注册表拿到的实例信息是空的,所以直接返回了 false,就会返回 NOT FOUND 了。,2023030610315816cf9b446778da47f6f55014b16e8781fa820e562,看registry.renew​方法,最终会调用到AbstractInstanceRegistry​中,初始化的时候注册表registry​肯定没有当前实例的信息,所以拿到是空的,返回了false,最终就返回了NOT_FOUND。,2023030610212504cf48c81a63ad806af58791958574d89d53d3644,因此,虽然我们把这两个参数都设置成了false,但是由于心跳默认 30 秒一次,所以最终我们发现配置的超级大的延迟注册的时间并没有完全生效。,OK,到此,延迟注册不生效的原因找到了,我们做一个总结。,默认情况下,配置了延迟注册的时间并不会生效,因为事件监听默认是true,服务启动之后就会立刻注册到 Eureka Server。,如果需要延迟注册生效,必须 eureka.client.healthcheck.enabled ​、eureka.client.onDemandUpdateStatusChange​ 都为false。,即便我们把所有途径都封死了,但是发送心跳的线程仍然会去注册,所以这个延迟注册的时间最多也不会超过 30 秒,即便配置的延迟时间超过 30 秒。

© 版权声明

相关文章