Netty源码解析(一)之Netty启动流程(4.1.47.Final)

对于使用过Netty的程序员来说,下面这段代码应该非常的熟悉。

EventLoopGroup bossGroup = new NioEventLoopGroup(); // (1)
EventLoopGroup workerGroup = new NioEventLoopGroup();
try {
    ServerBootstrap b = new ServerBootstrap(); // (2)
    b.group(bossGroup, workerGroup)
            .channel(NioServerSocketChannel.class) // (3)
            .childHandler(new ChannelInitializer<SocketChannel>() { // (4)
                @Override
                public void initChannel(SocketChannel ch) throws Exception {
                    ch.pipeline().addLast("framer", new DelimiterBasedFrameDecoder(8192, Delimiters.lineDelimiter()));
                    ch.pipeline().addLast("decoder", new StringDecoder());
                    ch.pipeline().addLast("encoder", new StringEncoder());
                }
            })
            .option(ChannelOption.SO_BACKLOG, 128)          // (5)
            .childOption(ChannelOption.SO_KEEPALIVE, true); // (6)

    // 绑定端口,开始接收进来的连接
    ChannelFuture f = b.bind(0).sync(); // (7)
    // 等待服务器  socket 关闭 。
    // 在这个例子中,这不会发生,但你可以优雅地关闭你的服务器。
    f.channel().closeFuture().sync();
    System.out.println("hello");


} catch (InterruptedException e) {
    e.printStackTrace();
} finally {
    workerGroup.shutdownGracefully();
    bossGroup.shutdownGracefully();
}

这是一个典型的搭建一个Netty服务器的流程。我们都知道Netty底层是基于Java NIO,但是却对Java NIO做了非常丰富的扩展和包装,使得程序员能够很方便地搭建一个服务器。这篇文章将对上述过程进行一个总体的概述,并在接下来的系列文章中从源码出发,深入解析Netty的每一个模块。

一. 组件的概述

1. Channel

Channel是对一个网络套接字(network socket)的抽象(socket channel)或者一个可以进行IO操作的组件的抽象。Netty中的Channel的IO操作都是异步的,比较常用的Channel有NioServerSocketChannel(服务器的socket channel)和NioSocketChannel(客户端的socket channel)

2. EventLoopGroup和EventLoop

EventLoopGroup和EventLoop其实就相当于线程组和线程,EventLoop可以用来处理IO事件,也可以用来执行一个普通的Runnable或Scheduled Task。当服务器每接收到一个连接后,会将该连接保存为一个Channel,然后交由EventLoopGroup处理,EventLoopGroup会从他的线程组中选择一个EventLoop来负责该Channel后续的IO事件,并且每一个Channel都由唯一的一个EventLoop来处理(但一个EvenLoop可以负责处理多个Channel的IO事件)。

3. ServerBootStrap

ServerBootstrap很好理解,他是用来启动一个ServerSocketChannel。

通过该方法

public ServerBootstrap group(EventLoopGroup parentGroup, EventLoopGroup childGroup) 

可以为一个ServerBootstrap设置两个EventLoopGroup:parentGroup和childGroup,其中parentGroup用于处理ServerSocketChannel的各种操作,例如bind,register;而当一个客户端连接到ServerSocketChannel上时,childGroup用于处理该连接的SocketChannel的IO事件,例如read,write。

4. ChannelPipeline和ChannelHandler

ChannelHandler用于处理一个IO事件或者拦截一个IO操作,而由多个ChannelHandler组成的队列即为一个ChannelPipeline,每个Channel都有自己的ChannelPipeline,当这个Channel触发一个IO事件或者执行一个IO操作的时候,该IO事件或IO操作会交给ChannelPipeline,pipeline上的每个handler可以对其进行处理或者传递给下一个handler(责任链模式)。

二. 服务器的启动和接收连接流程

1. 服务器的启动

 服务器的启动逻辑主要在ServerBootstrap.bind()方法中,可以总结为以下几个主要步骤:

  1. 通过反射创建一个NioServerSocketChannel实例
  2. 给ServerSocketChannel的ChannelPipeline添加一个ChannelInitializer,这个ChannelInitializer会在Channel初始化的时候再给ChannelPipeline添加一个名为ServerBootstrapAcceptor的Handler,这个Handler就是专门用来处理客户端的连接请求。
  3. ServerSocketChannel通过Java NIO的方式将自己注册到Selector上,但注册的SelectionKey为0,也就是不对任何事件感兴趣。
  4. ServerSocketChannel触发ChannelPipeline上的所有Handler的handlerAdded事件,此时就会触发第2步中所添加的ChannelInitializer的handlerAdded方法,而ChannelInitializer的handlerAdded方法会调用initChannel方法,从而将ServerBootstrapAcceptor正式添加到ServerSocketChannel的ChannelPipeline中。
  5. ServerSocketChannel的ChannelPipeline触发一个channelRegistered事件,此时ChannelPipeline中的所有Handler的channelRegistered方法被依次调用。
  6. ServerSocketChannel的ChannelPipeline触发一个channelActive事件,此时ChannelPipeline中的所有Handler的channelActive方法被依次调用。
  7. ServerSocketChannel将注册到Selector上的SelectionKey改为OP_ACCEPT。
  8. ServerSocketChannel将自己绑定到某个InetSocketAddress上。至此,该ServerSocketChannel就可以正式接收来自客户端的连接了。

2. 客户端的连接

客户端的连接的逻辑主要在NioEventLoop.run()方法中,run()方法中是一个死循环,说明EventLoop会一直处理IO事件。可以总结为以下几个主要步骤:

  1. Selector调用select方法,判断是否有IO事件的发生(是否有连接请求)。
  2. 如果有连接请求,调用ServerSocketChannel的accept方法获取客户端的SocketChannel,并将该SocketChannel包装为一个NioSocketChannel。
  3. ServerSocketChannel触发一个ChannelRead事件,ChannelPipeline会将该事件进行传递,然后由ServerBootstrapAcceptor捕获(就是服务器启动流程的第2步中为ServerSocketChannel所添加的Handler)。
  4. ServerBootstrapAcceptor为客户端NioSocketChannel添加childHandler并设置childOption(就是代码中第4步和第6步中用户自己添加的childHandler和childOption)。
  5. ServerBootstrapAcceptor将客户端NioSocketChannel交由childEventLoopGroup处理,childEventLoopGroup会将该NioSocketChannel注册到Selector上,该NioSocketChannel后续的读写IO事件都将由childEventLoopGroup负责。
  6. ServerSocketChannel触发一个ChannelReadComplete事件。

如图:

上述过程是对服务器的启动以及客户端的连接的大致流程,在下一篇文章中,我将从源码出发,对上述过程进行详细地解析。

Netty源码解析(二)之服务器启动源码_benjam1n77的博客-CSDN博客

猜你喜欢

转载自blog.csdn.net/benjam1n77/article/details/122702182