Eureka有延迟注册功能,即服务启动成功后,不会立即向EurekaServer注册,而是在一段时间后注册。这样做的主要目的是因为虽然服务启动成功,但是可能有一些框架或者业务代码没有初始化,可能会导致调用出错,所以需要延迟注册。但是我发现它不起作用。看来是延迟登记没有生效,调查开始了。延迟注册首先,延迟注册的功能主要取决于这两个参数,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,这里创建了一个同步注册到EurekaServer的定时任务。然后调用start方法创建一个定时任务,延迟执行40秒,就是我们实现的延迟注册的效果。默认首次注册,即延迟注册时间为40秒,之后每30秒同步一次注册信息。然而,即使我们配置了这两个属性,我们也发现它们似乎没什么用。接下来,我们需要检查为什么?我发现的第一个问题是在InstanceInfoReplica中有这么一段,终止当前线程池任务,直接调用run方法。我猜测失败是因为直接调用了这个方法导致延迟任务没有生效,因为直接调用这个方法导致延迟注册根本就失败了。影响。好像他有两个调用,第一个是registerHealthCheck,当有这种健康检查的时候,onDemandUpdate就会被调用。经过排查,我们发现只要配置eureka.client.healthcheck.enabled=true,就会创建一个HealthCheckHandler实例。默认情况下是false,所以应该对我们没有影响。这里需要说明一下eureka.client.healthcheck.enabled的作用。默认情况下,Eureka根据心跳判断应用状态。如果该属性配置为true,则将根据SpringBootActuator而不是心跳来判断。比如我们可以实现HealthIndicator接口,写一个Controller来动态改变服务的状态@RestControllerpublicclassControllerTest{@AutowiredprivateHealthCheckerhealthChecker;@RequestMapping("/change")publicStringtest(Booleanflag){healthChecker.setUp(newAtomicBoolean(flag));返回“成功”;}}实现HealthChecker,这样你会发现启动和离线服务EurekaServer的状态不会变成Down,只有应用状态Server的状态会通过调用接口手动改变。大家可以自己测试一下。@ComponentpublicclassHealthCheckerextendsEurekaHealthIndicatorimplementsHealthIndicator{privateAtomicBooleanup=newAtomicBoolean(true);publicHealthChecker(EurekaClienteurekaClient,EurekaInstanceConfiginstanceConfig,EurekaClientConfigclientConfig){super(eurekaClient,instanceConfig,clientConfig);}@OverridepublicHealthhealth(){if(up.get()){returnHealth.up().build();}else{返回Health.down().build();}}第二个问题我们找到了第一个问题,发现他不是我们问题的根本原因,所以继续排查。发现第二次调用注册在DiscoveryClient中,用于监听状态事件变化。如果状态发生变化,也会调用onDemandUpdate,影响延迟注册的效果。有一个配置项onDemandUpdateStatusChange,默认为true,所以他应该是对的。进入StatusChangeListener,发现一个调用。它是由setInstanceStatus方法触发的事件通知。这里有6个调用,一一查看,通过源码找啊找啊,最后定位到服务开始自动组装的地方,修改这里的服务状态为UP,然后触发事件通知,启动start方法和调用注册方法。继续调用并修改应用,使其处于UP状态。由此我们知道,只要服务启动成功,就会触发事件通知,所以基本上启动成功后会立马注册到EurekaServer,这样就会导致延迟注册失败,从中也可以直观的看出启动日志这个效果。验证为了验证我的猜测,我将这两个配置同时配置为false,并且将延迟注册时间调整为一个非常大的值。eureka.client.healthcheck.enabled=falseeureka.client.onDemandUpdateStatusChange=falseeureka.client.initial-instance-info-replication-interval-seconds=9999999//默认40秒eureka.client.instance-info-replication-interval-seconds=999999//默认30秒但是,但是!!!发现过了几十秒,还是注册到服务器上了,真是醉了。..然后继续阅读。再看注册方式,调用的地方可能不止一处。我们发现确实如此,在3处调用了注册方法。第一次调用注入DiscoveryClient的时候,看看这个,clientConfig.shouldEnforceRegistrationAtInit()默认为false,方法不会进来,不管他。然后继续看第二个调用。第二次调用可以看renew方法。我们看了就知道了。这不是心跳吗?!如果你发送心跳并返回NOT_FOUND,你将注册。感觉接近真相了。找到Serverheartbeat的源码,根据调用路径找到位于InstanceResource的源码。可以看到第一次注册时从注册中心获取的实例信息是空的,所以直接返回false会返回NOTFOUND。查看registry.renew方法,最终会调用AbstractInstanceRegistry。初始化的时候registry一定没有当前实例的信息,所以为空,返回false,最后返回NOT_FOUND。因此,虽然我们将这两个参数设置为false,但是由于默认的心跳是30秒,最后我们发现配置的超延时注册时间并没有完全生效。总结OK,到此为止,延迟注册不生效的原因已经找到了,我们来做一个总结。默认情况下,配置的延迟注册时间不会生效,因为事件监听器默认为true,服务启动后会立即注册到EurekaServer。如果需要延迟注册生效,必须将eureka.client.healthcheck.enabled和eureka.client.onDemandUpdateStatusChange设置为false。即使我们阻塞所有通道,发送心跳的线程仍然会注册,所以延迟注册时间最多不会超过30秒,即使配置的延迟时间超过30秒。OK,到此为止,结束,我是艾小仙,欢迎拍砖。