乐趣区

关于eureka:我也是醉了Eureka-延迟注册还有这个坑

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,到此为止,完结,我是艾小仙,欢送拍砖。

退出移动版