Skip to content

参数调优

参数设置有两种,分别是客户端参数和服务端参数:

  1. 客户端:
java
Bootstrap bootstrap = new Bootstrap();
bootstrap.option()
  1. 服务端:
java
ServerBootstrap serverBootstrap = new ServerBootstrap();
serverBootstrap.option()   // 针对ServerSocketChannel参数配置
serverBootstrap.childOption() // 针对SocketChannel参数配置

1. CONNECT_TIMEOUT_MILLIS

属于SocketChannal参数, 用在客户端建立连接时,如果在指定毫秒内无法连接,会抛出timeout异常。

java
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();
}

运行结果:
Alt text 会发现运行时没等待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握手过程

Alt text

  1. 第一次握手,client发送SYN到server,状态修改为SYN_SEND,server收到,状态改变为SYN_REVD,并将该请求放入sync queue队列
  2. 第二次握手,server回复SYN + ACK给client,client收到,状态改变为 ESTABLISHED,并发送ACK给server
  3. 第三次握手,server收到ACK,状态改变为ESTABLISHED,将该请求从sync queue放入accept queue

在linux2.2之前,backlog大小包括了两个队列的大小,在2.2之后,分别用下面两个参数来控制

  1. sync queue - 半连接队列
    • 大小通过 /proc/sys/net/ipv4/tcp_max_syn_backlog 指定,在syncookies启用的情况下,逻辑上没有最大值限制,这个设置便被忽略
  2. accept queue - 全连接队列
    • 其大小通过/proc/sys/net/core/somaxconn指定,在使用listen函数时,内核会根据传入的backlog参数与系统参数,取二者的较小值
    • 如果accpet queue队列满了,server将发送一个拒绝连接的错误信息到client(ConnectRefuse异常)

可以看到比较重要的参数就是全连接队列大小,因为它关系到当前服务端最大TCP连接数:

java
serverBootstrap.option(ChannelOption.SO_BACKLOG, 1024);

2.2 默认值

Netty中默认的全连接队列大小,查看NetUtil类提供默认值: Alt text Centos7.9中:

sh
[jack@hadoop102 ~]$ cat /proc/sys/net/core/somaxconn
128

3. ulimit -n

属于操作系统参数, 用于限制用户或进程最大文件描述符数命令。查看当前用户最大文件描述符数:

sh
[jack@hadoop102 ~]$ ulimit -n
65536

修改用户最大文件描述符数, 编辑/etc/security/limits.conf:

sh
# 格式:<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里面:

java
@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和堆内存,可以加上命令行参数:

sh
-Dio.netty.allocator.type=unpooled -Dio.netty.noPreferDirect=true

7. RCVBUF_ALLOCATOR

属于SocketChannal参数, 控制netty接收缓冲区大小, 负责入站数据的分配,决定入站缓冲区的大小(并可动态调整),统一采用direct直接内存,具体池化还是非池化由 allocator决定。

java
@Override
public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {
    log.info("msg:{}", msg);
}

上面代码中的msg参数,它是DirectByteBuf类型,因为netty在io读取上会直接使用堆外内存,这样可以读取系统的缓冲区,提高读取效率。