前言
这是源码共读活动的第二篇文章, 在上一章节中我们分析了 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的大概配置有了更全面的了解, 每天进步一丢丢, 加油