Netty15——Netty启动过程分析

本文详细解析了Netty Echo案例中ServerBootstrap的构建过程,涉及EventLoopGroup的创建、ChannelInitializer的配置、SSL支持及端口绑定等关键步骤,帮助理解Netty服务器启动的底层工作机制。
摘要由CSDN通过智能技术生成

 在Netty的io.netty.example包下有很多Netty的案例,可以用于源码分析和学习。现在使用该包下的Echo案例分析Netty的启动过程,入口代码如下:

public final class EchoServer {

    static final boolean SSL = System.getProperty("ssl") != null;
    static final int PORT = Integer.parseInt(System.getProperty("port", "8007"));

    public static void main(String[] args) throws Exception {
        // 配置 SSL
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // 配置服务端
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            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());
                 }
             });

            // 绑定监听端口,启动服务
            ChannelFuture f = b.bind(PORT).sync();

            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // 关闭所有的事件循环以终止所有的线程
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

 1、创建了两个EventLoopGroup对象:

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

 这两个对象是Netty的核心对象,Netty的整个运行过程都依赖于它们。bossGroup用于接收TCP请求,并将接收的请求交给workerGroup处理;workerGroup会获取到真正的连接,然后通过连接和客户端通信,完成读写、编解码等工作。
 EventLoopGroup,即事件循环组(线程组)。其中包含多个 EventLoop,EventLoop可以注册channel,用于在事件循环中进行选择(和选择器有关)。
 这里的1表示bossGroup事件循环组中只开1个线程用于接收请求,而workerGroup默认会开启CPU核数的2倍个线程:

private static final int DEFAULT_EVENT_LOOP_THREADS;
static {
    DEFAULT_EVENT_LOOP_THREADS = Math.max(1, SystemPropertyUtil.getInt(
            "io.netty.eventLoopThreads", NettyRuntime.availableProcessors() * 2));

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

 由上面的代码可知,我们可以通过设置 io.netty.eventLoopThreads 的值来自定义默认开启的线程个数。
 深入到 new NioEventLoopGroup() 的内部,代码如下:

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());
     }
     // 创建EventExecutor数组
     children = new EventExecutor[nThreads];
     for (int i = 0; i < nThreads; i ++) {
         boolean success = false;
         try {
             // 为EventExecutor数组中的每个元素赋值,赋值的每一个对象都是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) {
                 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) {
         e.terminationFuture().addListener(terminationListener);
     }

     Set<EventExecutor> childrenSet = new LinkedHashSet<EventExecutor>(children.length);
     Collections.addAll(childrenSet, children);
     readonlyChildren = Collections.unmodifiableSet(childrenSet);
 }

 先初始化一个EventExecutor数组,再为EventExecutor[]的每一个元素赋值:

// 初始化EventExecutor数组
children = new EventExecutor[nThreads];
for (int i = 0; i < nThreads; i ++) {
    ...        
    try {
        //为EventExecutor[]的每一个元素赋值
        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 {
        ...
    }
    ...
}

 这里给EventExecutor[]的每一个元素赋的值都是一个NioEventLoop对象:NioEventLoop实现了EventLoop和EventExecutor接口

@Override
protected EventLoop newChild(Executor executor, Object... args) throws Exception {
    return new NioEventLoop(this, executor, (SelectorProvider) args[0],
        ((SelectStrategyFactory) args[1]).newSelectStrategy(), (RejectedExecutionHandler) args[2]);
}

 2、完成创建两个 EventLoopGroup 对象之后,接着创建了一个 ServerBootstrap 对象,即服务端引导对象:该对象的主要作用是限制紧接着要在bind()中创建的ServerSocketChannel(代码中指定为NioServerSocketChannel类型)和后续客户端连接时要创建的SocketChannel对象,限制的内容包括ServerSocketChannel的实际类型、TCP参数、需绑定的handler等

try {
    ServerBootstrap b = new ServerBootstrap();
    b.group(bossGroup, workerGroup)
     // 限制ServerSocketChannel的类型为NioServerSocketChannel
     .channel(NioServerSocketChannel.class)
     // 设置一些TCP参数,option()可以调用多次设置多个参数
     .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());
         }
     });
     ...
} finally {
    ...
}

 ServerBootstrap 是一个引导对象,用于启动服务端和引导整个程序的初始化。它和ServerChannel关联,ServerChannel 继承了 Channel(通过方法channel(NioServerSocketChannel.class)指定channel类型,即指定了服务端接收请求的ServerSocketChannel的类型,它不限制客户端连接时创建的SocketChannel类型),可以通过ServerChannel获取到远程IP等信息:

public interface ServerSocketChannel extends ServerChannel {
    @Override
    ServerSocketChannelConfig config();
    @Override
    InetSocketAddress localAddress();
    @Override
    InetSocketAddress remoteAddress();
}

 需要注意的是在创建ServerBootstrap 对象时虽没有传入参数,但创建的对象是有一些默认属性(包括从父类继承的属性)的:

private final Map<ChannelOption<?>, Object> childOptions = new LinkedHashMap<ChannelOption<?>, Object>();
private final Map<AttributeKey<?>, Object> childAttrs = new LinkedHashMap<AttributeKey<?>, Object>();
// config对象后面会经常用到
private final ServerBootstrapConfig config = new ServerBootstrapConfig(this);
private volatile EventLoopGroup childGroup;
private volatile ChannelHandler childHandler;

 这里只是通过反射创建了一个ChannelFactory对象,并没有真正创建Channel对象(创建Channel对象是在bind()方法中完成的),这里只是给ChannelFactory指定了类型,以确定后续通过ChannelFactory创建哪种类型的Channel

public B channel(Class<? extends C> channelClass) {
    if (channelClass == null) {
        throw new NullPointerException("channelClass");
    }
    return channelFactory(new ReflectiveChannelFactory<C>(channelClass));
}

 通过option()设置TCP参数(option()方法可以调用多次,以向options中添加或移除配置),options的本质是一个Map:

private final Map<ChannelOption<?>, Object> options = new LinkedHashMap<ChannelOption<?>, Object>();

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();
}

 完成TCP的参数设置后,接着为bossGroup和workerGroup设置处理业务或者说处理事件的handler,其中handler()方法中设置的handler和bossGroup关联(或者说会注册到bossGroup的EventLoop上的ServerSocketChannel上,在后面的bind()方法中会完成该注册绑定)childHandler()方法中设置的handler和workerGroup关联(或者说会注册到workerGroup的EventLoop上的SocketChannel上,这个绑定将在有客户端连接时创建SocketChannel时完成)
 3、绑定端口,等待客户端连接:在bind()方法中会先初始化ServerSocketChannel并为ServerSocketChannel绑定handler,而SocketChannel则是在有客户端连接时创建,其handler也将在彼时绑定

// 绑定监听端口,启动服务端
ChannelFuture f = b.bind(PORT).sync();

 bind()方法最终会调用doBind()方法:

private ChannelFuture doBind(final SocketAddress localAddress) {
    // 初始化并注册ServerSocketChannel
    final ChannelFuture regFuture = initAndRegister();
    final Channel channel = regFuture.channel();
    if (regFuture.cause() != null) {
        return regFuture;
    }

    if (regFuture.isDone()) {
        // 到这儿,注册工作就算成功完成了
        ChannelPromise promise = channel.newPromise();
        //执行doBind0(),完成端口绑定
        doBind0(regFuture, channel, localAddress, promise);
        return promise;
    } else {
        // 到这,注册工作基本上总是可以成功,但为以防万一,进行了容错处理
        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;
    }
}

 doBind()方法的第一行就是用之前通过反射(channel(NioServerSocketChannel.class))初始化好的ChannelFactory来创建ServerSocketChannel(类型为指定的NioServerSocketChannel),创建后初始化并将该channel注册到bossGroup的EventLoop(即上文提到的NioEventLoop)上:

final ChannelFuture initAndRegister() {
    Channel channel = null;
    try {
        //创建channel
        channel = channelFactory.newChannel();
        //初始化channel,设置option中的参数等
        init(channel);
    } catch (Throwable t) {
        ...
    }
    // 注册channel,这里将channel注册到了bossGroup上
    ChannelFuture regFuture = config().group().register(channel);
    if (regFuture.cause() != null) {
        if (channel.isRegistered()) {
            channel.close();
        } else {
            channel.unsafe().closeForcibly();
        }
    }
    return regFuture;
}

 init(channel)源码如下:给每一个channel设置指定的属性和绑定handler

@Override
void init(Channel channel) throws Exception {
    final Map<ChannelOption<?>, Object> options = options0();
    synchronized (options) {
        setChannelOptions(channel, options, logger);
    }

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

    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();
            // 绑定handler:这里绑定的是在handler()方法中设置的handler
            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));
                }
            });
        }
    });
}

pipeLine是一个双向链表,且它本身初始化了head和tail,这里调用addLast(),是将新的handler插入到tail的前面,tail永远会在最后,做一些系统的固定工作

@Override
public final ChannelPipeline addLast(EventExecutorGroup group, String name, ChannelHandler handler) {
    final AbstractChannelHandlerContext newCtx;
    synchronized (this) {
        checkMultiplicity(handler);
        newCtx = newContext(group, filterName(name, handler), handler);
        addLast0(newCtx);
        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;
}

 addLast0()的源码:

private void addLast0(AbstractChannelHandlerContext newCtx) {
    AbstractChannelHandlerContext prev = tail.prev;
    newCtx.prev = prev;
    newCtx.next = tail;
    prev.next = newCtx;
    tail.prev = newCtx;
}

 创建、初始化并注册ServerSocketChannel之后会执行doBind0()方法,最终会调用到AbstractChannelHandlerContext的bind()方法:

@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()) {
        // 调用具体handler的bind方法
        next.invokeBind(localAddress, promise);
    } else {
        safeExecute(executor, new Runnable() {
            @Override
            public void run() {
                next.invokeBind(localAddress, promise);
            }
        }, promise, null);
    }
    return promise;
}

AbstractChannelHandlerContext的bind()方法会被调用多次,调用的次数和注册的handler的个数有关,会调用注册的handler的个数+1次,最后的1次调用的是DefaultChannelPipeline的bind()方法

@Override
public void bind(ChannelHandlerContext ctx, SocketAddress localAddress, ChannelPromise promise)
        throws Exception {
    unsafe.bind(localAddress, promise);
}

 接着会调用AbstractChannel的bind():

@Override
public final void bind(final SocketAddress localAddress, final ChannelPromise promise) {
    assertEventLoop();
    if (!promise.setUncancellable() || !ensureOpen(promise)) {
        return;
    }
    ...
    boolean wasActive = isActive();
    try {
        //绑定端口
        doBind(localAddress);
    } catch (Throwable t) {
        safeSetFailure(promise, t);
        closeIfClosed();
        return;
    }
    ...
}

 最后通过doBind()方法调用NioServerSocketChannel的doBind()完成端口监听,剩下的就是等待客户端连接了:

@Override
protected void doBind(SocketAddress localAddress) throws Exception {
    if (PlatformDependent.javaVersion() >= 7) {
        javaChannel().bind(localAddress, config.getBacklog());
    } else {
        javaChannel().socket().bind(localAddress, config.getBacklog());
    }
}

 这样服务端就启动了,然后会一直阻塞直到服务端的socket关闭(服务端停止运行),最后会优雅的关闭事件循环组中的所有线程资源。
 关于任务或者说事件的执行,见NioEventLoop中的run():

@Override
protected void run() {
    for (;;) {
        try {
            switch (selectStrategy.calculateStrategy(selectNowSupplier, hasTasks())) {
                case SelectStrategy.CONTINUE:
                    continue;
                case SelectStrategy.SELECT:
                    select(wakenUp.getAndSet(false));
                    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);
        }
    }
}

 通过for(;;)一直循环轮询看是否有新连接进入或有任务需要执行。
 Netty启动过程梳理:
  ①创建2个 EventLoopGroup 线程池数组,数组默认大小 CPU * 2,方便chooser选择线程池时提高性能
  ②BootStrap 将 boss 设置为 group属性,将 worker 设置为 childGroup 属性
  ③通过 bind 方法启动,内部重要的方法为 initAndRegister 和 dobind 方法
  ④initAndRegister 方法会通过反射创建 NioServerSocketChannel 及其相关的 NIO 的对象、pipeline、unsafe,同时也为 pipeline 初始了 head 节点和 tail 节点
  ⑤在register0 方法成功以后接着调用 dobind 方法,在其中又调用了 doBind0 方法,该方法会调用 NioServerSocketChannel 的 doBind 方法对 JDK 的 channel 和端口进行绑定, 完成 Netty 服务器的所有启动,并开始监听连接事件

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值