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秒

我们从源码先来看是怎么做到延迟注册的,先看 DiscoveryClientinitScheduledTasks ,这里创建了同步注册到 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 秒。

OK,到此为止,结束,我是艾小仙,欢迎拍砖。

我也是醉了,Eureka 延迟注册还有这个坑!的更多相关文章

  1. spring cloud eureka注册原理-注册失败填坑

    写在前面 我们知道Eureka分为两部分,Eureka Server和Eureka Client.Eureka Server充当注册中心的角色,Eureka Client相对于Eureka Serve ...

  2. SpringCloud笔记三:Eureka服务注册与发现

    目录 什么是Eureka? Eureka注册的三大步 第一步,引用Maven 第二步,配置yml 第三步,开启Eureka注解 新建Eureka子项目 把provider子项目变成服务端 Eureka ...

  3. spring cloud配置高可用eureka时遇到的一些坑

    考虑到注册中心的高可用,今天搭建一下eureka集群,并在中途遇到一些小的坑,前前后后花了两个小时来排除,在这里记录一下,以供后面查看. 首先,贴上要达到的效果: 一.起两个eureka模拟集群,在D ...

  4. Spring Cloud之Eureka服务注册与发现

    解决什么问题 ➟阐述微服务以及服务注册发现的部分概念 ➟阐述Eureka服务注册与发现的部分原理及细节 为什么需要服务中心 过去,每个应用都是一个CPU,一个主机上的单一系统.然而今天,随着大数据和云 ...

  5. Eureka集群的那些坑

    今天遇到一个Eureka集群的一个坑. 问题现场类似是这样的:两台Eureka组成的服务注册中心集群,两台服务提供方server1.server2,两个服务调用方client1.client2. 正常 ...

  6. SpringCloud的入门学习之概念理解、Eureka服务注册与发现入门

    1.微服务与微服务架构.微服务概念如下所示: 答:微服务强调的是服务的大小,它关注的是某一个点,是具体解决某一个问题.提供落地对应服务的一个服务应用,狭意的看,可以看作Eclipse里面的一个个微服务 ...

  7. SpringCloud微服务-Eureka服务注册与发现

    一. Eureka 是什么? Eureka是Netflix的一个子模块,也是核心模块之一.Eureka是一个基于REST的服务,用于定位服务,以实现云端中间层服务发现和故障转移.服务注册与发现对微服务 ...

  8. Spring-cloud & Netflix 源码解析:Eureka 服务注册发现接口 ****

    http://www.idouba.net/spring-cloud-source-eureka-client-api/?utm_source=tuicool&utm_medium=refer ...

  9. My.Ioc 代码示例——利用 ObjectBuilderRequested 事件实现延迟注册

    在使用 Ioc 框架时,一般我们建议集中在一个称为 Composition Root(其含义请参见下面的小注)的位置来注册 (Register) 和解析 (Resolve) 服务.这种做法的目的在于限 ...

随机推荐

  1. 不再空谈AI,从打造一台智能无人机开始

    对于大多数无人机爱好者来说,能自己从头开始组装一台无人机,之后加入AI算法,能够航拍,可以目标跟踪,是心中的梦想. 并且,亲自从零开始完成复杂系统,这是掌握核心技术的必经之路. 开课吧特邀北京航空航天 ...

  2. 前端面试 -HTTP系列

    http和https 的区别? 端口 经济 安全性 响应速度 http 80端口 不需要 明文传输,安全性差 页面响应速度快,使用tcp的3次握手 https 443端口 费钱SSL需要ca 证书 S ...

  3. Python 散列表查询_进入<哈希函数>为结界的世界

    1. 前言 哈希表或称为散列表,是一种常见的.使用频率非常高的数据存储方案. 哈希表属于抽象数据结构,需要开发者按哈希表数据结构的存储要求进行 API 定制,对于大部分高级语言而言,都会提供已经实现好 ...

  4. vmware 安装的虚拟机没有网络

    前提:需要先将 vmware 软件里的所有虚拟机关机 查看以下两个服务是否启动 如果以上两个服务未启动,就全部启动起来,如果某一个在启动时报错,就打开 vmware 软件,执行以下操作 编辑 > ...

  5. git提交时写message的规范

    message规范 angular示例 commit message(提交说明) git commit -m "写一行提交说明" # 跳出文本编辑器,写多行 git commit ...

  6. 130_传析阅管理系统accdb64位版本

    博客:www.jiaopengzi.com 焦棚子的文章目录 请点击下载附件 几年前笔者针对家居门店的进销存.人员管理.工资管理.任务系统.门店经营盈亏管理.销售分析.考勤请假等息息相关的业务基于Ac ...

  7. Oceanbase读写分离方案探索与优化

    [作者] 许金柱,携程资深DBA,专注于分布式数据库研究及运维. 台枫,携程高级DBA,主要负责MySQL和OceanBase的运维. [前言]    读写分离,是一种将数据库的查询操作和写入操作分离 ...

  8. 2020级cpp机考模拟题A卷-#题解1

    为了各位朋友的身心健康(不是),我们按照题目难度顺序来写题解. 第一次写题解,希望多点包容和鼓励(恬不知耻 1:谁先输出-4 题意: 输入3个整数,按从大到小的顺序输出,每两个数字间加一个空格. 题解 ...

  9. asp.net6 blazor 文件上传

    微软在asp.net6中给blazor新增了一个IJSStreamReference的接口. 我们今天的所有内容,都要依赖这个接口,因为它可以把流直接传到c#中,这样我们就可以做很多的骚操作了. 今天 ...

  10. Redis 应用只 消息队列 简单实现(生产者 消费者模式)

    运行效果: