spring boot actuator health endpoint + dynamic resilience4j circuit breakers - java

I have a spring boot app, which uses resilience4j AOP-based #CircuitBreakers.
Now I would like to make the circuit breakers' information available in the /actuator/health endpoint, but I'm not seeing the details.circuitBtreakers objects described in the docs in the JSON output.
What am I doing wrong?
By comparison, getting dynamic cache information to appear in the /actuator/metrics endpoint required a small amount of custom wiring, but this is well documented. I wonder if there is a similar trick that I can apply for dynamically defined #CircuitBreakers to be registerd with the /actuator/health endpoint.
MyService.java:
#Service
public class MyService {
#Autowired
private CacheManager cacheManager;
#Autowired
private CacheMetricsRegistrar cacheMetricsRegistrar;
#PostConstruct
public void postConstruct() {
// On-the-fly defined (annotation-based) caches are not auto-registered with micrometer metrics.
final Cache cache = cacheManager.getCache("myCache");
cacheMetricsRegistrar.bindCacheToRegistry(cache);
}
#CircuitBreaker(name = "myCB", fallbackMethod = "fallbackCallAnApi")
public String callAnApi() throws RestClientException {
// ...
}
#Cacheable("myCache")
public String getSomethingCacheable() {
// ...
}
}
application.properties:
resilience4j.circuitbreaker.configs.default.registerHealthIndicator=true
management.endpoints.web.expose=health,metrics
management.endpoints.web.exposure.include=health,metrics
management.endpoint.health.enabled=true
management.endpoint.metrics.enabled=true
management.metrics.enable.resilience4j.circuitbreaker.calls=true
management.health.circuitbreakers.enabled=true

Dynamically registering CircuitBreakers for the HealthIndicator endpoint doesn't work at the moment.
Unfortunately you have to configure them:
resilience4j.circuitbreaker:
configs:
default:
registerHealthIndicator: true
instances:
myCB:
baseConfig: default
You could say it's a bug.
https://github.com/resilience4j/resilience4j/blob/master/resilience4j-spring-boot2/src/main/java/io/github/resilience4j/circuitbreaker/monitoring/health/CircuitBreakersHealthIndicator.java#L99-L102

Related

Spring Boot 2.7.2 with Spring Kafka fails during initialisation with Exception: The 'ProducerFactory' must support transactions`

I'm using Spring Boot v2.7.2 and the latest version of Spring Kafka provided by spring-boot-dependencies:
<dependency>
<groupId>org.springframework.kafka</groupId>
<artifactId>spring-kafka</artifactId>
</dependency>
I want the app to load all configuration from file hence I created the beans with this bare minimum configuration:
public class KakfaConfig {
#Bean
public ProducerFactory<Integer, FileUploadEvent> producerFactory() {
return new DefaultKafkaProducerFactory<>(Collections.emptyMap());
}
#Bean
public KafkaTemplate<Integer, FileUploadEvent> kafkaTemplate() {
return new KafkaTemplate<Integer, anEvent>(producerFactory());
}
}
It works and loads the configuration from the application.yaml below as expected.
spring:
application:
name: my-app
kafka:
bootstrap-servers: localhost:9092
producer:
client-id: ${spring.application.name}
# transaction-id-prefix: "tx-"
template:
default-topic: my-topic
However, if I uncomment the transaction-id-prefix line, the application fails to start with the exception
java.lang.IllegalArgumentException: The 'ProducerFactory' must support transactions
The documentation in here reads
If you provide a custom producer factory, it must support
transactions. See ProducerFactory.transactionCapable().
The only way I managed to make it work is removing the transaction prefix from the application.yaml and configure it in the code as per below:
#Bean
public ProducerFactory<Integer, FileUploadEvent> fileUploadProducerFactory() {
var pf = new DefaultKafkaProducerFactory<Integer, FileUploadEvent>(Collections.emptyMap());
pf.setTransactionIdPrefix("tx-");
return pf;
}
Any thoughts on how I can configure everything using the application properties file? Is this a bug?
The only solution atm is really setting the transaction-prefix-id in the code whilst creating the ProducerFactory, despite it's already been defined in the application.yaml.
The Spring Boot team replied as per below:
The intent is that transactions should be used and that the ProducerFactory should support them. The transaction-id-prefix property can be set and this results in the auto-configuration of the kafkaTransactionManager bean. However, if you define your own ProducerFactory (to constrain the types, for example) there's no built-in way to have the transaction-id-prefix applied to that ProducerFactory.
It's a fundamental principle of auto-configuration that it backs off when a user defines a bean of their own. If we post-processed the user's bean to change its configuration, it would no longer be possible for your code to take complete control of how things are configured. Unfortunately, this flexibility does sometimes require you to write a little bit more code. This is one such time.
If we want to keep the prefix as a property in the application.yaml file, we can inject it to avoid config duplication:
#Value("${spring.kafka.producer.transaction-id-prefix}")
private String transactionPrefix;
#Bean
public ProducerFactory<Integer, FileUploadEvent> fileUploadProducerFactory() {
var pf = new DefaultKafkaProducerFactory<Integer, FileUploadEvent>(Collections.emptyMap());
pf.setTransactionIdPrefix(transactionIdPrefix);
return pf;
}

WebFlux Spring Boot #Transactional with reactive MongoDB

Does WebFlux Spring Boot #Transactional annotation work with reactive MongoDB?
I use WebFlux Spring Boot with reactive MongoDB like:
id 'org.springframework.boot' version '2.6.7'
...
implementation 'org.springframework.boot:spring-boot-starter-webflux'
implementation 'org.springframework.boot:spring-boot-starter-data-mongodb-reactive'
...
I marked one of my method #Transactional to test. But it seems the annotation does not work for me. If an error occurs inside this method, then it still adds a raw to my mongoDB database.
import org.springframework.transaction.annotation.Transactional;
...
#Transactional
public Mono<Chat> createChat(Chat chat) {
return chatRepository
.save(chat)
.map(
c-> {
if (true) {
throw new RuntimeException();
}
return c;
});
}
Do I miss something or Spring Boot #Transactional annotation does not work with reactive MongoDB?
I use MongoDB v5.0.8
It seems like that Spring Data for reactive MongoDB requires to set explicitly a special bean transactionManager. As soon as I have added this bean to my configuration for the reactive MongoDB, the #Transactional annotation started working. So the example method posted in my question does not add a new raw to the database anymore if an error occurs inside the method.
Here is my configuration with transactionManager bean:
#Configuration
#EnableReactiveMongoRepositories
#AllArgsConstructor
public class ReactiveMongoConfiguration extends AbstractReactiveMongoConfiguration {
private final MongoProperties mongoProperties;
#Override
public MongoClient reactiveMongoClient() {
return MongoClients.create();
}
#Override
protected String getDatabaseName() {
return mongoProperties.getDatabase();
}
#Bean
ReactiveMongoTransactionManager transactionManager(ReactiveMongoDatabaseFactory reactiveMongoDatabaseFactory) {
return new ReactiveMongoTransactionManager(reactiveMongoDatabaseFactory);
}
P.S.
It turns out the defining of transactionManager bean is not enough to enable transactions in reactive MongoDB. The very server of MongoDB should be also configured with replication. I followed these steps and it worked for me.

Spring cloud loadbalancer - Feign + SimpleDiscoveryClient with healthcheck/retry

I'm struggling to switch from Ribbon to Spring Cloud Loadbalancer after upgrading spring cloud versions.
Setting up the SimpleDiscoveryClient with Feign was easy.
But the simplediscovery client is 'too simple'.
I want to add at least a healthcheck so it doesn't use an instance that is potentially down & preferably also a retry mechanism.
I've read the docs over & over but cannot find an easy way to set this up.
https://docs.spring.io/spring-cloud-commons/docs/current/reference/html/#instance-health-check-for-loadbalancer
I found this example custom config for the health check, but it doesn't work.
public class CustomLoadBalancerConfiguration {
#Bean
public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
ConfigurableApplicationContext context) {
return ServiceInstanceListSupplier.builder()
.withDiscoveryClient()
.withHealthChecks()
.build(context);
}
}
if I run it as is, it throws the following missing bean error:
Method discoveryClientServiceInstanceListSupplier in XXX.CustomLoadBalancerConfig required a bean of type 'org.springframework.web.client.RestTemplate' that could not be found.
Can anyone give me some pointers on how to get this working or how I can replicate the ribbon behavior?
Add the below RestTemplate Bean in your configuration.
#Bean
public RestTemplate restTemplate() {
return new RestTemplate();
}
Try adding the following to your application class
#LoadBalancerClients(defaultConfiguration = LoadBalancerClientConfiguration.class)
public class GatewayApplication {
public static void main(String[] args) {
SpringApplication.run(GatewayApplication.class, args);
}
}
This will enable the creation of DiscoveryClientServiceInstanceListSupplier (note the class itself is not available in the context, so you can't just autowire it for testing).

How to use actuator endpoints programmatically?

I need some metrics in to the spring boot application. So I used MetricsEndpoint like below. But I can't access to metrics. invoke() method does not appear.
spring boot version: 2.2.6.RELEASE
#Controller
public class DashboardControllerImpl implements IDashboardController {
#Autowired
private MetricsEndpoint metricsEndpoint;
#Override
public ResponseEntity listDiskUsage() {
metricsEndpoint.invoke(); // invoke does not appear
// code block
}
}
I found some thing like that, that solved my problem!
((MetricsEndpoint.Sample)metricsEndpoint.metric("process.uptime", null).getMeasurements().get(0)).getValue()

Disabling HealtCheck for specific DataSources in Spring Actuator

i have issue with disabling specific DataSources in Spring Actuator. I currently have task in my application to implement Spring Actuator, but need Actuator to ignore/disable for some features in app(Health Indicator mainly). Application is built from other mini apps. Any suggestions or instructions how to start it ?
Disable the default datasources health indicator
management.health.db.enabled=false
and customise the your required data source with DataSourceHealthIndicator
example:
#Autowired
private DataSource requiredDataSource;
#Bean
public DataSourceHealthIndicator requiredDataSourceHealthIndicator() {
return new DataSourceHealthIndicator(requiredDataSource);
}
An important point to prevent the Health check system becomes fully none-functional due to infinite loop for waiting for database connection is to configure the connectionTimeout and validationTimeout in datasource config.
In the case that we using HikariCP as connection pool provider, with respect to the case in hand the implementation will look like below,
HikariConfig config = new HikariConfig();
...
config.setInitializationFailTimeout(1000);
config.setConnectionTimeout(1500);
config.setValidationTimeout(1500);
...
return new HikariDataSource(config).unwrap(DataSource.class)
#Bean
public DataSourceHealthIndicator dataSourceHealthIndicator(){
...
return new DataSourceHealthIndicator(dataSource, "SELECT 1");
}
#Component
#RequiredArgsConstructor
public class CustomHealth implements HealthIndicator {
#Override
public Health health() {
...
return Health.status(healthIndicator.health().getStatus()).build();
}
private final DataSourceHealthIndicator healthIndicator;
}
And as Bhushan Uniyal stated you can disable the actuator default DataSourceHealthIndicator in your property files
YML
management:
health:
db:
enabled: false
Properties
management.health.db.enabled: false

Categories