Netty is an asynchronous, event-driven network application framework that enables rapid development of maintainable, high-performance protocol-oriented servers and clients

Java Network Programming

Earlier Java apis only supported so-called blocking functions provided by the local system socket library, and the following code shows a common example of server code using traditional Java apis

// Create a ServerSocket to listen for connection requests on the specified port
ServerSocket serverSocket = new ServerSocket(5000);
// The call to the accept method will be blocked until a connection is established
Socket clientSocket = serverSocket.accept();
// These stream objects are derived from the socket's stream objects
BufferedReader in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream()));
PrintWriter out = new PrintWriter(clientSocket.getOutputStream(), true);
String request, response;
// The client sends "Done" to exit the loop
while((request = in.readLine()) ! =null) {
    if ("Done".equals(request)) {
        break;
    }
    // The processing method by which the request is passed to the server
    response = processRequest(request);
    // The server's response is sent to the client
    out.println(response);
}
Copy the code

This code can only handle one connection at a time. To manage multiple clients, we need to create a new Thread for each new client Socket. Let’s consider the impact of this scenario:

  • A large number of threads are dormant at any one time, wasting resources
  • Memory needs to be allocated for each thread’s call stack
  • Context switching of threads introduces overhead

This concurrency scheme is fine for a small number of clients, but does not work well for larger concurrency. Fortunately, there is another solution

Java NIO

NIO (non-blocking I/O, also called New I/O) is a synchronous non-blocking I/O model, which is also the basis of I/O multiplexing. Traditional IO streams are blocked, which means that when a thread invokes a read or write operation, the thread will block until the data has been fully read or written. NIO’s non-blocking mode allows a thread to do a read or write operation if no data is currently available, rather than keeping the thread blocked so it can continue doing other things until the data is ready

Class. Java nio. Channels. The Selector is the key to Java non-blocking IO implementation. It uses the event notification API to determine which of a set of non-blocking sockets are ready for IO related operations. Because read or write completion can be checked arbitrarily at any point in time, a single thread can handle multiple concurrent connections

This model provides better resource management than the blocking IO model:

  • Many connections can be handled with fewer threads, reducing the overhead of memory management and context switching
  • Threads can also use other tasks when there are no IO operations to handle

As efficient as Java NIO is, getting it right and secure is not easy, and reliably and efficiently processing and scheduling IO operations under high loads is a cumbersome and error-prone task, but Netty can help us out


Netty

Netty is a widely used Java network programming framework that hides the complexity behind Java’s advanced apis and provides a client/server framework with an easy-to-use API. Here we’ll discuss the main components of Netty:

1. Channel

A Channel, a basic construct of Java NIO, can be thought of simply as a carrier for incoming (inbound) or outgoing (outbound) data. Therefore, it can be turned on or off, connected or disconnected

2. The callback

A callback is actually a method. Netty uses a callback to handle events. When a callback is triggered, the associated event can be handled by ChannelHandler’s implementation. The following code shows an example: When a new connection has been established, the callback method of ChannelHandler’s channelActive() will be called and a message will be printed

public class ConnectHandler extends ChannelInboundHandlerAdapter {
    
    @override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("Client " + ctx.channel().remoteAddress() + " connected"); }}Copy the code

3. Future

A Future provides another way to notify the application when an action is complete, that it will be done at some point in the Future, and to provide access to its results. While Java provides an implementation of a Future, it is cumbersome to manually check that the corresponding action has completed, or to block until it does. Netty provides its own implementation, ChannelFuture, for use when performing asynchronous operations

ChannelFuture provides several additional methods that enable us to register one or more instances of ChannelFutureListener. The listener’s callback method operationComplete() will be called when the corresponding operation completes. Each Netty I/O operation returns a ChannelFuture, which is not blocked and can do other work at the same time, making more efficient use of resources

Channel channel = ... ;// Connect to the remote node asynchronously
ChannelFuture future = channel.connect(new InetSocketAddress("192.168.0.1".25));
Register a ChannelFutureListener
future.addListener(new ChannelFutureListener() {
    
    @Override
    public void operationComplete(ChannelFuture future) {
        // If the operation succeeds
        if(future.isSuccess()) {
            ...
        } else {
            // An exception occurred. }}});Copy the code

4. Events and ChannelHandler

Netty uses different events to trigger the appropriate action. Events are classified by their relevance to inbound or outbound data streams. Events that may be triggered by inbound data or associated state changes include:

  • The connection has been activated or failed
  • Data is read
  • User events
  • Error events

An outbound event is the result of an action that will be triggered in the future, including:

  • Open or close a connection to a remote node
  • Write or flush data to the socket

Each event can be distributed to a user-implemented method in the ChannelHandler class. The following figure shows how an event is handled by a ChannelHandler chain

Netty provides a number of predefined ChannelHandler implementations for developers to use

5. To summarize

Netty’s asynchronous programming model is based on the concept of futures and callbacks, sending events to channelHandlers to intercept and transform inbound and outbound data at high speed. Developers simply provide callbacks or use returned Futures. Netty abstracts the Selector from the application by triggering events, eliminating the need for hand-written distribution code. Internally, an EventLoop will be assigned to each Channel to handle all events, including:

  • Register for times of interest
  • Send the event to ChannelHandler
  • Schedule further action

EventLoop itself has a single thread driver that handles all IO events for a Channel. This simple and powerful design eliminates any concerns that synchronization might be required in the ChannelHandler implementation, so we can just focus on providing the right logic