advanced~2h

Health, Monitoring & Observability

A service that's running isn't the same as a service that's actually able to do its job. This module builds the health signal that Module 20's Kubernetes probes will later consume.

Learning objectives

  • Beginner: Explain the difference between a service that's "running" and one that's actually able to do its job.
  • Intermediate: Wire up Spring Boot Actuator's Kafka readiness health indicator for a producer or consumer service.
  • Advanced: Design health checks so a Kubernetes readiness probe (Module 20) correctly stops routing traffic to a broker-disconnected instance.

Actuator exposes operational endpoints (/actuator/health, /actuator/metrics, etc.) over HTTP with almost no code — it's the foundation every later observability/orchestration integration in this site builds on.

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

💻 Code example

<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency>

◆ The problem

A Spring Boot process can be fully "up" — accepting HTTP traffic, JVM healthy — while its connection to Kafka is broken (wrong broker address, network partition, authentication failure). A generic "is the process alive" check can't see that.

Here's the part that surprises people coming from other Spring Boot auto-configured health indicators (database, Redis): Spring Boot does NOT ship a built-in health indicator that meaningfully checks Kafka broker connectivity out of the box. You have to write one yourself — a small, genuinely useful amount of code, using AdminClient.describeCluster() to actually confirm the broker is reachable:

@Component public class KafkaHealthIndicator implements HealthIndicator { private final AdminClient adminClient; public KafkaHealthIndicator(AdminClient adminClient) { this.adminClient = adminClient; } @Override public Health health() { try { adminClient.describeCluster().nodes().get(3, TimeUnit.SECONDS); return Health.up().build(); } catch (Exception e) { return Health.down(e).build(); } } }

Once this bean exists, Spring Boot Actuator picks it up automatically (any HealthIndicator bean is auto-registered) and it's surfaced under Actuator's readiness health group — separate from basic liveness, deliberately, because "the JVM is up" and "Kafka is reachable" are different questions with different correct responses (see the liveness/readiness distinction fully developed in Module 20).

management: endpoint: health: probes: enabled: true group: readiness: include: readinessState,kafka
curl localhost:8080/actuator/health/readiness {"status":"UP","components":{"kafka":{"status":"UP"}}}

💻 Code example

@Component public class KafkaHealthIndicator implements HealthIndicator { private final AdminClient adminClient; public KafkaHealthIndicator(AdminClient adminClient) { this.adminClient = adminClient; } @Override public Health health() { try { adminClient.describeCluster().nodes().get(3, TimeUnit.SECONDS); return Health.up().build(); } catch (Exception e) { return Health.down(e).build(); } } }

Both library-events-producer and library-events-consumer get identical Actuator + Kafka readiness configuration — the producer needs to know it can reach a broker to publish; the consumer needs to know it's actively connected before Kubernetes routes it real traffic once deployed (Module 20).

✓ Quick recap

Why isn't a plain "process is running" check sufficient for a Kafka-backed service? The process can be alive while its Kafka connection is broken — readiness needs to check the dependency, not just the process.

Want a visual for this concept?

Generate a diagram tailored to “Health, Monitoring & Observability” — the AI picks whichever visual (flowchart, comparison, sequence, etc.) best fits.

Sign in to generate a visual →

Practice quiz

Next Step

Continue to Packaging & Running Standalone← Back to all Kafka & Microservices chapters