前言
这是 源码共读活动 的第二篇文章, 在上一章节中我们分析了 backlog 的作用, 接下来我们看一看 张师傅 为我们准备的Netty
启动类都进行了哪些配置吧
往期文章:
没有拉取代码的小伙伴可以通过git
输入以下命令拉一下代码, 让我们保持代码的同步
git clone https://github.com/arthur-zhang/netty-study.git
配置分析
首先, 我们可以看到这个项目的目录结构很简单, 只有三个类, 通过名称可以知道, 分别是两个 Headler 类和一个 启动类 MyServer
, 本篇文章也是主要针对MyServer
来进行讲解的
MyServer详解
下图是MyServer
类的全部代码, 可以看到实际上没有多少,一图装得下, 接下来我们一起逐行代码去学习
ServerBootstrap serverBootstrap = new ServerBootstrap();
Bootstrap
的意思是引导, 在Netty
应用中, 通常也是由Bootstrap
开始的, 他的作用就是对Netty
进行配置
翻译工具为`uTools`插件 词典
本次主要对Netty
中的ServerBootstrap
进行讲解, ServerBootstrap
是服务端引导类, 他的继承关系如下所示
在
Netty
中,AbstractBootstrap
的实现类主要有两种, 分别是服务端ServeBootstrap
和客户端Bootstrap
, 对Bootstrap
感兴趣的小伙伴可以评论区留言
serverBootstrap.channel() 方法;
serverBootstrap.channel()
方法是用来设置Netty
对应的通道的
在执行该方法的时候可以看到, 他实际上是调用了ServeBootstrap
类的抽象父类AbstractBootstrap
在channel
方法中实际上是初始化了一个ReflectiveChannelFactory
工厂类, 同时将该工厂对象保存在AbstractBootstrap
抽象类的channelFactory
属性中, 后续可以调用生成channel
对象, 目前只是保存
下面我们看一下在ReflectiveChannelFactory
工厂类的初始化和后续调用生成channel
对象的方法
NioServerSocketChannel.class
NioServerSocketChannel
是Netty
官方封装的, 用来代替或包装 JDK 原生的SocketChannel
对象, 他的继承关系图如下所示
过多的就不讲了, 在讲下去就该这个类的源码分析, 感兴趣的小伙伴可以评论区说出来
option()和 childOption() 方法
在Netty
中option()
方法主要是设置ServerChannel
的一些选项, 而childOption()
方法是用来设置ServerChannel
的子Channel
的选项
注: 如果是 客户端 , 因为是
Bootstrap
, 只会有option()
, 没有childOption()
, 所以设置的是 客户端Channel 的选项
所以, childOption()
方法是写在ServerBootstrap
类中, 而不是继承于AbstractBootstrap
抽象类的
option()
方法是写在了AbstractBootstrap
抽象类中, 记住他, 后面我们分析Netty
启动的时候还会看到
NioChannelOption
NioChannelOption
类是继承于ChannelOption
同时新增了几个方法, 那么我们主要讲一下ChannelOption
里面的常量信息
@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_SNDBUF 和 SO_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 方法
同时大家应该看到了, 在实现childHeadler()
方法的时候有创建一个类并实现了相应的方法, 他的作用就是初始化了一下 Channel 并把日志级别更改为 info
在 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
总结
本篇文章我们对 Netty 的启动类进行了逐行分析, 也让我对Netty
的大概配置有了更全面的了解, 每天进步一丢丢, 加油
本文内容到此结束了
如有收获欢迎点赞?收藏?关注✔️,您的鼓励是我最大的动力。
如有错误❌疑问?欢迎各位大佬指出。
我是 宁轩 , 我们下次再见