netty的引导过程(服务端)

本文详细解析了Netty服务端的引导流程,包括创建EventLoopGroup、ServerBootstrap配置、Channel注册过程等关键技术点。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >


首先先来看一段netty的服务端代码

        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        bossGroup.next();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        protected void initChannel(SocketChannel ch)
                                throws Exception {
                            ch.pipeline().addLast("http-decoder",
                                    new HttpRequestDecoder());
                        }
                    });
            ChannelFuture future = b.bind(new InetSocketAddress(port)).sync();(阻塞直到bind完成)
            System.out.println("HTTP订购服务器启动,网址是 : " + "http://localhost:"
                    + port);
            future.channel().closeFuture().sync();
        } finally {
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }


在服务端,会新建两个EventLoopGroup,一个是bossGroup,用来处理accept操作,一个是workerGroup用来处理Channel的IO及event事件处理,接下来是对ServerBootstrap一系列变量进行设置的操作。
重点来看ChannelFuture future = b.bind(new InetSocketAddress(port)).sync()这一句, 这一句即NIO中注册ServerChannel注册到Selector中的操作。我们来进入bind方法详细了解下netty的引导过程:
netty引导分客户端和服务端两种(类分别是Bootstrap和ServerBootstrap),两者都继承自AbstratBootstrap,而bind的实现就是在AbstratBootstrap中,来看看这段代码
    public ChannelFuture bind(SocketAddress localAddress) {
        validate();
        if (localAddress == null) {
            throw new NullPointerException("localAddress");
        }
        return doBind(localAddress);
    }

前面是检查操作,具体实现在doBind()中
    private ChannelFuture doBind(final SocketAddress localAddress) {
        final ChannelFuture regFuture = initAndRegister();
        final Channel channel = regFuture.channel();
       ............
    }


先屏蔽后面的代码,重点来看initAndRegister()
    final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
        	//1、生成一个新的Channel,因为在设置引导时设置的Channel类是NioServerSocketChannel.class,所以这里生成了一个NioServerSocketChannel实例
            channel = channelFactory.newChannel();
            //2、对NioServerSocketChannel进行一些初始化操作
            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);
        }
        //3、重中之重,把channel注册到selector中
        ChannelFuture regFuture = config().group().register(channel);
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }

1、生成一个新的Channel,因为在设置引导时设置的Channel类是NioServerSocketChannel.class,所以这里生成了一个NioServerSocketChannel实例。NioServerSocketChannel实现了ServerSocketChannel接口
2、对NioServerSocketChannel进行一些初始化操作。init()方法具体实现是在ServerBootstrap类中,在init中主要是对Channel的属性进行设置以及添加handler到pipeline中
3、正常流程下来,会走到ChannelFuture regFuture = config().group().register(channel); config().group()返回的是和当前ServerBootstrap绑定的bossEventLoopGroup,利用这个EventLoopGroup实例进行注册serverSocketChannel操作。这里的bossEventLoopGroup是一个NioEventLoopGroup,它继承了MultithreadEventLoopGroup(从字面可以看出这是一个多线程的EventLoopGroup)register的具体实现就是在MultithreadEventLoopGroup中,
public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }

public ChannelFuture register(Channel channel) {
        return next().register(channel);
    }


next()返回一个属于当前EventLoopGroup的EventLoop,而NioEventLoop是一个单线程的线程池,继承自SingleThreadEventLoop,register方法就在这个类中实现
@Override
    public ChannelFuture register(Channel channel) {
        return register(new DefaultChannelPromise(channel, this));
    }


    @Override
    public ChannelFuture register(final ChannelPromise promise) {
        ObjectUtil.checkNotNull(promise, "promise");
        promise.channel().unsafe().register(this, promise);
        return promise;
    }

再往下看,走到AbstractChannel中的内部类AbstractUnsafe中,看到register方法,这里eventLoop.execute(new Runnable task)会把task加入到taskQueue中,并另起一个线程进行selector 的select操作

        @Override
        public final void register(EventLoop eventLoop, final ChannelPromise promise) {
            ......
            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);
                }
            }
        }

将当前Channel(ServerSocketChannel)的eventloop设置为之前选择的eventloop中,并且用这个eventloop线程进行register,

        private void register0(ChannelPromise promise) {
            try {
                
                if (!promise.setUncancellable() || !ensureOpen(promise)) {
                    return;
                }
                boolean firstRegistration = neverRegistered;
                doRegister();
                neverRegistered = false;
                registered = true;

                // Ensure we call handlerAdded(...) before we actually notify the promise. This is needed as the
                // user may already fire events through the pipeline in the ChannelFutureListener.
                pipeline.invokeHandlerAddedIfNeeded();

                safeSetSuccess(promise);
                pipeline.fireChannelRegistered();
                // Only fire a channelActive if the channel has never been registered. This prevents firing
                // multiple channel actives if the channel is deregistered and re-registered.
                if (isActive()) {
                    if (firstRegistration) {
                        pipeline.fireChannelActive();
                    } else if (config().isAutoRead()) {
                        // This channel was registered before and autoRead() is set. This means we need to begin read
                        // again so that we process inbound data.
                        //
                        // See https://github.com/netty/netty/issues/4805
                        beginRead();
                    }
                }
            } catch (Throwable t) {
                // Close the channel directly to avoid FD leak.
                closeForcibly();
                closeFuture.setClosed();
                safeSetFailure(promise, t);
            }
        }


doRegister()就是进行Channel注册到Selector的方法,下面就是触发Channel Register事件, 
1、invokeHandlerAddedIfNeeded() 2、fireChannelRegistered() 3、如果是第一次则触发fireChannelActive()


走完Channel的注册过程,接下来就是绑定Channel和地址的过程,怎个过程是由AbstractChannel中的bind方法完成,第一次绑定会触发fireChannelActive()事件,绑定过程是由Channel的eventloop执行,绑定完成后,整个引导过程就结束了
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值