Backend Development 10 min read

Understanding the Netty Server Startup Process

This article explains how Netty initializes and starts a server by detailing the creation of EventLoopGroups, the configuration of ServerBootstrap, the execution of channel(), handler(), childHandler(), and doBind() methods, and the handling of JDK selector bugs, illustrated with code and diagrams.

Java Captain
Java Captain
Java Captain
Understanding the Netty Server Startup Process

Netty is a Java NIO‑based network framework, and this article explores exactly when and how its server side is started, including the moments when register and bind are invoked and when data reading begins.

Using the official EchoServer example, the article first presents the complete startup code, which creates a boss EventLoopGroup , a worker EventLoopGroup , configures a ServerBootstrap , and finally binds the server to a port.

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();
ServerBootstrap b = new ServerBootstrap();
 b.group(bossGroup, workerGroup)
  .channel(NioServerSocketChannel.class)
  .option(ChannelOption.SO_BACKLOG, 100)
  .handler(new LoggingHandler(LogLevel.INFO))
  .childHandler(new ChannelInitializer
() {
      @Override
      public void initChannel(SocketChannel ch) throws Exception {
          ChannelPipeline p = ch.pipeline();
          p.addLast(new EchoServerHandler());
      }
  });
ChannelFuture f = b.bind(PORT).sync();

The startup process consists of five key stages, with the most critical step occurring at the final bind call (step 5). To aid understanding, the article provides flow diagrams for each stage.

1. Initializing EventLoopGroup – The EventLoopGroup constructors chain up to AbstractEventExecutor , creating the core executor objects needed for handling I/O events.

2. Executing channel() – This method creates a ReflectiveChannelFactory that later produces the actual Channel instances.

3. Executing handler() – Assigns a user‑provided ChannelHandler to the bootstrap’s handler field for later use during initialization.

4. Executing childHandler() – Assigns the child ChannelHandler that will process inbound data on accepted connections.

5. Executing doBind() – The most complex step, split into initAndRegister() and doBind0() . initAndRegister() creates a channel via the factory, initializes it, and registers it with a JDK Selector . doBind0() performs the actual port binding, starts the event‑loop thread, and repeatedly calls Selector.select() to handle I/O events.

The article also discusses the JDK epoll empty‑poll bug and shows how Netty mitigates it by rebuilding the Selector after a configurable number of consecutive empty selects. A snippet of the selector loop illustrates the detection logic:

private void select(boolean oldWakenUp) throws IOException {
    Selector selector = this.selector;
    try {
        int selectCnt = 0;
        long currentTimeNanos = System.nanoTime();
        long selectDeadLineNanos = currentTimeNanos + delayNanos(currentTimeNanos);
        for (;;) {
            long timeoutMillis = (selectDeadLineNanos - currentTimeNanos + 500000L) / 1000000L;
            if (timeoutMillis <= 0) {
                if (selectCnt == 0) {
                    selector.selectNow();
                    selectCnt = 1;
                }
                break;
            }
            int selectedKeys = selector.select(timeoutMillis);
            selectCnt++;
            if (selectedKeys != 0 || oldWakenUp || wakenUp.get() || hasTasks()) {
                break;
            }
            // Rebuild selector if empty‑poll threshold exceeded
            if (SELECTOR_AUTO_REBUILD_THRESHOLD > 0 && selectCnt >= SELECTOR_AUTO_REBUILD_THRESHOLD) {
                rebuildSelector();
                selector = this.selector;
                selector.selectNow();
                selectCnt = 1;
                break;
            }
        }
    } catch (CancelledKeyException e) {
        // log and continue – harmless JDK bug
    }
}

Finally, the article summarizes the complete startup sequence: creating EventLoopGroups and a selector, building a channel and its pipeline, setting handlers, registering the channel, binding to the port, and running the event‑loop that selects, rebuilds the selector when necessary, and dispatches read events to the child handler.

JavaNIONettyNetworkingServerEventLoop
Java Captain
Written by

Java Captain

Focused on Java technologies: SSM, the Spring ecosystem, microservices, MySQL, MyCat, clustering, distributed systems, middleware, Linux, networking, multithreading; occasionally covers DevOps tools like Jenkins, Nexus, Docker, ELK; shares practical tech insights and is dedicated to full‑stack Java development.

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.