Thanks for sharing your insights, however, I noticed a potential oversight that could lead to some confusion. Despite it’s been a while, I’d like to build on your answer by sharing some thoughts that may enhance its clarity and usefulness.
If the goal was to compare a non-blocking server implementation against netty, just replying with a constant string to every client’s request is not enough to reproduce any real-world load. In this case the efforts taken to produce a payload are comparable to (or less than) the efforts to sustain communication, so the testing can go either way and its results can be flaky. Moreover, when you make the server close the connection after each response, you end up benchmarking how quickly the OS kernel sets up TCP connections.
Next, let’s take a close look at two solutions provided in the answer. The initial one that contains blocking code uses Java NIO Selector and Selectable channel API. The alternate “highly performant” solution uses Java NIO.2 Asynchronous channel API. That might be confusing and can lead to the incorrect conclusion that the performance is gained by adopting NIO.2 API instead of NIO.
If we look closer at the implementation of both APIs (I arbitrarily choose the JDK 17, but you can pick another modern JDK version), we’ll see that they both use epoll under the hood:
These API introduce different programming models, but there is no strict reason pointing to one should be much more performant than another. Roughly speaking, NIO.2 with its asynchronous channels doesn’t rely on the OS async file IO (like Linux AIO or else). It uses epoll with a thread pool to emulate asynchronous file operations. Nothing stops you from using the same approach. If you introduce a thread pool to the first server implementation that uses Selector API and utilize it for request processing, the results will be comparable or even better than the second “highly performant” implementation.
Hope this helps provide a more comprehensive view.