前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >Netty的写缓冲区容量一直为0,最后居然是网络带宽问题...

Netty的写缓冲区容量一直为0,最后居然是网络带宽问题...

原创
作者头像
叫我阿柒啊
修改2024-09-19 00:07:31
1110
修改2024-09-19 00:07:31
举报
文章被收录于专栏:Java放弃之路入门到放弃之路

前言

之前使用Netty消费Kafka中的数据,做实时数据上传的时候,数据总是积压,带宽总是在32Gbit/s,我在代码中对缓冲区做了一个容量监控,在发送的过程中一直提示容量不足不可写(后来拉扯了一年,证明是对端网络以及带宽问题,最后调整了网络和办卡,带宽达到了64Gbit/s)。后来为了收集问题证据,我还是学到了不少东西,包括今天要说的:缓冲区。

缓冲区

缓冲区,顾名思义就是用来做缓冲的,例如I/O中的BufferedInputStream,以及NIO里的Buffer。在我的应用中,数据传输底层使用的是socket(TCP),数据传输通常是由发送方和接收方组成,每一方都会有sendBuf和recvBuf。

sendBuf发送缓冲区,当应用程序需要发送数据时,数据首先被写入发送缓冲区。网络协议栈会从发送缓冲区中取出数据,进行协议处理(如TCP/IP封装),然后发送到网络上。

recvBuf是接收缓冲区,当数据从网络到达时,网络协议栈会将接收到的数据放入接收缓冲区。应用程序可以从接收缓冲区中读取数据进行处理。

如果应用程序处理数据的速度慢于网络接收速度,数据会暂时存储在接收缓冲区中,避免数据丢失。如果网络拥堵或接收端处理速度慢,数据会在发送缓冲区中等待,直到可以发送为止。

在数据传输中,我是客户端,对方是服务端。通过socket端口连接对面的服务进行发送,在数据发送过程中,其实是先将数据发送到客户端主机的sendBuf,然后再经过网络传输到对端主机的recvBuf,然后对面程序从recvBuf中读取数据。

Netty缓冲区

在Netty概念中,Buffer是数据载体的缓冲区,而在创建Netty客户端/服务端的时候,设置缓冲区就是数据在网络传输中的sendBuf和recvBuf。

代码语言:java
复制
bootstrap.childOption(ChannelOption.SO_SNDBUF, 32 * 1024); // 32KB
bootstrap.childOption(ChannelOption.SO_RCVBUF, 32 * 1024); // 32KB
ch.config().setWriteBufferHighWaterMark(30 * 1024); 
ch.config().setWriteBufferLowWaterMark(10 * 1024 * 1024);

前两个参数用来设置Netty中sendBuf和recvBuf的大小,下面两个参数用来设置sendBuf的高低水位线。

写当sendBuf中的字节数超过HighWaterMark高水位线时,Channel 将被认为是不可写的,channel.isWritable() 将返回 false。当sendBuf中的字节数降到LowWaterMark低水位线以下时,Channel 将重新变得可写,channel.isWritable() 将返回 true。

我在程序中的代码如下:

代码语言:java
复制
if (ctx.channel().isWritable()) {
    ctx.channel().writeAndFlush(xdr);
} else {
  logger.info("可用容量:" + ctx.channel().bytesBeforeUnwritable() + "需要释放容量:" + ctx.channel().bytesBeforeWritable());
}

结果就一直提示可用容量为0。

Linux缓冲区

在刚开始测试程序的时候,因为一直提示sendBuf容量不够,我就一股脑儿地修改Netty的参数,最后甚至修改到了100MB,但是发现没有什么用,后来发现Netty中的缓冲区大小,受到操作系统缓冲区大小的显示,上面提到的网络传输中的缓冲区,指的就是操作系统的缓冲区。

查看缓冲区

在Linux系统中,我们可以查看缓冲区的大小:

代码语言:bash
复制
# 查看当前的发送缓冲区最大值
sysctl net.core.wmem_max
# 查看当前的发送缓冲区默认值
sysctl net.core.wmem_default
# 等价于下面两条命令
cat /proc/sys/net/core/wmem_max
cat /proc/sys/net/core/wmem_default

通过执行上面的命令,我们可以看到发送缓冲区的大小为208KB。

修改缓冲区

我们可以通过sysctl命令临时修改缓冲区,但是这种方式主机重启之后就会重置。

代码语言:bash
复制
# 修改发送缓冲区最大值/默认值为10MB
sysctl -w net.core.wmem_max=10485760
sysctl -w net.core.wmem_default=10485760

# 修改接收缓冲区最大值/默认值为10MB
sysctl -w net.core.rmem_max=10485760
sysctl -w net.core.rmem_default=10485760

如果想要永久修改的话,可以使用下面的命令。

代码语言:bash
复制
echo "net.core.wmem_max=10485760" | sudo tee -a /etc/sysctl.conf
echo "net.core.wmem_default=10485760" | sudo tee -a /etc/sysctl.con

缓冲区监控

ss命令可以用于查看具体套接字的缓冲区使用情况。

代码语言:bash
复制
ss -m  -a -n 

执行情况如下:

rb就是可使用的recvBuf的容量,tb就是可使用的sendBuf的容量。

结语

Netty的写缓冲区控制应用层的数据写入行为,而操作系统的发送缓冲区控制实际的网络传输。操作系统的最大缓冲区大小限制了应用程序能够设置的最大缓冲区大小。如果操作系统的 wmem_max 或 rmem_max 小于你在 Netty 中设置的值实际的缓冲区大小将被限制在系统最大值。

所以如果sendBuf一直满的话,多考虑是网络带宽的问题,如果recvBuf满的话,多考虑应用程序的处理能力。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。

如有侵权,请联系 cloudcommunity@tencent.com 删除。

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • 缓冲区
  • Netty缓冲区
  • Linux缓冲区
    • 查看缓冲区
      • 修改缓冲区
        • 缓冲区监控
        • 结语
        领券
        问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档