Netty源碼解析 - 服務端啓動流程

1. 概述

Netty是一個穩定、高性能NIO通信框架,它對JDK NIO的使用做了很好的封裝,對使用者屏蔽了NIO通信的底層細節,對使用NIO降低業務開發工作量,降低開發難度

2. Netty IO Reactor模型

3. Netty服務端啓動流程

4. Netty組件分析

4.1 EventLoopGroup

Netty處理IO請求線程池,管理一組線程處理IO請求。啓動一個Netty服務,會初始化兩個線程組,主線程組和工作線程組。主線程組會啓動一個線程,接收客戶端請求,將接收到的連接註冊到工作線程組一個線程,交給工作組線程處理具體IO讀寫操作;工作線程組會啓動一組線程,處理具體IO讀寫工作。

線程組線程初始化工作是在構造函數中完成,通過指定線程組大小,初始化線程組管理線程數。

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

初始化的線程會委託給EventExecutorChooser管理,對於IO請求,它會按照一定的策略將IO請求轉交給一個具體線程來處理。

EventLoopGroup實際是管理一組EventLoop線程,具體IO處理是交給EventLoop類來處理的。

4.2 NioEventLoop

NioEventLoop是實際處理IO讀寫的線程,每個NioEventLoop會啓動一個線程,循環遍歷,處理IO讀寫任務。NioEventLoop有一個Selector,IO channel會被註冊到Selector上。NioEventLoop會維護一個taskQueue隊列,分配到的Runnable會放入taskQueue隊列中,在循環遍歷線程中,會取出Runnable進行處理。

對於每個EventLoop都有一個線程運行,這個線程通過startThread()啓動。這個線程的啓動有點繞,每個EventLoop持有一個ThreadPerTaskExecutor,ThreadPerTaskExecutor.execute()執行 DefaultThreadFactory.newThread(command).start()啓動線程。這個線程會啓動一個循環遍歷,監聽IO讀寫和順序執行隊列任務。

4.3 NioServerSocketChannel

Channel組件是IO讀寫通道,NioServerSocketChannel接收客戶端連接請求,NioSocketChannel處理連接讀寫請求。Channel處理IO讀寫任務委託給Unsafe和ChannelPipeline組件,Unsafe處理Channel讀寫任務,ChannelPipeline是讀寫請求處理鏈,可以在處理鏈中添加處理邏輯。

protected AbstractChannel(Channel parent) {
        this.parent = parent;
        id = newId();
        unsafe = newUnsafe();
        pipeline = newChannelPipeline();
    }

Unsafe是Channel的內部類,可以方便使用Channel持有的資源。對於NioServerSocketChannel,NioMessageUnsafe.read()接收客戶端連接請求,然後調用pipeline.fireChannelRead()觸發調用鏈執行,ServerBootstrapAcceptor處理客戶端連接請求,將客戶端請求連接註冊到工作線程組。對於NioSocketChannel,NioByteUnsafe.read()處理IO讀寫。

4.4 ServerBootstrap

組合使用Nettty各個組件,啓動Netty服務。它定義了一個Netty使用的門面,屏蔽了內部複雜的邏輯,方便的啓動一個Netty服務。

public final class EchoServer {
    static final int     PORT = Integer.parseInt(System.getProperty("port", "8007"));
    public static void main(String[] args) throws Exception {
        // Configure the server.
        EventLoopGroup bossGroup = new NioEventLoopGroup(1);
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        final EchoServerHandler serverHandler = new EchoServerHandler();
        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();
                            //p.addLast(new LoggingHandler(LogLevel.INFO));
                            p.addLast(serverHandler);
                        }
                    });
            // Start the server.
            ChannelFuture f = b.bind(PORT).sync();
            // Wait until the server socket is closed.
            f.channel().closeFuture().sync();
        } finally {
            // Shut down all event loops to terminate all threads.
            bossGroup.shutdownGracefully();
            workerGroup.shutdownGracefully();
        }
    }
}

ServerBootstrap定義兩個EventLoopGroup線程組,一個處理客戶端連接請求,一個處理接收連接IO讀寫請求。調用bind()方法,完成服務端的啓動。bind()依次會調用initAndRegister()和doBind0()。

final ChannelFuture initAndRegister() {
        Channel channel = null;
        try {
            channel = channelFactory.newChannel(); 【1】
            init(channel);                         【2】 
        } catch (Throwable t) {
        }
        
        ChannelFuture regFuture = config().group().register(channel); 【3】
        if (regFuture.cause() != null) {
            if (channel.isRegistered()) {
                channel.close();
            } else {
                channel.unsafe().closeForcibly();
            }
        }
        return regFuture;
    }
  1. 使用反射生成.channel(NioServerSocketChannel.class)方法指定的NioServerSocketChannel對象
  2. 設置channel參數,在channel pipeline中添加ServerBootstrapAcceptor處理邏輯,用於接收客戶端請求處理

    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(0));
            }
            synchronized (childAttrs) {
                currentChildAttrs = childAttrs.entrySet().toArray(newAttrArray(0));
            }
    
            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));
                        }
                    });
                }
            });
        }
    
  3. 將channel添加到EventLoopGroup線程組的NioEventLoop線程上,NioEventLoop線程啓動循環遍歷,處理channel連接請求。當接收到客戶端連接請求,會執行ServerBootstrapAcceptor邏輯,將接收到的請求添加到worker線程組一個NioEventLoop線程上,NioEventLoop線程啓動循環遍歷處理客戶端IO讀寫請求。每個NioEventLoop線程都有一個Selector,channel註冊到Selector上。

4.5 ServerBootstrapAcceptor

作爲NioServerSocketChannel處理鏈中的一個Handler,處理客戶端連接請求。NioServerSocketChannel收到客戶端連接請求,pipeline.fireChannelRead()調用處理鏈。
接收到的客戶端請求channel會添加到工作線程組,處理IO讀寫請求。

public void channelRead(ChannelHandlerContext ctx, Object msg) {
            // 客戶端連接channel
            final Channel child = (Channel) msg;
            // channel pipeline
            child.pipeline().addLast(childHandler);
            // 設置channel參數
            setChannelOptions(child, childOptions, logger);

            for (Entry<AttributeKey<?>, Object> e: childAttrs) {
                child.attr((AttributeKey<Object>) e.getKey()).set(e.getValue());
            }

            try {
                // 將channel添加到工作線程組
                childGroup.register(child).addListener(new ChannelFutureListener() {
                    @Override
                    public void operationComplete(ChannelFuture future) throws Exception {
                        if (!future.isSuccess()) {
                            forceClose(child, future.cause());
                        }
                    }
                });
            } catch (Throwable t) {
                forceClose(child, t);
            }
        }

相關文檔

  1. Netty源碼:服務端啓動過程
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章