Backend Development 21 min read

How Kafka’s Broker Handles Millions of Requests: Inside Its Network Architecture

This article deeply analyzes Kafka broker’s network architecture and request‑handling pipeline, walking through simple sequential models, multithreaded async designs, the Reactor pattern with Java NIO, key thread roles, core processing flow, and practical tuning parameters for high‑throughput, low‑latency deployments.

Sanyou's Java Diary
Sanyou's Java Diary
Sanyou's Java Diary
How Kafka’s Broker Handles Millions of Requests: Inside Its Network Architecture

01 Overview

Understanding Kafka broker request handling starts with the classic Request/Response model. Both producers and consumers communicate with the broker using this pattern, and the article evolves this simple model into Kafka’s actual network request processing architecture.

02 Sequential Processing Model

The most straightforward implementation is a single while‑loop that continuously accepts producer requests and writes them to disk. This approach suffers from two fatal drawbacks: request blocking (each request must wait for the previous one) and extremely low throughput, making it unsuitable for high‑concurrency workloads.

03 Multithreaded Asynchronous Model

To avoid blocking, Kafka can adopt a "connection per thread" model where each incoming request is handled by its own thread. This improves throughput and eliminates blocking, but the per‑thread overhead becomes prohibitive under heavy load.

04 Reactor Pattern

The solution is an event‑driven, multiplexed design based on the Reactor pattern. A single Acceptor thread registers OP_ACCEPT events on a Java NIO Selector , while multiple Processor threads handle OP_READ/OP_WRITE events. This architecture distributes load across threads and scales with CPU cores.

05 Kafka’s High‑Concurrency Network Architecture

The broker consists of two main components:

SocketServer : contains Acceptor, Processor threads and a RequestChannel; implements the Reactor pattern to accept and dispatch client connections. RequestHandlerPool : a pool of I/O worker threads that execute the actual request logic via KafkaApis and write data to disk.

Acceptor Thread

The Acceptor creates a NIO Selector , opens a ServerSocketChannel , registers OP_ACCEPT, and assigns incoming connections to Processor threads in a round‑robin fashion.

<code>/**
 * Thread that accepts and configures new connections. There is one of these per endpoint.
 */
private class Acceptor(val endPoint: EndPoint,
                      val sendBufferSize: Int,
                      val recvBufferSize: Int,
                      brokerId: Int,
                      connectionQuotas: ConnectionQuotas,
                      metricPrefix: String) extends AbstractServerThread(connectionQuotas) with KafkaMetricsGroup {
  private val nioSelector = NSelector.open()
  val serverChannel = openServerSocket(endPoint.host, endPoint.port)
  private val processors = new ArrayBuffer[Processor]()
  def run(): Unit = {
    serverChannel.register(nioSelector, SelectionKey.OP_ACCEPT)
    startupComplete()
    try {
      var currentProcessorIndex = 0
      while (isRunning) {
        try {
          val ready = nioSelector.select(500)
          if (ready > 0) {
            // accept connections and assign to processors
            accept(key).foreach { socketChannel =>
              val processor = synchronized { processors(currentProcessorIndex) }
              currentProcessorIndex += 1
            }
          }
        } catch { case _: Exception => }
      }
    } finally { shutdownComplete.countDown() }
  }
}
</code>

Processor Thread

Processor threads manage three queues: newConnections (pending sockets), inflightResponses (temporary responses), and responseQueue (final responses to clients). They poll the selector for ready I/O events, read requests, and write responses.

<code>override def run(): Unit = {
  startupComplete()
  try {
    while (isRunning) {
      try {
        configureNewConnections()
        processNewResponses()
        poll()
        processCompletedReceives()
      } catch { case _: Exception => }
    }
  } finally { /* cleanup */ }
}
</code>

06 Core Request‑Handling Flow

Clients send requests to the Acceptor.

Acceptor registers OP_ACCEPT on the selector and creates a ServerSocketChannel.

Acceptor creates a pool of Processor threads (controlled by num.network.threads ) and enqueues new connections.

Processor threads poll for OP_READ/OP_WRITE events, read request data, and place Request objects into the RequestChannel queue.

KafkaRequestHandler threads (size num.io.threads ) consume requests, invoke KafkaApis.handle , and write data to disk.

Responses are placed into the Processor’s response queue and sent back to the client.

07 System Tuning

Key broker parameters for high performance:

num.network.threads : set to 2 × CPU cores.

num.io.threads : set to 2 × disk count.

num.replica.fetchers : set to CPU / 4.

compression.type : use lz4 for better CPU utilization and reduced network traffic.

queued.max.requests : configure ≥ 500 for production.

Leave log flush settings at defaults to let the OS manage disk writes.

auto.leader.rebalance.enable : disable to avoid unpredictable load spikes.

08 Summary

The article demonstrates that Kafka’s broker achieves high availability, performance, and concurrency by evolving from a simple sequential model to an event‑driven Reactor architecture using Java NIO, multiple selectors, and dedicated thread pools. Understanding these components enables effective performance tuning and reliable large‑scale deployments.

network architectureBackend DevelopmentKafkaperformance tuningReactor PatternJava NIO
Sanyou's Java Diary
Written by

Sanyou's Java Diary

Passionate about technology, though not great at solving problems; eager to share, never tire of learning!

0 followers
Reader feedback

How this landed with the community

login Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.