I created a btrace script to reproduce it.
In my case, I set the renew interval to 5s.
There is two registration flows during application starting:
InstanceInfoReplicator
, this starts to register instance triggered by status change (ApplicationInfoManager.StatusChangeListener
in DiscoveryClient
).DiscoveryClient$HeartbeatThread
, this thread starts with delay renewInterval
to DiscoveryClient
initiated. It sends heartbeat request to eureka server. If response code is 404, it will do registration instead.Both flows above will invoke AbstractJerseyEurekaHttpClient$register()
and use EurekaJacksonCodec$InstanceInfoSerializer
to serialize instance object. During serializing, it records status
first, then records lastDirtyTimestamp
.
In particularly extreme case, HeartbeatThread
records status as STARTING
and records lastDirtyTimestamp
after status changed to UP
. But InstanceInfoReplicator
thread records status as UP
and same lastDirtyTimestamp
and send request to eureka server first.
Then registration request of HeartbeatThread
overwrites status with STARTING
. In subsequent heartbeat request, even status is UP
but lastDirtyTimestamp
are same, and eureka server won'tchange instance status to UP
due to logic in InstanceResource$validateDirtyTimestamp
Below tcp dump shows the flow described above.