Netty event monitoring and processing (below)

The last article introduced the basic concepts of event monitoring, responsibility chain model, socket interface, IO model, thread model, etc., as well as the overall structure of Netty. This article will talk about one of the three core modules of Netty: event monitoring and processing.

As mentioned earlier, Netty is a NIO framework, which abstracts the status changes such as the establishment, readability, and writability of IO channels into events, and transfers them in a chain of responsibility. Events of interest are monitored and processed.

Through the introduction, you will learn:

  • Event listening and processing model
  • Event listener: EventLoop
  • Event handling: ChannelPipeline and ChannelHandler
  • Implementing the Websocket protocol using Netty

There are benefits at the end of the article~

Event listening and processing model

When doing network programming, the general writing process is as follows:

  • Create a server socket and listen to a port;
  • When there is a client connection, a new client Socket will be created to monitor the readable and writable status of the data, and a client Socket will be created for each connection request;
  • Reading and writing data will call the interface provided by Socket. The interface list is mentioned in the previous article;

In the traditional model, each client socket will create a separate thread to monitor socket events. On the one hand, the system can create a limited number of threads, which limits the number of concurrency. On the other hand, there are too many threads and frequent thread switching, resulting in serious performance degradation.

With the development of the IO model of the operating system, multiplexing IO can be used, and one thread monitors multiple Sockets. In addition, the server handles the client connection and monitors the client Socket, which can be processed in different threads.

Netty uses multiplexed IO for event monitoring. In addition, different threads are used to handle client connections and data read and write.

The entire processing structure is shown in the figure below, briefly explained:

  • The Boss EventLoopGroup mainly handles the client's connect event, including multiple EventLoops, one thread per EventLoop;
  • The Worker EventLoopGroup mainly handles the data read and write events of the client Socket, including multiple EventLoops, and each EventLoop has a thread;
  • Whether it is Boos or Worker, the processing of events is organized through the Channel Pipeline, which is an implementation of the responsibility chain pattern, including one or more Handlers;
  • Listening to a port will only bind to one Eventloop in the Boss EventLoopGroup;
  • An Eventloop in the Worker EventLoopGroup can monitor multiple client sockets;

Event listening and processing model

EventLoop

An EventLoop is actually bound to a specific thread, and the bound thread will not change during its life cycle.

EventLoop has two tasks:

  • The first is as an IO thread to perform IO operations related to Channel, including calling select to wait for ready IO events, reading and writing data and data processing, etc.;
  • The second task is used as a task queue to execute the tasks in the taskQueue. For example, the timed task submitted by the user calling eventLoop.schedule is also executed by this thread;

The first task is easy to understand, and the second one is mainly explained: from socket data to data processing, to writing response data, Netty is processed in one thread, mainly for thread safety considerations, reducing competition and thread switching, Through the task queue, the processing logic can be submitted in the user thread and executed in the Eventloop.

What the whole EventLoop does is select -> processIO -> runAllTask, processIO handles the logic related to IO events, and runAllTask ​​handles the tasks in the task queue. If too many tasks are executed, it will affect the processing of IO events, so it will limit the task processing. Time, the whole process is as follows:

EventLoop processing

The run code of EventLoop is as follows:

protected void run() {
     for (; ; ) {
         oldWakenUp = wakenUp.getAndSet(false);
         try {
             if (hasTasks()) { //如果有任务,快速返回
                 selectNow();
             } else {
                 select(); //如果没任务,等待事件返回
                 if (wakenUp.get()) {
                     selector.wakeup();
                 }
             }
             cancelledKeys = 0;
             final long ioStartTime = System.nanoTime();
             needsToSelectAgain = false;

             //处理IO事件
             if (selectedKeys != null) {
                 processSelectedKeysOptimized(selectedKeys.flip());
             } else {
                 processSelectedKeysPlain(selector.selectedKeys());
             }

             //计算IO处理时间
             final long ioTime = System.nanoTime() - ioStartTime;
             final int ioRatio = this.ioRatio; //默认为50

             //处理提交的任务
             runAllTasks(ioTime * (100 - ioRatio) / ioRatio);

             if (isShuttingDown()) {
                 closeAll();
                 if (confirmShutdown()) {
                     break;
                 }
             }
         } catch (Throwable t) {
             try {
                 Thread.sleep(1000);
             } catch (InterruptedException e) {
             }
         }
     }
 }

ChannelPipeline和ChannelHandler

ChannelPipeline is an interface that has a default implementation class DefaultChannelPipeline with two properties: head and tail, both of which implement the ChannelHandler interface, corresponding to the head and tail of the processing chain.

 protected DefaultChannelPipeline(Channel channel) {
     this.channel = ObjectUtil.checkNotNull(channel, "channel");
     succeededFuture = new SucceededChannelFuture(channel, null);
     voidPromise =  new VoidChannelPromise(channel, true);

     tail = new TailContext(this);
     head = new HeadContext(this);

     head.next = tail;
     tail.prev = head;
}

When each Channel is created, a ChannelPipeline object is created to handle various events of the channel, and the ChannelHandler can be dynamically modified at runtime.

Where the ChannelHandler carries the business processing logic, we have the most contact with the class, you can customize the Handler, add it to the processing chain, and implement custom logic.

ChannelHandler can be divided into two categories: ChannelInboundHandler and ChannelOutboundHandle, these two interfaces correspond to the processing of inbound and outbound messages, corresponding to data reading and data writing. It provides interface methods for us to implement and handle various events.

public interface ChannelInboundHandler extends ChannelHandler {
	void channelRegistered(ChannelHandlerContext ctx) throws Exception;
	void channelUnregistered(ChannelHandlerContext ctx) throws Exception;
	void channelActive(ChannelHandlerContext ctx) throws Exception;
	void channelInactive(ChannelHandlerContext ctx) throws Exception;
	void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception;
	void channelReadComplete(ChannelHandlerContext ctx) throws Exception;
	void userEventTriggered(ChannelHandlerContext ctx, Object evt) throws Exception;
	void channelWritabilityChanged(ChannelHandlerContext ctx) throws Exception;
}

When customizing Handler, it generally inherits ChannelInboundHandlerAdapter or ChannelOutboundHandlerAdapter.

It should be noted that it is not recommended to directly implement time-consuming or blocking operations in ChannelHandler, because this may block the Netty worker thread, resulting in Netty unable to respond to IO processing in time.

ChannelPipline

Implementing the Websocket protocol using Netty

Websocket protocol

It's not the focus of this article, just briefly explain:

  • It is a long connection protocol, which is supported by most browsers. Through websocket, the server can actively send messages to the client;
  • The Websocket protocol uses the HTTP protocol in the handshake phase. After the handshake is completed, the Websocket protocol is used;
  • Websocket is a binary protocol;
initialization

Netty provides the ChannelInitializer class for us to initialize, create the WebSocketServerInitializer class, inherit the ChannelInitializer class, and add ChannelHandler:

public class WebSocketServerInitializer extends ChannelInitializer<SocketChannel> {

	@Resource
	private CustomTextFrameHandler customTextFrameHandler;

    @Override
    public void initChannel(SocketChannel ch) throws Exception {
        ChannelPipeline pipeline = ch.pipeline();
        pipeline.addLast("codec-http", new HttpServerCodec());
        pipeline.addLast("aggregator", new HttpObjectAggregator(65536));
        
        pipeline.addLast("websocket-protocal-handler",new WebSocketServerProtocolHandler());
        pipeline.addLast("custome-handler", customTextFrameHandler);
    }
}

Analyze these Handlers, which are provided by Netty by default:

  • HttpServerCodec: used to parse Http requests, mainly in the handshake phase;
  • HttpObjectAggregator: used to combine Http request headers and request bodies, mainly processed in the handshake phase;
  • WebSocketServerProtocolHandler: handles the Websocket protocol;
  • CustomTextFrameHandler: A custom Handler for adding your own business logic.

Isn't it very convenient? After being processed by WebSocketServerProtocolHandler, the text data is read out, so you don't need to deal with the problems of data co-packing and unpacking yourself.

CustomTextFrameHandler

Custom Handler for business processing:

public class CustomTextFrameHandler extends SimpleChannelInboundHandler<TextWebSocketFrame> {
    @Override
    protected void channelRead0(final ChannelHandlerContext ctx, TextWebSocketFrame frame) throws Exception {
        final String content = frame.text();
        System.out.println("接收到数据:"+content);   
        
        // 回复数据
        TextWebSocketFrame respFrame = new TextWebSocketFrame("我收到了你的数据");
        if (ctx.channel().isWritable()) {
		      ChannelFuture future = ctx.writeAndFlush(respFrame);
		  }			        
    }
}

Benefit Description

Finally, let's talk about the benefits: Xiaoai speaker F code.

I prepared 2 copies, mainly to thank the friends of "WeChat Official Account" and "Nuggets Community". Each copy includes 1 Xiaoai speaker F code and 1 Xiaoai speaker mini F code.

Xiaomi mobile phone F code is derived from the English word "Friend", which is the right of first refusal provided by Xiaomi to core users of Xiaomi and netizens who have contributed to Xiaomi. If you have Xiaomi F code, you can use Xiaomi F code directly without waiting. Buy related products!

To put it simply, F code means you don't need to grab it, you can buy it directly~

Draw Deadline

April 9th ​​at 12 noon

Sweepstakes Rules
Nuggets community
  • Need to pay attention to my Nuggets account to be effective, personal homepage ;

  • Use the WeChat lottery assistant to randomly draw for the Nuggets community;

Nuggets Sweepstakes

WeChat public account
  • You need to pay attention to my WeChat public account to be effective;

love story

  • Use the WeChat lottery assistant to randomly select the for WeChat public account;

WeChat public account lottery

Guess you like

Origin http://43.154.161.224:23101/article/api/json?id=325154451&siteId=291194637