Netty服务端初始化详解

 2023-01-10
原文作者:宁轩 原文地址:https://juejin.cn/post/7173570343981416456

前言

这是 源码共读活动 的第二篇文章, 在上一章节中我们分析了 backlog 的作用, 接下来我们看一看 张师傅 为我们准备的Netty启动类都进行了哪些配置吧

往期文章:

没有拉取代码的小伙伴可以通过git输入以下命令拉一下代码, 让我们保持代码的同步

    git clone https://github.com/arthur-zhang/netty-study.git

配置分析

首先, 我们可以看到这个项目的目录结构很简单, 只有三个类, 通过名称可以知道, 分别是两个 Headler 类和一个 启动类 MyServer, 本篇文章也是主要针对MyServer来进行讲解的

202212302136239441.png

MyServer详解

下图是MyServer类的全部代码, 可以看到实际上没有多少,一图装得下, 接下来我们一起逐行代码去学习

202212302136244752.png

ServerBootstrap serverBootstrap = new ServerBootstrap();

Bootstrap的意思是引导, 在Netty应用中, 通常也是由Bootstrap开始的, 他的作用就是对Netty进行配置

202212302136252433.png

翻译工具为`uTools`插件 词典

本次主要对Netty中的ServerBootstrap进行讲解, ServerBootstrap是服务端引导类, 他的继承关系如下所示

202212302136258854.png

Netty中, AbstractBootstrap的实现类主要有两种, 分别是服务端ServeBootstrap和客户端Bootstrap, 对Bootstrap感兴趣的小伙伴可以评论区留言

202212302136265595.png

serverBootstrap.channel() 方法;

serverBootstrap.channel()方法是用来设置Netty对应的通道的

在执行该方法的时候可以看到, 他实际上是调用了ServeBootstrap类的抽象父类AbstractBootstrap

channel方法中实际上是初始化了一个ReflectiveChannelFactory工厂类, 同时将该工厂对象保存在AbstractBootstrap抽象类的channelFactory属性中, 后续可以调用生成channel对象, 目前只是保存

202212302136272466.png

下面我们看一下在ReflectiveChannelFactory工厂类的初始化和后续调用生成channel对象的方法

202212302136278807.png

NioServerSocketChannel.class

NioServerSocketChannelNetty官方封装的, 用来代替或包装 JDK 原生的SocketChannel对象, 他的继承关系图如下所示

202212302136283968.png

过多的就不讲了, 在讲下去就该这个类的源码分析, 感兴趣的小伙伴可以评论区说出来

option()和 childOption() 方法

Nettyoption()方法主要是设置ServerChannel的一些选项, 而childOption()方法是用来设置ServerChannel子Channel的选项

注: 如果是 客户端 , 因为是Bootstrap, 只会有option(), 没有childOption(), 所以设置的是 客户端Channel 的选项

202212302136291389.png

所以, childOption()方法是写在ServerBootstrap类中, 而不是继承于AbstractBootstrap抽象类的

2022123021362965210.png

option()方法是写在了AbstractBootstrap抽象类中, 记住他, 后面我们分析Netty启动的时候还会看到

2022123021363110211.png

NioChannelOption

NioChannelOption类是继承于ChannelOption同时新增了几个方法, 那么我们主要讲一下ChannelOption里面的常量信息

2022123021363163412.png

@SuppressJava6Requirement 注解的作用: 清除 java6 的警告, 现在我们大多使用的都是 java8 以上了, 这个注解可以无视掉

1、ChannelOption.SO_BACKLOG

SO_BACKLOG 参数用来初始化服务端可连接队列。

服务端处理客户端连接请求是顺序处理的,所以同一时间只能处理一个客户端连接,多个客户端来的时候,服务端将不能处理的客户端连接请求放在队列中等待处理, backlog 参数指定了队列的大小。

同时在设置 backlog 参数的时候, 也要根据需求来设置, 避免因为队列设置的太小导致消息溢出

在推一手上一篇文章 Netty源码分析(一) backlog 参数

2、ChannelOption.SO_REUSEADDR

SO_REUSEADDR 参数表示允许重复使用本地地址和端口。

比如,某个服务器进程占用了TCP的80端口进行监听,此时再次监听该端口就会返回错误,使用该参数就可以解决问题,该参数允许共用该端口,这个在服务器程序中比较常使用。

3、ChannelOption.SO_KEEPALIVE

SO_KEEPALIVE 默认为 false, 启用该功能时, TCP 会主动探测空闲连接的有效性, 可以将此功能视为TCP的心跳机制,需要注意的是:默认的心跳间隔是7200s即2小时。Netty默认关闭该功能

4、ChannelOption.SO_SNDBUF和ChannelOption.SO_RCVBUF

SO_SNDBUFSO_RCVBUF 这两个参数用于操作发送缓冲区大小和接受缓冲区大小。

接收缓冲区用于保存网络协议站内收到的数据,直到应用程序读取成功,发送缓冲区用于保存发送数据,直到发送成功。

5、ChannelOption.SO_LINGER

Linux内核默认的处理方式是当用户调用close()方法的时候,函数返回,在可能的情况下,尽量发送数据,不一定保证会发送剩余的数据,造成了数据的不确定性,使用 SO_LINGER 可以阻塞close()的调用时间,直到数据完全发送.

6、ChannelOption.TCP_NODELAY

TCP_NODELAY 参数的使用与Nagle算法有关。

该参数的作用就是禁止使用Nagle算法,使用于小数据即时传输。和TCP_NODELAY相对应的是TCP_CORK,该选项是需要等到发送的数据量最大的时候,一次性发送数据,适用于文件传输。

Nagle算法是将小的数据包组装为更大的帧然后进行发送,而不是输入一次发送一次,因此在数据包不足的时候会等待其他数据的到来,组装成大的数据包进行发送,虽然该算法有效提高了网络的有效负载,但是却造成了延时。

handler() 和 childHandler()

本来是到 serverBootstrap.heandler()方法了, 但是我一想下面还有一个serverBootstrap.childHandler()方法, 正好一起讲了, 顺便对比一下两个 headler 方法

2022123021363251813.png

同时大家应该看到了, 在实现childHeadler()方法的时候有创建一个类并实现了相应的方法, 他的作用就是初始化了一下 Channel 并把日志级别更改为 info

2022123021363320914.png

在 ServerBootstrap 中

handler()方法是针对bossGroup线程组起作用
childHandler()方法是针对workerGroup线程组起作用

在 Bootstrap 中

只有handler()方法, 因为客户端只需要一个事件线程组

NioEventLoopGroup 和 serverBootstrap.group()

NioEventLoopGroup 是一个可处理I/O操作的多线程事件循环。Netty提供了多种 EventLoopGroup 的实现用于不同类型的传输。

serverBootstrap.group()方法就是分配 bossGroup 和 workGroup 两个线程组的

bossGroup 和 workGroup

bossGroup是负责处理客户端和服务端建立连接注册的 selector

workGroup是负责处理客户端读事件的 selector 逻辑

全部代码

    public static void main(String[] args) throws InterruptedException {
    
        // 启动类
        ServerBootstrap serverBootstrap = new ServerBootstrap();
    
        // 设置对应的通道
        serverBootstrap.channel(NioServerSocketChannel.class);
        // 设置线程的连接个数
        serverBootstrap.option(NioChannelOption.SO_BACKLOG, 511);
        // TCP_NODELAY=true,如果TCP_NODELAY没有设置为true,那么底层的TCP为了能减少交互次数,会将网络数据积累到一定的数量后,
        // 服务器端才发送出去,会造成一定的延迟。在互联网应用中,通常希望服务是低延迟的,建议将TCP_NODELAY设置为true。
        serverBootstrap.childOption(NioChannelOption.TCP_NODELAY, true);
        // 以给定的日志级别打印出 LoggingHandler 中的日志
        serverBootstrap.handler(new LoggingHandler(LogLevel.INFO));
    
        // 监听
        NioEventLoopGroup bossGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("boss"));
        // 处理每一条数据读写的线程组
        NioEventLoopGroup workGroup = new NioEventLoopGroup(0, new DefaultThreadFactory("worker"));
    
        try {
            // 设置对应的线程组
            serverBootstrap.group(bossGroup, workGroup);
            final MyEchoServerHandler serverHandler = new MyEchoServerHandler();
            serverBootstrap.childHandler(new ChannelInitializer<NioSocketChannel>() {
                @Override
                protected void initChannel(NioSocketChannel ch) {
                    ChannelPipeline pipeline = ch.pipeline();
                    pipeline.addLast(new ServerIdleCheckHandler());
                    pipeline.addLast(new LoggingHandler(LogLevel.INFO));
                    pipeline.addLast(serverHandler);
                }
            });
    
            // 启动 Netty
            ChannelFuture f = serverBootstrap.bind(8888).sync();
            // 资源优雅释放
            f.channel().closeFuture().sync();
        } finally {
            // 资源优雅释放
            bossGroup.shutdownGracefully();
            workGroup.shutdownGracefully();
        }
    }

idea技巧

查看类的继承关系图

双击选中想要查看的类 ==> 右键 ==> Diagrams ==> ShowDiagramPopup

2022123021363371315.png

总结

本篇文章我们对 Netty 的启动类进行了逐行分析, 也让我对Netty的大概配置有了更全面的了解, 每天进步一丢丢, 加油

本文内容到此结束了

如有收获欢迎点赞?收藏?关注✔️,您的鼓励是我最大的动力。

如有错误❌疑问?欢迎各位大佬指出。

我是 宁轩 , 我们下次再见