I have written CustomHealthIndicator to check the producer binding is running, if not then Pause the Consumer binding. We have implemented the application using Spring-cloud-streams-kafka-binder in functional style. Below is the snippet of code doing that:
import org.springframework.boot.actuate.health.Health;
import org.springframework.boot.actuate.health.HealthIndicator;
import org.springframework.boot.availability.AvailabilityChangeEvent;
import org.springframework.boot.availability.ReadinessState;
import org.springframework.cloud.stream.binder.Binding;
import org.springframework.cloud.stream.binding.BindingsLifecycleController;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;
import lombok.extern.slf4j.Slf4j;
@Slf4j
@Component
public class BindingHealthIndicator implements HealthIndicator {
private final ApplicationContext context;
private final ApplicationEventPublisher eventPublisher;
private String PRODUCER_ERROR_MESSAGE = "The Src topic is unavailable";
private String CONSUMER_ERROR_MESSAGE = "The Dest topic is unavailable";
private String SUCCESS_MESSAGE = "The Consumer and producer topics are Healthy";
public BindingHealthIndicator(ApplicationContext context, ApplicationEventPublisher eventPublisher) {
this.context = context;
this.eventPublisher = eventPublisher;
}
@Override
public Health health() {
BindingsLifecycleController bindingsController = context.getBean(BindingsLifecycleController.class);
Boolean producerBindingStatus;
try {
Binding producerBinding = bindingsController.queryState("consumeAndProcessAllAccountUpdate-out-0");
producerBindingStatus = producerBinding.isRunning();
} catch(Exception ex) {
bindingsController.changeState("consumeAndProcessAllAccountUpdate-in-0", BindingsLifecycleController.State.PAUSED);
AvailabilityChangeEvent.publish(this.eventPublisher, PRODUCER_ERROR_MESSAGE, ReadinessState.REFUSING_TRAFFIC);
log.error(PRODUCER_ERROR_MESSAGE);
return Health.down().withDetail("description", PRODUCER_ERROR_MESSAGE).build();
}
Binding consumerBinding = bindingsController.queryState("consumeAndProcessAllAccountUpdate-in-0");
Boolean consumerBindingStatus = consumerBinding.isRunning();
if(!(producerBindingStatus)) {
log.error(PRODUCER_ERROR_MESSAGE);
bindingsController.changeState("consumeAndProcessAllAccountUpdate-in-0", BindingsLifecycleController.State.PAUSED);
AvailabilityChangeEvent.publish(this.eventPublisher, PRODUCER_ERROR_MESSAGE, ReadinessState.REFUSING_TRAFFIC);
return Health.down().withDetail("description", PRODUCER_ERROR_MESSAGE).build();
}
if(!(consumerBindingStatus)) {
log.error(CONSUMER_ERROR_MESSAGE);
AvailabilityChangeEvent.publish(this.eventPublisher, CONSUMER_ERROR_MESSAGE, ReadinessState.REFUSING_TRAFFIC);
return Health.down().withDetail("description", CONSUMER_ERROR_MESSAGE).build();
}
bindingsController.changeState("consumeAndProcessAllAccountUpdate-in-0", BindingsLifecycleController.State.RESUMED);
AvailabilityChangeEvent.publish(this.eventPublisher, SUCCESS_MESSAGE, ReadinessState.ACCEPTING_TRAFFIC);
return Health.up().withDetail("description", SUCCESS_MESSAGE).build();
}
}
I want to simulate through Unit testing that the producer binder to be having issues or to have the isRunning return false, and check if consumer is PAUSED, could you please suggest the correct approach to be followed?