dependency cycle when Use my own ConsulServiceRegistry with spring-boot-starter-actuator
When consul set with discovery.heartbeat.enabled=true and spring-boot-starter-actuator's health endpoint enabled.
At same time, spring-boot-starter-data-mongodb also added. Use my own ConsulServiceRegistry.
Startup and comes with dependency cycle for consul's TtlScheduler! Here is my example with logs, spring_consul_err.
I'm having the same issue, but without overriding anything from consul, just having spring-cloud-starter-consul-discovery, spring-boot-starter-actuator and spring-boot-starter-data-mongodb-reactive on the same classpath and enabling the spring.cloud.consul.discovery.heartbeat.enabled=true.
Here's a reproduction project
in heartbeat, set use-actuator-health to false
heartbeat:
use-actuator-health: false
enabled: true
You can check the ConsulHeartbeatAutoConfiguration to find out the issue
@ConditionalOnProperty(value = "spring.cloud.consul.discovery.heartbeat.use-actuator-health", havingValue = "true",
matchIfMissing = true)
static class ActuatorBasedApplicationStatusProviderConfig
Startup and comes with dependency cycle for consul's TtlScheduler! Here is my example with logs, spring_consul_err.
Copying the cycle here for clarity
***************************
APPLICATION FAILED TO START
***************************
Description:
The dependencies of some of the beans in the application context form a cycle:
stationController defined in file [/Users/clemon/Downloads/demo/build/classes/java/main/com/example/demo/station/StationController.class]
┌─────┐
| mongoTemplate defined in class path resource [org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryDependentConfiguration.class]
↑ ↓
| mappingMongoConverter defined in class path resource [org/springframework/boot/autoconfigure/data/mongo/MongoDatabaseFactoryDependentConfiguration.class]
↑ ↓
| mongoMappingContext defined in class path resource [org/springframework/boot/autoconfigure/data/mongo/MongoDataConfiguration.class]
↑ ↓
| consulAutoServiceRegistrationListener defined in class path resource [org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistrationAutoConfiguration.class]
↑ ↓
| consulAutoServiceRegistration defined in class path resource [org/springframework/cloud/consul/serviceregistry/ConsulAutoServiceRegistrationAutoConfiguration.class]
↑ ↓
| commonConsulServiceRegistry defined in class path resource [com/example/demo/consul/CommonConsulAutoConfiguration.class]
↑ ↓
| ttlScheduler defined in class path resource [org/springframework/cloud/consul/support/ConsulHeartbeatAutoConfiguration.class]
↑ ↓
| actuatorHealthStatusProvider defined in class path resource [org/springframework/cloud/consul/support/ConsulHeartbeatAutoConfiguration$ActuatorBasedApplicationStatusProviderConfig.class]
↑ ↓
| healthEndpoint defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class]
↑ ↓
| healthContributorRegistry defined in class path resource [org/springframework/boot/actuate/autoconfigure/health/HealthEndpointConfiguration.class]
↑ ↓
| mongoHealthContributor defined in class path resource [org/springframework/boot/actuate/autoconfigure/data/mongo/MongoHealthContributorAutoConfiguration.class]
└─────┘
Here is my analysis:
mongoMappingContextis a InitializingBean- During initialization it produces MappingContextEvents (which is subtype of ApplicationEvent)
consulAutoServiceRegistrationListeneris a SmartApplicationListener- SmartApplicationListener extends ApplicationListener<ApplicationEvent>
- So it is necessary to create
consulAutoServiceRegistrationListenerto handle events frommongoMappingContext, thus cycle appears
WebServerInitializedEvent is the only event type that consulAutoServiceRegistrationListener can process, so It should be enough to explicitly declare it in bean definiton to fix this issue. Either by changing implements SmartApplicationListener to implements ApplicationListener<WebServerInitializedEvent> or annotating onApplicationEvent method with @EventListener(WebServerInitializedEvent.class).
Unfortunately I don't have time to test this hypothesis, but I would be glad if someone could do it and submit a PR if everything works fine.