Netty(五)—服务端小记

Netty服务端

  • Netty客户端程序员启动需要使用 Bootstrap 对象
  • 和客户端不同,服务端使用 ServerBootStrap 对象来启动

Server代码实现

  • 通过和客户端代码实现对比,可以发现
    • 服务端设置了两个EventLoopGroup对象,分别用于处理客户端连接请求和处理IO操作
    • 与客户端实现不同 服务端使用 ServerBootstrap 来实现程序启动
    • 使用 ServerBootstrap 的 bind 方法来启动服务
public void start(int port) {
        EventLoopGroup bossGroup = new NioEventLoopGroup();
        EventLoopGroup workerGroup = new NioEventLoopGroup();
        try {
            ServerBootstrap b = new ServerBootstrap();
            b.group(bossGroup, workerGroup)
                    .channel(NioServerSocketChannel.class)
                    .option(ChannelOption.SO_BACKLOG, 1024)
                    .childHandler(new ChannelInitializer<SocketChannel>() {
                        @Override
                        public void initChannel(SocketChannel ch) throws Exception {
                            ChannelPipeline pipeline = ch.pipeline();
                            ...
                        }
                    });
            ChannelFuture f = b.bind(this.port).sync();
            log.info("服务已启动,监听端口" + this.port);
            f.channel().closeFuture().sync();
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            workerGroup.shutdownGracefully();
            bossGroup.shutdownGracefully();
        }
    }
  • 服务端使用ServerBootstrap而非Bootstarp
  • ServerBootstrap同样提供channel方法设置Channel Type
    • 完成实例化操作的对象是 ReflectiveChannelFactory,通过反射调用到 NioServerSocketChannel 的构造方法

NioServerSocketChannel初始化和注册

  • 客户端使用Channel Type为NioSocketChannel
  • 与客户端不同的,服务端的入口是ServerBootstrap的bind方法,然后调用到父类的 doBind 方法完成 初始化和注册

在这里插入图片描述

  • 与客户端不同的一点是,服务端传入参数是 SelectionKey.OP_ACCEPT ,表示服务启动后要监听客户端的连接请求

在这里插入图片描述

  • 在服务端由于有两个EventLoopGroup,所以Channel的注册与客户端略有不同
  • 在initAndRegister方法中,首先完成了bossGroup和NioServerSocketChannel的关联
  • 接下来在init方法中完成了workerGroup和NioServerSocketChannel的关联
  • 当一个客户端连接发送到服务端时,Java底层NIO的ServerSocketChannel会有已经初始化完成的 SelectionKey.OP_ACCEPT就绪
  • 由NioServerSocketChannel的doReadMessages方法通过调用javaChannel().accept()方法获取建立连接的SocketChannel对象

bossGroup和workerGroup

  • 客户端初始化流程的时候实例化了一个EventLoopGroup对象
  • 而服务端实例化了两个EventLoopGroup对象,其工作模型如下

在这里插入图片描述

  • 服务端bossGroup不断监听是否有客户端的连接,当拿到一个连接之后,bossGroup初始化各项资源
  • 然后workerGroup中选出一个EventLoop与目标连接建立绑定
  • 接下来与客户端的交互过程在分配的 EventLoop 中完成

Selector事件轮询

  • 服务端启动由ServerBootstrap的bind()方法开始,调用到AbstractBootstrap的doBind方法

在这里插入图片描述

  • doBind0 方法,这里调用了 与 channel关联的 EventLoop 的execute方法 AbstractBootstrap -> doBind0

在这里插入图片描述

  • 接下来调用到 SingleThreadEventExecutor 的 execute 方法
  • 这里主要做的事情就是创建线程,将线程添加到EventLoop的队列中去

在这里插入图片描述

  • startThread() 方法

在这里插入图片描述

  • SingleThreadEventExecutor.this.run方法,这里的this实际上时EventLoop对象
    • 这里实现了具体的轮询逻,体现了java NIO中的 select() 方法
    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();
                        }
                    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 {
                        final long ioTime = System.nanoTime() - ioStartTime;
                        runAllTasks(ioTime * (100 - ioRatio) / ioRatio);
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
            try {
                if (isShuttingDown()) {
                    closeAll();
                    if (confirmShutdown()) {
                        return;
                    }
                }
            } catch (Throwable t) {
                handleLoopException(t);
            }
        }
    }

本文总结

  • Netty作为服务端程序启动与客户端的区别
  • 服务端使用NioServerSocketChannel进行初始化和注册的基本过程
  • boosGroup与workerGroup的区别及其工作模型
  • 跟踪源码找到了相关代码,体现了selector的轮询机制
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章