Java Fundamentals Tutorial 0/33 lessons ~6 min read Lesson 26

    Kafka

    Apache Kafka is the event streaming platform behind LinkedIn, Uber, Netflix, and most Java microservices architectures.

    Course progress0%
    Focus
    21 guided sections
    Practice signal
    Examples included
    Career prep
    Interview Q&A included

    Introduction

    Apache Kafka is the event streaming platform behind LinkedIn, Uber, Netflix, and most Java microservices architectures. Producers publish records to topics partitioned for parallelism; consumers in consumer groups read with at-least-once or exactly-once semantics.

    Java engineers interact with Kafka via spring-kafka, the official kafka-clients library, or Kafka Streams. This lesson covers production fundamentals: partition strategy, consumer group rebalancing, offset management, and exactly-once processing with idempotent producers and transactional APIs.

    Business problem

    Why synchronous microservices fail at scale:

    • Cascade failures: Order service synchronously calls inventory, payment, notification — one slow service blocks all.
    • Peak load: Black Friday order spike — HTTP thread pools exhaust; events buffer in Kafka instead.
    • Audit and replay: Regulators require event log — REST calls leave no durable history.
    • Multiple consumers: Same OrderCreated event feeds inventory, analytics, fraud — REST requires fan-out from producer.
    • Decoupled deploy: New fraud service subscribes to existing topic — zero changes to order producer.

    Why this topic exists

    Kafka solves the "n×m integration problem" — n producers × m consumers without point-to-point wiring:

    • Log abstraction: Durable append-only log — producers write; consumers read at their pace with offset bookmarks.
    • Partitions: Parallelism — topic split into ordered partitions; scale consumers up to partition count.
    • Consumer groups: Each partition consumed by one consumer in group — horizontal scale with automatic rebalancing.
    • Retention: Events replayable for days — new consumer rebuilds state from history.
    • Exactly-once: Idempotent producer + transactions — payment events processed once despite retries.

    Core concepts

    Kafka core concepts for Java developers:

    • Producer: Sends records to topic partition — key determines partition (hash(key) % numPartitions).
    • Consumer: Polls batches from assigned partitions; commits offset after processing.
    • Partition: Ordered sequence within partition — no global order across partitions.
    • Consumer Group: Group ID — Kafka assigns each partition to one consumer; add consumer → rebalance.
    • Offset: Position in partition log — auto-commit vs manual commit for at-least-once control.
    • Exactly-once: enable.idempotence=true + transactional producer + read_committed consumer isolation.

    Internal architecture

    Kafka cluster topology for Java services:

    text
    Topic: order.events (6 partitions, replication-factor 3)
    Producer (Order Service) Consumer Group: inventory-workers
    │ key=orderId ├─ Consumer 1 → P0, P1
    ▼ ├─ Consumer 2 → P2, P3
    ┌─────────┐ ┌─────────┐ └─ Consumer 3 → P4, P5
    │ Broker 1│ │ Broker 2│ ┌─────────┐
    │ P0 L │ │ P1 L │ │ Broker 3│ Consumer Group: analytics
    │ P1 F │ │ P2 L │ │ P0 F │ └─ Consumer 1 → all partitions
    └─────────┘ │ P3 L │ │ P2 F │ (separate group = independent read)
    └─────────┘ └─────────┘
    Exactly-once flow:
    producer.initTransactions()
    producer.beginTransaction()
    producer.send(orderEvent)
    producer.sendOffsetsToTransaction(offsets, consumerGroupMetadata)
    producer.commitTransaction()

    Producer → partition → consumer group flow:

    Producer to partition routing
    Producer
    Order service
    Key hash
    orderId → partition
    Partitions
    P0…P5 ordered
    Replicas
    RF=3 durability
    Same key → same partition — ordering per order ID guaranteed.
    Consumer group assignment
    Group A
    3 consumers
    6 partitions
    2 each
    Rebalance
    On join/leave
    Offset commit
    After process
    Max consumers in group = partition count — extra consumers idle.
    Exactly-once semantics
    Idempotent
    PID + sequence
    Transaction
    Atomic multi-send
    read_committed
    Consumer filter
    EOS
    Process + publish
    Transactional API ties offset commit to output publish — no duplicates.

    Code walkthrough

    Spring Kafka producer and consumer — order events with manual ack:

    • ENABLE_IDEMPOTENCE: Broker deduplicates retries via producer ID and sequence numbers.
    • Key = orderId: All events for one order land in same partition — ordered processing.
    • Manual ack: Offset committed only after DB write succeeds — at-least-once without data loss on crash.
    • groupId: Multiple inventory service instances share load — one partition per consumer max.
    java
    @Configuration
    public class KafkaConfig {
    @Bean
    public ProducerFactory<String, OrderEvent> producerFactory() {
    Map<String, Object> props = new HashMap<>();
    props.put(ProducerConfig.BOOTSTRAP_SERVERS_CONFIG, "kafka:9092");
    props.put(ProducerConfig.KEY_SERIALIZER_CLASS_CONFIG, StringSerializer.class);
    props.put(ProducerConfig.VALUE_SERIALIZER_CLASS_CONFIG, JsonSerializer.class);
    props.put(ProducerConfig.ENABLE_IDEMPOTENCE_CONFIG, true); // exactly-once producer
    props.put(ProducerConfig.ACKS_CONFIG, "all");
    return new DefaultKafkaProducerFactory<>(props);
    }
    }
    @Service
    public class OrderEventPublisher {
    private final KafkaTemplate<String, OrderEvent> kafka;
    public void publish(OrderEvent event) {
    // key = orderId ensures all events for order go to same partition
    kafka.send("order.events", event.orderId(), event);
    }
    }
    @Component
    public class InventoryConsumer {
    @KafkaListener(topics = "order.events", groupId = "inventory-workers",
    containerFactory = "manualAckFactory")
    public void onOrderCreated(OrderEvent event, Acknowledgment ack) {
    reserveStock(event.orderId(), event.items());
    ack.acknowledge(); // commit offset after successful processing
    }
    }

    Production example

    Exactly-once consume-process-produce with KafkaTransactions:

    • transaction-id-prefix: Enables transactional producer — begin/commit around listener.
    • read_committed: Consumers skip messages from aborted transactions.
    • EOS listener: Spring Kafka ties consumer offset to outgoing publish in one transaction.
    • Trade-off: Higher latency than at-least-once — use for payment/fraud, not analytics.
    java
    @Bean
    public ConcurrentKafkaListenerContainerFactory<String, OrderEvent> eosFactory(
    ConsumerFactory<String, OrderEvent> cf,
    KafkaTemplate<String, ProcessedEvent> template) {
    var factory = new ConcurrentKafkaListenerContainerFactory<String, OrderEvent>();
    factory.setConsumerFactory(cf);
    factory.getContainerProperties().setAckMode(AckMode.RECORD);
    factory.setKafkaTemplate(template); // enables EOS listener
    return factory;
    }
    @KafkaListener(topics = "order.events", groupId = "fraud-check",
    containerFactory = "eosFactory")
    public void process(OrderEvent event) {
    FraudScore score = fraudEngine.score(event);
    // Transactional: offset commit + output publish atomic
    kafkaTemplate.send("fraud.results", event.orderId(), new FraudResult(score));
    }
    // application.yml
    spring.kafka:
    producer:
    transaction-id-prefix: fraud-tx-
    consumer:
    isolation-level: read_committed // skip aborted transactions

    Enterprise case study

    LinkedIn — Kafka origin: LinkedIn built Kafka to handle real-time activity feeds — billions of events/day. Java services produce page views, job applications, messaging events. Key lesson: partition count is a capacity planning decision — increasing partitions later is painful; plan for peak consumer throughput upfront.

    • Before: Point-to-point queues — adding consumer required producer changes.
    • After: Kafka log — unlimited consumers at their own pace; 7-day retention for replay.
    • Java stack: Kafka Streams for aggregations; spring-kafka for service integration.
    • Ops: Monitor consumer lag — alert when lag > 10K messages for payment topics.

    Performance considerations

    Kafka performance tuning for Java:

    • Batching: linger.ms=5, batch.size=32KB — amortize network round-trips.
    • Compression: lz4 or zstd on producer — CPU for bandwidth trade-off worth it.
    • Partition count: Start with #partitions ≥ peak consumer instances; order key avoids hot partitions.
    • Consumer poll: max.poll.records and processing time < max.poll.interval.ms — avoid rebalance storm.
    • Serializers: Avro/Protobuf + Schema Registry — smaller payloads than JSON at scale.

    Security considerations

    Kafka security for Java clients:

    • SASL/SCRAM or mTLS: Authenticate producers/consumers — never plaintext in production.
    • ACLs: Order service WRITE on order.events only — principle of least privilege.
    • No PII in keys: Keys logged in metrics — use opaque order ID not email.
    • Encryption at rest: Broker disk encryption + TLS in transit.

    Scalability considerations

    Scaling Kafka consumers and producers:

    • Horizontal consumers: Add pods until consumer count = partition count — then add partitions (requires planning).
    • Multiple consumer groups: Inventory and analytics both read order.events — independent scale.
    • Broker scale: Add brokers + rebalance partitions — use cruise control for automation.
    • Compacted topics: changelog topics for Kafka Streams state — retention by key, not time.

    Production challenges

    Real Kafka production issues:

    • Consumer lag: Slow handler or insufficient consumers — scale or optimize processing.
    • Rebalance storm: Processing exceeds max.poll.interval.ms — consumer kicked, partitions reshuffled, duplicate processing.
    • Hot partitions: Null key or skewed key distribution — one partition overloaded.
    • Poison messages: Deserialization failure — DLQ pattern with @RetryableTopic (Spring Kafka 2.7+).
    • Schema evolution: Avro backward compatibility — new fields optional, never remove required fields.

    Common mistakes

    • Auto-commit before processing completes — message lost on crash after commit.
    • More consumers than partitions — idle consumers waste resources; no speedup.
    • No message key — round-robin partitioning loses per-entity ordering.
    • Ignoring consumer lag alerts — backlog grows until retention deletes unprocessed messages.
    • Exactly-once everywhere — unnecessary latency; at-least-once + idempotent handler often sufficient.

    Debugging guide

    Debug Kafka issues in Java services:

    • Consumer lag: kafka-consumer-groups.sh --describe --group inventory-workers.
    • Log correlation: Enable logging.level.org.apache.kafka=INFO — watch rebalance events.
    • Trace propagation: Inject traceparent in record headers — link producer span to consumer span.
    • Stuck consumer: Check max.poll.interval.ms vs handler duration — thread dump on consumer pod.
    bash
    # Consumer group lag
    kafka-consumer-groups.sh --bootstrap-server kafka:9092 \
    --describe --group inventory-workers
    # Inspect topic messages
    kafka-console-consumer.sh --bootstrap-server kafka:9092 \
    --topic order.events --from-beginning --max-messages 5
    # List partitions and leaders
    kafka-topics.sh --describe --topic order.events --bootstrap-server kafka:9092

    Best practices

    • Use message keys for entities needing ordered processing (orderId, userId).
    • Set acks=all and min.insync.replicas=2 for durability.
    • Enable idempotent producer — free dedup on retries.
    • Manual commit or transactional listener after side effects succeed.
    • Monitor consumer lag per group — primary health metric.
    • Use Schema Registry (Avro) for production topics — not raw JSON.
    • Implement DLQ for poison messages — don't block partition indefinitely.

    Anti-patterns

    • Using Kafka as job queue without keys — ordering and fairness break down.
    • Giant messages (>1MB) — use S3 reference + event with pointer; Kafka for metadata.
    • Single partition topic for high throughput — bottleneck; no consumer parallelism.
    • Sync blocking send in request thread — use async callback or separate executor.
    • Consumer doing heavy work in poll thread — blocks heartbeat; use async processing with pause/resume.

    Staff engineer notes

    • Staff engineers default to at-least-once + idempotent consumer — exactly-once only when financial correctness demands it.
    • Partition count is forever-ish — changing it reorders keys; plan 2–3× expected peak consumer count.
    • Consumer group ID is a deployment identity — never reuse group ID for different logic (reads wrong offsets).
    • Kafka is not a database — retention expires data; use compacted topics or external store for source of truth.
    • Spring @KafkaListener is production-ready — but understand what it wraps: poll loop, ack, rebalance listener.

    Interview questions

    Interview preparation

    15 questions grouped by difficulty — expand any card for a model answer and follow-up probe.

    Beginner

    5
    1. 1What is Apache Kafka?
      Beginner

      Model answer

      Distributed event streaming platform.

      Producers append records to topics split into partitions.

      Consumers read via pull model with offset tracking.

      Durable, replayable, high-throughput log abstraction.

      Follow-up probe

      vs RabbitMQ?

    2. 2What is a Kafka partition?
      Beginner

      Model answer

      • Ordered, immutable sequence of records within a topic. Parallelism unit
      • each partition hosted on one broker leader. Records within partition strictly ordered; no order guarantee across partitions.

      Follow-up probe

      How choose partition count?

    3. 3What is a consumer group?
      Beginner

      Model answer

      id.

      Kafka assigns each partition to exactly one consumer in group.

      Scale consumers up to partition count.

      Rebalance on consumer join/leave.

      Follow-up probe

      Two groups same topic?

    4. 4How does a Kafka producer work?
      Beginner

      Model answer

      Serializes key/value, determines partition (key hash or round-robin), sends batch to broker leader.

      Waits for ack per acks config (0, 1, all).

      Retries on transient failure; idempotent producer deduplicates.

      Follow-up probe

      What is acks=all?

    5. 5What is a consumer offset?
      Beginner

      Model answer

      Pointer to last consumed position in partition.

      Stored in __consumer_offsets topic.

      Auto-commit periodically or manual commit after processing.

      Enables resume after restart.

      Follow-up probe

      Commit before or after process?

    Intermediate

    5
    1. 6Explain at-least-once vs exactly-once.
      Intermediate

      Model answer

      • At-least-once: may redeliver on failure
      • handler must be idempotent. Exactly-once: idempotent producer + transactional API + read_committed
      • broker guarantees no duplicates within transaction scope. EOS in Kafka is end-to-end with external systems only with transactional outbox pattern.

      Follow-up probe

      Is EOS truly exactly-once?

    2. 7What triggers consumer rebalance?
      Intermediate

      Model answer

      • Consumer joins/leaves group, session timeout (missed heartbeat), max.poll.interval exceeded (processing too slow), partition count change. During rebalance consumption pauses
      • minimize with cooperative sticky assignor.

      Follow-up probe

      Avoid rebalance storm?

    3. 8Why use message keys?
      Intermediate

      Model answer

      • Same key always goes to same partition
      • preserves ordering per entity (all order-123 events ordered). Without key, round-robin
      • no per-entity order. Key also affects load distribution.

      Follow-up probe

      Hot key problem?

    4. 9How implement idempotent consumer in Java?
      Intermediate

      Model answer

      • Store processed message ID (event ID or offset+partition) in DB before ack. On redelivery, check if already processed
      • skip. Or use natural idempotency (UPSERT by business key). At-least-once + idempotent handler = effective exactly-once.

      Follow-up probe

      Where store dedup state?

    5. 10Spring Kafka @KafkaListener internals?
      Intermediate

      Model answer

      Wraps KafkaConsumer poll loop in thread.

      Deserializes record, invokes method, commits offset per AckMode.

      Concurrent listeners = one thread per partition max typically.

      Error handlers route to retry/DLQ.

      Follow-up probe

      AckMode values?

    Advanced

    5
    1. 11Design order pipeline with Kafka.
      Advanced

      Model answer

      events (12 partitions, key=orderId).

      Order service produces OrderCreated.

      Inventory group consumes, reserves stock, produces InventoryReserved or Failed.

      Payment consumes, charges, produces PaymentCompleted.

      Saga via events.

      DLQ for poison.

      Lag alerts.

      Avro schemas.

      Follow-up probe

      Orchestration vs choreography?

    2. 12When NOT to use Kafka?
      Advanced

      Model answer

      Low volume request-reply, task needing immediate single response, small team without ops capacity.

      Use REST/RabbitMQ for simple queues.

      Kafka shines at high throughput, replay, multiple consumers, event sourcing.

      Follow-up probe

      Kafka vs event bus?

    3. 13How handle schema evolution?
      Advanced

      Model answer

      Avro/Protobuf with Schema Registry.

      Backward compatible changes: add optional fields with defaults.

      Never delete required fields.

      Consumers with old schema read new data.

      CI validates compatibility.

      Follow-up probe

      Breaking change strategy?

    4. 14Explain Kafka transactions.
      Advanced

      Model answer

      id.

      beginTransaction → send records → sendOffsetsToTransaction (consumer offsets) → commitTransaction.

      Atomic: all sends visible or none.

      Consumer isolation read_committed skips aborted.

      Follow-up probe

      Performance cost?

    5. 15Partition reassignment impact?
      Advanced

      Model answer

      • Adding partitions changes key→partition mapping for new keys only (sticky partitioner helps). Increasing partitions does not split existing data
      • old records stay on old partitions. Consumers rebalance. Plan partition count upfront; use cruise control for broker balance.

      Follow-up probe

      Can decrease partitions?

    Hands-on exercise

    Lab: Kafka concepts in Java:

    • Run playground — observe partition assignment simulation output.
    • Add PaymentFailed event to sealed hierarchy — update handler switch.
    • Write pseudo @KafkaListener method with manual ack comment.
    • Calculate: 6 partitions, 4 consumers — how many idle consumers?
    • Bonus: explain when you'd choose exactly-once vs at-least-once for payment events.

    JavaApache Kafka for Java

    Starter Templates
    OutputRemote JVM (Piston · Java 15)
    Press Run to execute. Real javac + JVM via remote API. Falls back to simulator on network failure.

    Architecture trade-offs

    • At-least-once vs exactly-once: At-least-once wins simplicity; exactly-once wins financial correctness at latency cost.
    • Kafka vs RabbitMQ: Kafka wins throughput and replay; RabbitMQ wins routing flexibility and low-latency task queues.
    • JSON vs Avro: JSON wins dev speed; Avro wins payload size and schema governance.
    • Choreography vs orchestration: Kafka events enable choreography; orchestrator adds visibility at coupling cost.

    Summary

    Kafka is the nervous system of Java microservices — master producers, consumers, partitions, consumer groups, and exactly-once before wiring Spring @KafkaListener in production. Plan partition count early, key your messages, and treat consumer lag as a page-worthy alert.

    Key takeaways

    • Topics + partitions = parallelism; keys preserve per-entity ordering.
    • Consumer groups scale consumption — max one consumer per partition per group.
    • Idempotent producer + manual ack = production baseline.
    • Exactly-once for money paths; at-least-once + idempotent handler elsewhere.
    • Monitor consumer lag — primary Kafka health metric.
    Ready to mark this lesson complete?Track your journey across the entire course.