Netty core source code analysis (1), Netty's server-side startup process source code analysis

Series Article Directory

Netty core source code analysis (1), Netty’s server-side startup process source code analysis
Netty core source code analysis (2), Netty’s server-side receiving request process source code analysis
Netty core source code analysis (3) The key to business request execution - ChannelPipeline, ChannelHandler, ChannelHandlerContext source code analysis
Netty core source code analysis (4) heartbeat detection source code analysis
Netty core source code analysis (5) core component EventLoop source code analysis

1. Source code analysis of Netty's Server startup process

1. The creation of NioEventLoopGroup

Two EventLoopGroups will be created on the server side, we generally use NioEventLoopGroup:

EventLoopGroup bossGroup = new NioEventLoopGroup(1);
EventLoopGroup workerGroup = new NioEventLoopGroup();

These two EventLoopGroups are the core objects of the entire Netty. boosGroup is used to receive Tcp requests, he will pass the request to workerGroup, workerGroup will obtain the real connection, and then communicate with the connection, such as reading, writing, decoding and encoding.

EventLoopGroup is an event loop group (thread group), which contains multiple EventLoops, and can register channels for selection in the event loop (related to select).

(1) Construction method

In the construction method of NioEventLoopGroup, if no parameters are passed, the number of cpu cores*2 threads will be created by default:

// io.netty.channel.nio.NioEventLoopGroup#NioEventLoopGroup()
public NioEventLoopGroup() {
    
    
    this(0);
}
// ... 一直往上追
// io.netty.channel.MultithreadEventLoopGroup#MultithreadEventLoopGroup(int, java.util.concurrent.Executor, java.lang.Object...)
protected MultithreadEventLoopGroup(int nThreads, Executor executor, Object... args) {
    
    
    super(nThreads == 0 ? DEFAULT_EVENT_LOOP_THREADS : nThreads, executor, args);
}

We found that in the parent class, if nThreads is passed as 0, it will be assigned DEFAULT_EVENT_LOOP_THREADS:

// MultithreadEventLoopGroup静态代码块
private static final int DEFAULT_EVENT_LOOP_THREADS;

static {
    
    
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2)); // 获取cpu核心数 * 2

    if (logger.isDebugEnabled()) {
    
    
        logger.debug("-Dio.netty.eventLoopThreads: {}", DEFAULT_EVENT_LOOP_THREADS);
    }
}

In the end, the number of CPU cores * 2 is obtained, and these threads are created. My local CPU has 20 cores, so 40 workerGroup threads are created.
insert image description here
We continue to chase up in this construction method:

// nThreads表示使用的线程数
// executor表示执行器如果为null就采用Netty默认的线程工厂和默认的执行器ThreadPerTaskExecutor
// chooserFactory是上一步传入的DefaultEventExecutorChooserFactory
// args表示在创建执行器时传入的固定参数
// io.netty.util.concurrent.MultithreadEventExecutorGroup#MultithreadEventExecutorGroup(int, java.util.concurrent.Executor, io.netty.util.concurrent.EventExecutorChooserFactory, java.lang.Object...)
protected MultithreadEventExecutorGroup(int nThreads, Executor executor,
                                        EventExecutorChooserFactory chooserFactory, Object... args) {
    
    
    if (nThreads <= 0) {
    
    
        throw new IllegalArgumentException(String.format("nThreads: %d (expected: > 0)", nThreads));
    }

    if (executor == null) {
    
     // 为空的话,会创建默认的执行器
        executor = new ThreadPerTaskExecutor(newDefaultThreadFactory());
    }
	// 创建指定线程数的执行器数组
    children = new EventExecutor[nThreads]; // 40
	// 初始化线程数组
    for (int i = 0; i < nThreads; i ++) {
    
    
        boolean success = false;
        try {
    
    
            // 创建NioEventLoop
            children[i] = newChild(executor, args);
            success = true;
        } catch (Exception e) {
    
    
            // TODO: Think about if this is a good exception type
            throw new IllegalStateException("failed to create a child event loop", e);
        } finally {
    
    
            if (!success) {
    
    
            	// 如果启动失败,会关闭线程并且停止EventExecutor ,优雅关闭
                for (int j = 0; j < i; j ++) {
    
    
                    children[j].shutdownGracefully();
                }

                for (int j = 0; j < i; j ++) {
    
    
                    EventExecutor e = children[j];
                    try {
    
    
                        while (!e.isTerminated()) {
    
    
                            e.awaitTermination(Integer.MAX_VALUE, TimeUnit.SECONDS);
                        }
                    } catch (InterruptedException interrupted) {
    
    
                        // Let the caller handle the interruption.
                        Thread.currentThread().interrupt();
                        break;
                    }
                }
            }
        }
    }

    chooser = chooserFactory.newChooser(children);

    final FutureListener<Object> terminationListener = new FutureListener<Object>() {
    
    
        @Override
        public void operationComplete(Future<Object> future) throws Exception {
    
    
            if (terminatedChildren.incrementAndGet() == children.length) {
    
    
                terminationFuture.setSuccess(null);
            }
        }
    };

    for (EventExecutor e: children) {
    
    
    	// 每个EventExecutor添加监听器
        e.terminationFuture().addListener(terminationListener);
    }
	// 将所有单例线程池添加到HashSet中
    Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
    Collections.addAll(childrenSet, children);
    readonlyChildren = Collections.unmodifiableSet(childrenSet);
}

At this point we know that these EventExecutors are included in the children of NioEventLoopGroup, and our default is to use NioEventLoop.

insert image description here
The parent class of NioEventLoop, SingleThreadEventLoop, contains many subclasses:
insert image description here
in the new NioEventLoopGroup() we defined, it is equivalent to defining the implementation. Usually we use NioEventLoop or EpollEventLoop (linux needs to support epoll to improve performance).

2. The creation of ServerBootstrap

ServerBootstrap b = new ServerBootstrap();
b.group(bossGroup, workerGroup)
 .channel(NioServerSocketChannel.class)
 .option(ChannelOption.SO_BACKLOG, 100)
 .handler(new LoggingHandler(LogLevel.INFO))
 .childHandler(new ChannelInitializer<SocketChannel>() {
    
    
     @Override
     public void initChannel(SocketChannel ch) throws Exception {
    
    
         ChannelPipeline p = ch.pipeline();
         if (sslCtx != null) {
    
    
             p.addLast(sslCtx.newHandler(ch.alloc()));
         }
         p.addLast(new LoggingHandler(LogLevel.INFO));
         //p.addLast(new EchoServerHandler());
     }
 });

(1) Construction method

The created ServerBootstrap is a bootstrap class used to start the server and guide the initialization of the entire program. It is associated with ServerChannel, and ServerChannel inherits Channel.

ServerBootstrap contains ChannelHandler information and EventLoopGroup information:

public class ServerBootstrap extends AbstractBootstrap<ServerBootstrap, ServerChannel> {
    
    

    private static final InternalLogger logger = InternalLoggerFactory.getInstance(ServerBootstrap.class);

    private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
    private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
    private volatile EventLoopGroup childGroup;
    private volatile ChannelHandler childHandler;

    public ServerBootstrap() {
    
     }

    private ServerBootstrap(ServerBootstrap bootstrap) {
    
    
        super(bootstrap);
        childGroup = bootstrap.childGroup;
        childHandler = bootstrap.childHandler;
        synchronized (bootstrap.childOptions) {
    
    
            childOptions.putAll(bootstrap.childOptions);
        }
        synchronized (bootstrap.childAttrs) {
    
    
            childAttrs.putAll(bootstrap.childAttrs);
        }
    }

The parent class of ServerBootstrap also contains some address and other information:

public abstract class AbstractBootstrap<B extends AbstractBootstrap<B, C>, C extends Channel> implements Cloneable {
    
    

    volatile EventLoopGroup group;
    @SuppressWarnings("deprecation")
    private volatile ChannelFactory<? extends C> channelFactory;
    private volatile SocketAddress localAddress;
    private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();
    private final Map<AttributeKey<?>, Object> attrs = new LinkedHashMap<AttributeKey<?>, Object>();
    private volatile ChannelHandler handler;

    AbstractBootstrap() {
    
    
        // Disallow extending from a different package.
    }

    AbstractBootstrap(AbstractBootstrap<B, C> bootstrap) {
    
    
        group = bootstrap.group;
        channelFactory = bootstrap.channelFactory;
        handler = bootstrap.handler;
        localAddress = bootstrap.localAddress;
        synchronized (bootstrap.options) {
    
    
            options.putAll(bootstrap.options);
        }
        synchronized (bootstrap.attrs) {
    
    
            attrs.putAll(bootstrap.attrs);
        }
    }

(2) group method

b.group(bossGroup, workerGroup)

Pass boosGroup and workerGroup into parameters:

// io.netty.bootstrap.ServerBootstrap#group(io.netty.channel.EventLoopGroup, io.netty.channel.EventLoopGroup)
public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) {
    
    
    super.group(parentGroup); // 将bossGroup放入父类AbstractBootstrap
    if (childGroup == null) {
    
    
        throw new NullPointerException("childGroup");
    }
    if (this.childGroup != null) {
    
    
        throw new IllegalStateException("childGroup set already");
    }
    this.childGroup = childGroup; // workerGroup放入ServerBootstrap 中
    return this;
}

// io.netty.bootstrap.AbstractBootstrap#group(io.netty.channel.EventLoopGroup)
public B group(EventLoopGroup group) {
    
    
    if (group == null) {
    
    
        throw new NullPointerException("group");
    }
    if (this.group != null) {
    
    
        throw new IllegalStateException("group set already");
    }
    this.group = group; // 将bossGroup放入AbstractBootstrap
    return self();
}

We found that ServerBootstrap and its parent class AbstractBootstrap seem to have a hierarchical relationship with EventLoopGroup, where bossGroup is placed in the parent class and workerGroup is placed in the subclass.

(3) channel method

.channel(NioServerSocketChannel.class)

A Channel class object is added, and the bootstrap class will create a ChannelFactory through reflection of this Class object

// io.netty.bootstrap.AbstractBootstrap#channel
public B channel(Class<? extends C> channelClass) {
    
    
    if (channelClass == null) {
    
    
        throw new NullPointerException("channelClass");
    }
    // 创建ReflectiveChannelFactory
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

In ReflectiveChannelFactory, the newChannel method is rewritten to create Channel through reflection:

// io.netty.channel.ReflectiveChannelFactory#newChannel
@Override
public T newChannel() {
    
    
    try {
    
    
        return clazz.getConstructor().newInstance();
    } catch (Throwable t) {
    
    
        throw new ChannelException("Unable to create Channel from class " + clazz, t);
    }
}

Notice! Channel is created in the bind method, and the newChannel method of ChannelFactory is called.

(4) option method

.option(ChannelOption.SO_BACKLOG, 100)

Set some configuration options:

// io.netty.bootstrap.AbstractBootstrap#option
public <T> B option(ChannelOption<T> option, T value) {
    
    
    if (option == null) {
    
    
        throw new NullPointerException("option");
    }
    if (value == null) {
    
    
        synchronized (options) {
    
    
            options.remove(option);
        }
    } else {
    
    
        synchronized (options) {
    
    
            options.put(option, value);
        }
    }
    return self();
}

(5) handler method

.handler(new LoggingHandler(LogLevel.INFO))

Add some handlers specific to service requests:

// io.netty.bootstrap.AbstractBootstrap#handler(io.netty.channel.ChannelHandler)
public B handler(ChannelHandler handler) {
    
    
    if (handler == null) {
    
    
        throw new NullPointerException("handler");
    }
    this.handler = handler;
    return self();
}

In fact, the Handler passed in in the handler method is the Handler handed over to boosGroup for processing, because the handler method is in AbstractBootstrap, the parent class of ServerBootstrap.

(6) childHandler method

// io.netty.bootstrap.ServerBootstrap#childHandler(io.netty.channel.ChannelHandler)
public ServerBootstrap childHandler(ChannelHandler childHandler) {
    
    
    if (childHandler == null) {
    
    
        throw new NullPointerException("childHandler");
    }
    this.childHandler = childHandler;
    return this;
}

Add the processing class of workerGroup, this method is ServerBootstrap, and the added handler also serves for workerGroup.

(7) bind method

ChannelFuture f = b.bind(PORT).sync();

Bind the port and block it. After the bind method is successfully executed, the server will be started successfully.

// io.netty.bootstrap.AbstractBootstrap#bind(int)
public ChannelFuture bind(int inetPort) {
    
    
    return bind(new InetSocketAddress(inetPort));
}
// io.netty.bootstrap.AbstractBootstrap#bind(java.net.SocketAddress)
public ChannelFuture bind(SocketAddress localAddress) {
    
    
    validate();
    if (localAddress == null) {
    
    
        throw new NullPointerException("localAddress");
    }
    return doBind(localAddress);
}

// io.netty.bootstrap.AbstractBootstrap#doBind
private ChannelFuture doBind(final SocketAddress localAddress) {
    
    
	// 初始化和注册,Channel的创建和初始化pipeline就是在这做的
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
    
    
        return regFuture;
    }

    if (regFuture.isDone()) {
    
    
        // At this point we know that the registration was complete and successful.
        ChannelPromise promise = channel.newPromise();
        // 完成对端口的绑定
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
    
    
        // Registration future is almost always fulfilled already, but just in case it's not.
        final PendingRegistrationPromise promise = new PendingRegistrationPromise(channel);
        regFuture.addListener(new ChannelFutureListener() {
    
    
            @Override
            public void operationComplete(ChannelFuture future) throws Exception {
    
    
                Throwable cause = future.cause();
                if (cause != null) {
    
    
                    // Registration on the EventLoop failed so fail the ChannelPromise directly to not cause an
                    // IllegalStateException once we try to access the EventLoop of the Channel.
                    promise.setFailure(cause);
                } else {
    
    
                    // Registration was successful, so set the correct executor to use.
                    // See https://github.com/netty/netty/issues/2586
                    promise.registered();

                    doBind0(regFuture, channel, localAddress, promise);
                }
            }
        });
        return promise;
    }
}

(8) In the bind method - initAndRegister method

// io.netty.bootstrap.AbstractBootstrap#initAndRegister
final ChannelFuture initAndRegister() {
    
    
    Channel channel = null;
    try {
    
    
    	// 真正创建Channel的方法,ServerBootstrap的channel方法传入的class,在此处通过工厂进行了实例化
        channel = channelFactory.newChannel();
        init(channel);
    } catch (Throwable t) {
    
    
        if (channel != null) {
    
    
            // channel can be null if newChannel crashed (eg SocketException("too many open files"))
            channel.unsafe().closeForcibly();
            // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
            return new DefaultChannelPromise(channel, GlobalEventExecutor.INSTANCE).setFailure(t);
        }
        // as the Channel is not registered yet we need to force the usage of the GlobalEventExecutor
        return new DefaultChannelPromise(new FailedChannel(), GlobalEventExecutor.INSTANCE).setFailure(t);
    }

    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
    
    
        if (channel.isRegistered()) {
    
    
            channel.close();
        } else {
    
    
            channel.unsafe().closeForcibly();
        }
    }

    // If we are here and the promise is not failed, it's one of the following cases:
    // 1) If we attempted registration from the event loop, the registration has been completed at this point.
    //    i.e. It's safe to attempt bind() or connect() now because the channel has been registered.
    // 2) If we attempted registration from the other thread, the registration request has been successfully
    //    added to the event loop's task queue for later execution.
    //    i.e. It's safe to attempt bind() or connect() now:
    //         because bind() or connect() will be executed *after* the scheduled registration task is executed
    //         because register(), bind(), and connect() are all bound to the same thread.

    return regFuture;
}

(9) In the bind method - Channel creation logic in the initAndRegister method

After initAndRegister calls channelFactory.newChannel();, it actually calls the newChannel method in ReflectiveChannelFactory to create a Channel through reflection:

// io.netty.channel.ReflectiveChannelFactory#newChannel
@Override
public T newChannel() {
    
    
    try {
    
    
        return clazz.getConstructor().newInstance();
    } catch (Throwable t) {
    
    
        throw new ChannelException("Unable to create Channel from class " + clazz, t);
    }
}

The construction method of NioServerSocketChannel does a lot of initialization work of NioServerSocketChannel:

private static ServerSocketChannel newSocket(SelectorProvider provider) {
    
    
    try {
    
    
        /**
         *  Use the {@link SelectorProvider} to open {@link SocketChannel} and so remove condition in
         *  {@link SelectorProvider#provider()} which is called by each ServerSocketChannel.open() otherwise.
         *
         *  See <a href="https://github.com/netty/netty/issues/2308">#2308</a>.
         */
        return provider.openServerSocketChannel();
    } catch (IOException e) {
    
    
        throw new ChannelException(
                "Failed to open a server socket.", e);
    }
}

/**
 * Create a new instance
 */
public NioServerSocketChannel() {
    
    
    this(newSocket(DEFAULT_SELECTOR_PROVIDER));
}

(1) Obtain the JDK channel through the openServerSocketChannel method of NIO's SelectorProvider. The purpose is to let Netty wrap the JDK channel.
(2) Created a unique ChannelId, created a NioMessageUnsafe for operating messages, and created a DefaultChannelPipeline pipeline, which is a doubly linked list structure, used to filter all incoming and outgoing messages.
(3) A NioServerSocketChannelConfig object is created to display some configurations externally.

(10) In the bind method - the init method in the initAndRegister method

The init method is a method implemented in ServerBootstrap:

// io.netty.bootstrap.ServerBootstrap#init
@Override
void init(Channel channel) throws Exception {
    
    
	// 获取Options配置的属性
    final Map<ChannelOption<?>, Object> options = options0();
    synchronized (options) {
    
    
    	// 线程不安全,LinkedHashMap,所以需要同步
        setChannelOptions(channel, options, logger);
    }

    final Map<AttributeKey<?>, Object> attrs = attrs0();
    synchronized (attrs) {
    
     // 处理attr属性
        for (Entry<AttributeKey<?>, Object> e: attrs.entrySet()) {
    
    
            @SuppressWarnings("unchecked")
            AttributeKey<Object> key = (AttributeKey<Object>) e.getKey();
            channel.attr(key).set(e.getValue());
        }
    }
	// 处理pipeline
    ChannelPipeline p = channel.pipeline();

    final EventLoopGroup currentChildGroup = childGroup;
    final ChannelHandler currentChildHandler = childHandler;
    final Entry<ChannelOption<?>, Object>[] currentChildOptions;
    final Entry<AttributeKey<?>, Object>[] currentChildAttrs;
    synchronized (childOptions) {
    
    
        currentChildOptions = childOptions.entrySet().toArray(newOptionArray(childOptions.size()));
    }
    synchronized (childAttrs) {
    
    
        currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(childAttrs.size()));
    }

    p.addLast(new ChannelInitializer<Channel>() {
    
    
        @Override
        public void initChannel(final Channel ch) throws Exception {
    
    
            final ChannelPipeline pipeline = ch.pipeline();
            ChannelHandler handler = config.handler();
            if (handler != null) {
    
    
                pipeline.addLast(handler);
            }

            ch.eventLoop().execute(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    pipeline.addLast(new ServerBootstrapAcceptor(
                            ch, currentChildGroup, currentChildHandler, currentChildOptions, currentChildAttrs));
                }
            });
        }
    });
}

(11) Pipeline's addLast method

Pipeline's addLast method is the core.

// io.netty.channel.DefaultChannelPipeline#addLast(io.netty.util.concurrent.EventExecutorGroup, java.lang.String, io.netty.channel.ChannelHandler)
@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    
    
    final AbstractChannelHandlerContext newCtx;
    synchronized (this) {
    
    
    	// 检查handler是否符合标准
        checkMultiplicity(handler);
		// 创建一个AbstractChannelHandlerContext 对象,该对象是ChannelHandler和ChannelPipeline之间的关联,每当有ChannelHandler添加到Pipeline中时,都会创建COntext。Context的主要功能是管理他所关联的Handler和同一个Pipeline中其他Handler之间的交互。
        newCtx = newContext(group, filterName(name, handler), handler);

		// 将newContext保存
        addLast0(newCtx);

        // If the registered is false it means that the channel was not registered on an eventloop yet.
        // In this case we add the context to the pipeline and add a task that will call
        // ChannelHandler.handlerAdded(...) once the channel is registered.
        if (!registered) {
    
    
            newCtx.setAddPending();
            callHandlerCallbackLater(newCtx, true);
            return this;
        }

        EventExecutor executor = newCtx.executor();
        if (!executor.inEventLoop()) {
    
    
            newCtx.setAddPending();
            executor.execute(new Runnable() {
    
    
                @Override
                public void run() {
    
    
                    callHandlerAdded0(newCtx);
                }
            });
            return this;
        }
    }
    callHandlerAdded0(newCtx);
    return this;
}
// io.netty.channel.DefaultChannelPipeline#addLast0
private void addLast0(AbstractChannelHandlerContext newCtx) {
    
    
    AbstractChannelHandlerContext prev = tail.prev;
    newCtx.prev = prev;
    newCtx.next = tail;
    prev.next = newCtx;
    tail.prev = newCtx;
}

We can see that the addLast method does not put the Handler at the end, but puts our custom Handler before the tail, so that the tail will always be at the end, doing some fixed work of the system.

(13) In the bind method - the dobind0 method

// io.netty.bootstrap.AbstractBootstrap#doBind0
private static void doBind0(
        final ChannelFuture regFuture, final Channel channel,
        final SocketAddress localAddress, final ChannelPromise promise) {
    
    

    // This method is invoked before channelRegistered() is triggered.  Give user handlers a chance to set up
    // the pipeline in its channelRegistered() implementation.
    channel.eventLoop().execute(new Runnable() {
    
    
        @Override
        public void run() {
    
    
            if (regFuture.isSuccess()) {
    
    
            	// 调用channel的bind方法,因为此刻channel已经初始化完成了
                channel.bind(localAddress, promise).addListener(ChannelFutureListener.CLOSE_ON_FAILURE);
            } else {
    
    
                promise.setFailure(regFuture.cause());
            }
        }
    });
}

The channel.bind method we trace step by step:

// io.netty.channel.AbstractChannel#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
@Override
public ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    
    
    return pipeline.bind(localAddress, promise);
}
// io.netty.channel.DefaultChannelPipeline#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
@Override
public final ChannelFuture bind(SocketAddress localAddress, ChannelPromise promise) {
    
    
    return tail.bind(localAddress, promise);
}
// io.netty.channel.AbstractChannelHandlerContext#bind(java.net.SocketAddress, io.netty.channel.ChannelPromise)
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
    
    
    if (localAddress == null) {
    
    
        throw new NullPointerException("localAddress");
    }
    if (isNotValidPromise(promise, false)) {
    
    
        // cancelled
        return promise;
    }

    final AbstractChannelHandlerContext next = findContextOutbound();
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
    
    
    	// 执行
        next.invokeBind(localAddress, promise);
    } else {
    
    
        safeExecute(executor, new Runnable() {
    
    
            @Override
            public void run() {
    
    
                next.invokeBind(localAddress, promise);
            }
        }, promise, null);
    }
    return promise;
}
// io.netty.channel.AbstractChannelHandlerContext#invokeBind
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
    
    
    if (invokeHandler()) {
    
    
        try {
    
    
        	// 执行
            ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
        } catch (Throwable t) {
    
    
            notifyOutboundHandlerException(t, promise);
        }
    } else {
    
    
        bind(localAddress, promise);
    }
}
// io.netty.handler.logging.LoggingHandler#bind
@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise) throws Exception {
    
    
    if (logger.isEnabled(internalLevel)) {
    
    
        logger.log(internalLevel, format(ctx, "BIND", localAddress));
    }
    ctx.bind(localAddress, promise);
}
// 
@Override
public ChannelFuture bind(final SocketAddress localAddress, final ChannelPromise promise) {
    
    
    if (localAddress == null) {
    
    
        throw new NullPointerException("localAddress");
    }
    if (isNotValidPromise(promise, false)) {
    
    
        // cancelled
        return promise;
    }

    final AbstractChannelHandlerContext next = findContextOutbound();
    EventExecutor executor = next.executor();
    if (executor.inEventLoop()) {
    
    
    	// 执行
        next.invokeBind(localAddress, promise);
    } else {
    
    
        safeExecute(executor, new Runnable() {
    
    
            @Override
            public void run() {
    
    
                next.invokeBind(localAddress, promise);
            }
        }, promise, null);
    }
    return promise;
}
// io.netty.channel.AbstractChannelHandlerContext#invokeBind
private void invokeBind(SocketAddress localAddress, ChannelPromise promise) {
    
    
    if (invokeHandler()) {
    
    
        try {
    
    
        	// 执行
            ((ChannelOutboundHandler) handler()).bind(this, localAddress, promise);
        } catch (Throwable t) {
    
    
            notifyOutboundHandlerException(t, promise);
        }
    } else {
    
    
        bind(localAddress, promise);
    }
}
// io.netty.channel.DefaultChannelPipeline.HeadContext#bind
@Override
public void bind(
        ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
        throws Exception {
    
    
    // unsafe.bind
    unsafe.bind(localAddress, promise);
}
// io.netty.channel.AbstractChannel.AbstractUnsafe#bind
@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
    
    
    assertEventLoop();

    if (!promise.setUncancellable() || !ensureOpen(promise)) {
    
    
        return;
    }

    // See: https://github.com/netty/netty/issues/576
    if (Boolean.TRUE.equals(config().getOption(ChannelOption.SO_BROADCAST)) &&
        localAddress instanceof InetSocketAddress &&
        !((InetSocketAddress) localAddress).getAddress().isAnyLocalAddress() &&
        !PlatformDependent.isWindows() && !PlatformDependent.maybeSuperUser()) {
    
    
        // Warn a user about the fact that a non-root user can't receive a
        // broadcast packet on *nix if the socket is bound on non-wildcard address.
        logger.warn(
                "A non-root user can't receive a broadcast packet if the socket " +
                "is not bound to a wildcard address; binding to a non-wildcard " +
                "address (" + localAddress + ") anyway as requested.");
    }

    boolean wasActive = isActive();
    try {
    
    
    	// 关键方法
        doBind(localAddress);
    } catch (Throwable t) {
    
    
        safeSetFailure(promise, t);
        closeIfClosed();
        return;
    }

    if (!wasActive && isActive()) {
    
    
        invokeLater(new Runnable() {
    
    
            @Override
            public void run() {
    
    
                pipeline.fireChannelActive();
            }
        });
    }

    safeSetSuccess(promise);
}

Debug has been chasing a lot, and finally came to our key point! Here is the bind method that executes the NIO channel:

// io.netty.channel.socket.nio.NioServerSocketChannel#doBind
@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    
    
    if (PlatformDependent.javaVersion() >= 7) {
    
    
    	// 版本大于jdk7
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
    
    
    	// 版本小于jdk7
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

3. Started event loop

After the bind method is executed, if the debug continues at this time, it will eventually enter the run method of NioEventLoop, which is an infinite loop:

// io.netty.channel.nio.NioEventLoop#run
@Override
protected void run() {
    
    
    for (;;) {
    
    
        try {
    
    
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
    
    
                case SelectStrategy.CONTINUE:
                    continue;
                case SelectStrategy.SELECT:
                    select(wakenUp.getAndSet(false));

                    // 'wakenUp.compareAndSet(false, true)' is always evaluated
                    // before calling 'selector.wakeup()' to reduce the wake-up
                    // overhead. (Selector.wakeup() is an expensive operation.)
                    //
                    // However, there is a race condition in this approach.
                    // The race condition is triggered when 'wakenUp' is set to
                    // true too early.
                    //
                    // 'wakenUp' is set to true too early if:
                    // 1) Selector is waken up between 'wakenUp.set(false)' and
                    //    'selector.select(...)'. (BAD)
                    // 2) Selector is waken up between 'selector.select(...)' and
                    //    'if (wakenUp.get()) { ... }'. (OK)
                    //
                    // In the first case, 'wakenUp' is set to true and the
                    // following 'selector.select(...)' will wake up immediately.
                    // Until 'wakenUp' is set to false again in the next round,
                    // 'wakenUp.compareAndSet(false, true)' will fail, and therefore
                    // any attempt to wake up the Selector will fail, too, causing
                    // the following 'selector.select(...)' call to block
                    // unnecessarily.
                    //
                    // To fix this problem, we wake up the selector again if wakenUp
                    // is true immediately after selector.select(...).
                    // It is inefficient in that it wakes up the selector for both
                    // the first case (BAD - wake-up required) and the second case
                    // (OK - no wake-up required).

                    if (wakenUp.get()) {
    
    
                        selector.wakeup();
                    }
                    // fall through
                default:
            }

            cancelledKeys = 0;
            needsToSelectAgain = false;
            final int ioRatio = this.ioRatio;
            if (ioRatio == 100) {
    
    
                try {
    
    
                    processSelectedKeys();
                } finally {
    
    
                    // Ensure we always run tasks.
                    runAllTasks();
                }
            } else {
    
    
                final long ioStartTime = System.nanoTime();
                try {
    
    
                    processSelectedKeys();
                } finally {
    
    
                    // Ensure we always run tasks.
                    final long ioTime = System.nanoTime() - ioStartTime;
                    runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                }
            }
        } catch (Throwable t) {
    
    
            handleLoopException(t);
        }
        // Always handle shutdown even if the loop processing threw an exception.
        try {
    
    
            if (isShuttingDown()) {
    
    
                closeAll();
                if (confirmShutdown()) {
    
    
                    return;
                }
            }
        } catch (Throwable t) {
    
    
            handleLoopException(t);
        }
    }
}

At this point, events are received, and Netty is officially started.
The BoosGroup has been created and started, and the following process of Accept receiving client requests begins.
insert image description here

Guess you like

Origin blog.csdn.net/A_art_xiang/article/details/130315357