参数调优
参数设置有两种,分别是客户端参数和服务端参数:
- 客户端:
Bootstrap bootstrap = new Bootstrap();
bootstrap.option()
- 服务端:
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.option() // 针对ServerSocketChannel参数配置
serverBootstrap.childOption() // 针对SocketChannel参数配置
1. CONNECT_TIMEOUT_MILLIS
属于SocketChannal参数, 用在客户端建立连接时,如果在指定毫秒内无法连接,会抛出timeout异常。
NioEventLoopGroup group = new NioEventLoopGroup();
try {
Bootstrap bootstrap = new Bootstrap();
bootstrap.channel(NioSocketChannel.class);
bootstrap.group(group);
bootstrap.option(ChannelOption.CONNECT_TIMEOUT_MILLIS, 5000);
Channel channel = bootstrap.connect("localhost", 8080).sync().channel();
channel.closeFuture().sync();
} catch (Exception e) {
log.error("client error", e);
} finally {
group.shutdownGracefully();
}
运行结果: 会发现运行时没等待5s就报错超时了,没超过2s就返回一个底层java.net.ConnectException,这是因为底层发现服务端根本没开,也就根本连不上,就不再等待了。
区别CONNECT_TIMEOUT_MILLIS和SO_TIMEOUT
SO_TIMEOUT主要用在阻塞IO(Netty中基本用不到,Netty使用NIO),阻塞IO中accept,read等都是无限等待的,如果不希望永远阻塞,使用它调整超时时间。
2. SO_BACKLOG
属于ServerSocketChannal参数,用来设置全连接队列的大小。而全连接队列是TCP中的一个概念。
2.1 TCP握手过程
- 第一次握手,client发送SYN到server,状态修改为SYN_SEND,server收到,状态改变为SYN_REVD,并将该请求放入sync queue队列
- 第二次握手,server回复SYN + ACK给client,client收到,状态改变为 ESTABLISHED,并发送ACK给server
- 第三次握手,server收到ACK,状态改变为ESTABLISHED,将该请求从sync queue放入accept queue
在linux2.2之前,backlog大小包括了两个队列的大小,在2.2之后,分别用下面两个参数来控制
- sync queue - 半连接队列
- 大小通过 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,在
syncookies
启用的情况下,逻辑上没有最大值限制,这个设置便被忽略
- 大小通过 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,在
- accept queue - 全连接队列
- 其大小通过/proc/sys/net/core/somaxconn指定,在使用listen函数时,内核会根据传入的backlog参数与系统参数,取二者的较小值
- 如果accpet queue队列满了,server将发送一个拒绝连接的错误信息到client(ConnectRefuse异常)
可以看到比较重要的参数就是全连接队列大小,因为它关系到当前服务端最大TCP连接数:
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);
2.2 默认值
Netty中默认的全连接队列大小,查看NetUtil类提供默认值: Centos7.9中:
[jack@hadoop102 ~]$ cat /proc/sys/net/core/somaxconn
128
3. ulimit -n
属于操作系统参数, 用于限制用户或进程最大文件描述符数命令。查看当前用户最大文件描述符数:
[jack@hadoop102 ~]$ ulimit -n
65536
修改用户最大文件描述符数, 编辑/etc/security/limits.conf:
# 格式:<domain> <type> <item> <value>
username hard nofile 65536 # 用户名的硬限制
username soft nofile 65536 # 用户名的软限制
@groupname hard nproc 4096 # 组的硬限制
@groupname soft nproc 2048 # 组的软限制
4. TCP_NODELAY
属于SocketChannal参数, 默认开启,建议关闭。
5. SO_SNDBUF & SO_RCVBUF
- SO_SNDBUF属于SocketChannal参数
- SO_RCVBUF既可用于SocketChannal参数,也可以用于ServerSocketChannal参数(建议设置到ServerSocketChannal上)
不建议手动修改,操作系统会按照实际内存情况动态配置。
6. ALLOCATOR
属于SocketChannal参数, 用来创建ByteBuf。在ChannelHandler里面:
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
ByteBuf byteBuf = ctx.alloc().buffer();
log.info("byteBuf:{}", byteBuf);
log.info("msg:{}", msg);
}
如果想要控制ctx.alloc().buffer()
使用pooled和堆内存,可以加上命令行参数:
-Dio.netty.allocator.type=unpooled -Dio.netty.noPreferDirect=true
7. RCVBUF_ALLOCATOR
属于SocketChannal参数, 控制netty接收缓冲区大小, 负责入站数据的分配,决定入站缓冲区的大小(并可动态调整),统一采用direct直接内存,具体池化还是非池化由 allocator决定。
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
log.info("msg:{}", msg);
}
上面代码中的msg参数,它是DirectByteBuf类型,因为netty在io读取上会直接使用堆外内存,这样可以读取系统的缓冲区,提高读取效率。