Netty服务端和客户端创建的源码分析

<h2>服务端</h2>

以下是Netty官方的一个Echo服务示例:

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 {
        // Configure SSL.
        final SslContext sslCtx;
        if (SSL) {
            SelfSignedCertificate ssc = new SelfSignedCertificate();
            sslCtx = SslContextBuilder.forServer(ssc.certificate(), ssc.privateKey()).build();
        } else {
            sslCtx = null;
        }

        // Configure the server.
        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());
                 }
             });
  
            // 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();
        }
        
    }
}

上面代码创建Server端服务的流程如下:

  1. 创建ServerBootstrap实例
  2. 设置EventLoopGroup
  3. 设置创建的Channel类型
  4. option配置属性
  5. 设置Handler,处理请求
  6. 设置ChildHandler,处理对应channel的请求
  7. 通过bind创建Chnnel并绑定,启动服务

服务端创建时序图

服务端重要组件
ServerBootstrap
ServerBootstrap是Netty服务端的启动辅助类,提供一系列的方法用于设置服务端的参数和配置,简化开发。(衍生一点:ServerBootstrap的构造方法是无参的,因为参数太多所以采用了Builder模式)

继承自AbstractBootstrap,核心属性有childGroup和childHandler。

  • childGroup:负责调度和执行客户端的接入、网络读写事件的处理、用户自定义任务和定时任务的执行
  • childHandler:自定义的业务Handler

AbstractBootstrap核心属性有group和handler。

  • group:处理客户端的链接请求,并转交给childGroup(读取的数据是穿件的NioSocketChannel)

Reactor线程池
Netty的Reactor线程池是EventLoopGroup,实际上是一个EventLoop的数组。

EventLoop的职责是处理所有注册到本线程多路复用器Selector上的Channel,Selector的轮训操作有EventLoop线程run方法驱动。

另外用户自定义的Task和定时任务Task也由统一的EventLoop负责处理。

Channel
作为Nio服务,需要创建ServerSocketChannel,Netty对原生NIO类库做了封装,对应实现类为NioServerSocketChannel。

用户只需要制定Channel的实现类型,内部通过反射机制来创建对应的实例。

因为只在监听端口时创建,所以反射的性能影响并不大。

ChannelPipeline
ChannelPipeline是网络事件处理的职责链,负责管理和执行ChannelHandler。网络事件以事件流的形式在ChannelPipeline中流转。

ChannelHandler
ChannelHandler是提供给用户定制和扩展的关键接口,包括编解码,业务处理等都是通过ChannelHandler进行的。

Selector
Selector轮训操作由NioEventLoop调度和执行,选择准备就绪的Channel集合。

NioServerSocketChannel
绑定Server端地址的Server,读取客户端的链接请求(只有一个,在bind时创建)。

NioSocketChannel
和客户端之间的链接。

服务端线程模型

  • mainReactor:parentGroup
  • subReactor:childGroup
  • ThreadPool:如果没指定,使用childGroup执行,如果指定了则是业务线程(执行业务Handler的线程)

Handler模型

Server启动的关键流程

  1. bind操作创建了NioServerSocketChannel并注册到NioEventLoop中(parent group中会有一个线程执行selector的轮训操作)

Server端的请求接入流程

  1. NioEventLoop轮训到到就绪时间后,调用Unsafe.read(NioMessageUnsafe实现)创建NioSocketChannel并传递到ServerBootstrap的ServerBootstrapAcceptor的channelRead方法中。
  2. ServerBootstrapAcceptor的channelRead方法将NioSocketChannel注册到childGroup的Selector上(实现代码是AbstractNioSocket的doRegister;之后NioSocketChannel的事件就由child group处理,这点和NioServerSocketChannel的注册、处理是一样的;和上面的线程模型也是呼应的:两个reactor)。

<h2>客户端</h2>

public static void main(String[] args) throws Exception {
    // Configure SSL.git
    final SslContext sslCtx;
    if (SSL) {
        sslCtx = SslContextBuilder.forClient()
            .trustManager(InsecureTrustManagerFactory.INSTANCE).build();
    } else {
        sslCtx = null;
    }

    // Configure the client.
    EventLoopGroup group = new NioEventLoopGroup();
    try {
        Bootstrap b = new Bootstrap();
        b.group(group)
         .channel(NioSocketChannel.class)
         .option(ChannelOption.TCP_NODELAY, true)
         .handler(new ChannelInitializer<SocketChannel>() {
             @Override
             public void initChannel(SocketChannel ch) throws Exception {
                 ChannelPipeline p = ch.pipeline();
                 if (sslCtx != null) {
                     p.addLast(sslCtx.newHandler(ch.alloc(), HOST, PORT));
                 }
                 //p.addLast(new LoggingHandler(LogLevel.INFO));
                 p.addLast(new EchoClientHandler());
             }
         });

        // Start the client.
        ChannelFuture f = b.connect(HOST, PORT).sync();

        // Wait until the connection is closed.
        f.channel().closeFuture().sync();
    } finally {
        // Shut down the event loop to terminate all threads.
        group.shutdownGracefully();
    }
}

创建客户端的大致流程:

  1. 创建Bootstrap实例
  2. 设置EventLoop
  3. 指定Channel类型
  4. option配置
  5. 指定Handler
  6. connect

客户端创建时序图

客户端和服务端的模式基本一致,由线程轮训Selector的事件,由Pipeline进行事件传递,EventLoop进行处理。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
平台声明:文章内容(如有图片或视频亦包括在内)由作者上传并发布,文章内容仅代表作者本人观点,简书系信息发布平台,仅提供信息存储服务。

推荐阅读更多精彩内容