Register method in Netty’s bind process

@Override public final void register(EventLoop eventLoop, final ChannelPromise promise) { if (eventLoop == null) { throw new NullPointerException("eventLoop"); } if (isRegistered()) { promise.setFailure(new IllegalStateException("registered to an event loop already")); return; } if (! isCompatible(eventLoop)) { promise.setFailure( new IllegalStateException("incompatible event loop type: " + eventLoop.getClass().getName())); return; } AbstractChannel.this.eventLoop = eventLoop; if (eventLoop.inEventLoop()) { register0(promise); } else { try { eventLoop.execute(new Runnable() { @Override public void run() { register0(promise); }}); } catch (Throwable t) { logger.warn( "Force-closing a channel whose registration task was not accepted by an event loop:  {}", AbstractChannel.this, t); closeForcibly(); closeFuture.setClosed(); safeSetFailure(promise, t); }}}Copy the code

Register0 will eventually call a Java NIO method, as shown below. Here’s a Java NIO Selector set of things:

public final SelectionKey register(Selector sel, int ops, Object att) throws ClosedChannelException { synchronized (regLock) { if (! isOpen()) throw new ClosedChannelException(); if ((ops & ~validOps()) ! = 0) throw new IllegalArgumentException(); if (blocking) throw new IllegalBlockingModeException(); SelectionKey k = findKey(sel); if (k ! = null) { k.interestOps(ops); k.attach(att); } if (k == null) { // New registration synchronized (keyLock) { if (! isOpen()) throw new ClosedChannelException(); k = ((AbstractSelector)sel).register(this, ops, att); addKey(k); } } return k; }}Copy the code

In the register method, there is a judgment eventloop.ineventloop (), which is found in many places in the Netty source code and is used to determine whether the currently executing thread is the thread in the eventLoop (also known as the I/O thread).

@Override
public boolean inEventLoop() {
    return inEventLoop(Thread.currentThread());
}

@Override
public boolean inEventLoop(Thread thread) {
    return thread == this.thread;
}
Copy the code

This judgment can lead to some netty thread processing, in Netty:

  1. An EventLoopGroup can contain one or more Eventloops.
  2. An EventLoop is bound to a single Thread throughout its lifetime.
  3. All the various I/O events handled by EventLoop will be processed on the Thread it is associated with.
  4. A Channel registers with only one EventLoop during its lifetime.
  5. An EventLoop is assigned one or more channels during execution.

Important conclusion 1: In Netty, the implementation of a Channel must be thread-safe (only bound to a single thread, equivalent to a single thread); Based on this, we can store a Channel reference and use this reference to call the corresponding method of the Channel when data needs to be sent to the remote endpoint, and the messages must be sent in order.

Important conclusion 2: During business development, do not put long time consuming tasks in the execution queue of EventLoop, because it will block other execution tasks on all channels corresponding to this thread. If we need to do blocking calls or time-consuming operations (common in real development), we need to use a dedicated EventExecutor (business thread pool).

Business thread pools are typically implemented in two ways:

  1. In the callback method of ChannelHandler, use your own defined business thread pool
  2. Pass the EventExecutor with the addLast method that netty calls when adding a ChannelHandler to the ChannelPipeline.

Description: By default (addLast(Handler)), callback methods in ChannelHandler are executed by the I/O thread, If ChannelPipeline addLast(EventExecutorGroup Group, ChannelHandler… Handlers), then the callback methods in ChannelHandler are executed by the group thread group in the parameters.