zoukankan      html  css  js  c++  java
  • Netty之UDP丢包解决

    程序背景

    程序是Java编写,基于Netty框架写的客户端及服务端。

    现象

    客户端大数据量持续发UDP数据,作为UDP服务器出现了部分数据频繁丢失触发程序自身重传逻辑。
    通过GC日志对比发现丢包的时间点偶有处于Full GC,说明Java程序接收间歇性stop world的不是根因。

    观察Udp的dump

    通过watch -n 1 -d 'cat /proc/net/udp >> /usr/udpDump.txt'在发送数据的过程中持续观察Udp缓冲区的状况

    • /proc/net/udp是瞬时的Udp socket dump,另有/proc/net/udp6用于监控IPv6
    • dump输出里的tx_queue是发送缓冲区,rx_queue是接收缓冲区,单位都是byte
    • 如果应用层收发效率足够好,正常情况下tx_queuerx_queue两者永远是0
    • 发送数据过程中频现rx_queue>0,说明Udp缓冲区有堆积现象
    • 输出解释见How to monitor Linux UDP buffer available space?Meaning of fields in /proc/net/udp

    观察Udp的stats

    通过watch -n 1 -d 'netstat -su >> /usr/udpStats.txt'持续观察Udp的stats输出

    • 输出里packets received的值指应用层从读入缓冲区里取走的包
    • 输出里packets to unknown port received的值指端口无应用监听而分发至该端口的包
    • 输出里packet receive errors的值指Udp接收错误数,正常情况下应该是0,在观察中不停增加,证明出现Udp包溢出接收缓冲区的情况
      • 发生错误的包数与接收错误数非一一对应
    • 资料参见Udp Packet Receive ErrorsUdp packet drops and packet receive error difference

    解决问题

    服务端代码优化

    定论:
    默认的UDP socket读缓冲区不够引发系统丢弃UDP包。
    服务端代码优化设置UDP socket读缓冲区为2M,代码如下

    Bootstrap selfBootStrap = new Bootstrap();
    selfBootStrap.group(group);
    selfBootStrap.channel(NioDatagramChannel.class);
    selfBootStrap.option(ChannelOption.SO_BROADCAST, true);
    // 这一行设置了UDP socket读缓冲区为2M
    selfBootStrap.option(ChannelOption.SO_RCVBUF, 1024 * 2048);
    selfBootStrap.handler(channelInitializer);
    selfBootStrap.localAddress(selfPort);
    

    理论上Udp socket读缓冲区设置为2M在我们的测试场景下已经足够。优化后虽有改善但仍有丢包现象。

    Linux系统级调优

    定论:
    应用层设置了UDP socket缓冲区不一定在Linux上生效,原因在于Linux对Udp socket缓冲区存有系统级限制,超过该限制的缓冲区大小无效。

    Windows对socket的缓冲区没有限制

    要点分析:
    Linux通过net.core.rmem_max控制Udp的读缓冲区,通过net.core.wmem_max控制Udp的写缓冲区。
    在程序的启动sh脚本里添加如下代码修改net.core.rmem_max

    # 服务器默认UDP读缓冲区最大128K。修改为2G。解决UDP丢包问题
    rmemCount=`cat /etc/sysctl.conf|grep "net.core.rmem_max" | wc -l`
    if [ ${rmemCount} -eq 0 ]
    then
        echo "net.core.rmem_max = 2147483647" >> /etc/sysctl.conf
        sysctl -p
    fi
    

    脚本的作用就是修改/etc/sysctl.conf文件,并键入sysctl -p命令使自定义参数生效。
    资料参见Improving UDP Performance by Configuring OS UDP Buffer LimitsUDP Drops on Linux

  • 相关阅读:
    SQLServer如何批量替换某一列中的某个字符串
    能成为一名合格的Java架构师
    来看看Uber的司机支持服务签到及预约系统的架构设计思路
    什么是三层架构?你真的理解分层的意义吗?
    京东7Fresh新零售架构设计分析
    解密京东千亿商品系统核心架构
    因特尔黑科技:黑暗中快速成像系统
    分布式缓存架构设计
    各种排序算法汇总小结
    系统架构设计之-任务调度系统的设计
  • 原文地址:https://www.cnblogs.com/lewh/p/6251502.html
Copyright © 2011-2022 走看看