Java中UDP DatagramSocket线程的高CPU使用率

sr4lhrrt  于 2023-02-18  发布在  Java
关注(0)|答案(2)|浏览(176)

我正在运行一个多线程的java服务器应用程序,它在3个不同的线程上从3个不同的多播源(端口)接收UDP数据包。
它运行在一个最新的双插槽redhat盒子上(总共8核(4x 2cpu),没有超线程)。
“top”命令显示CPU使用率为250~ 300%。shift-H显示2个线程的使用率约为99%,1个线程的使用率为70%。快速线程jstack分析显示这些线程对应于我的UDP处理线程。
考虑到CPU速度与UDP消息速率(大约300 msg/s,有效负载大约250字节),我对CPU使用率的水平感到有点惊讶,我正在对此进行调查。有趣的是,注意到第三个线程(对应于较低的CPU使用率)具有较低的数据速率(50~100 msg/s)
我已经包含了一些调试代码来测量大部分时间花在哪里,它似乎在DatagramSocket的“receive()”方法中:

_running    = true;
_buf        = new byte[300];
_packet     = new DatagramPacket(_buf, _buf.length);

while(_running) {
    try {
        long t0 = System.nanoTime();
        _inSocket.receive(_packet);
        long t1 = System.nanoTime();
        this.handle(_packet);
        long t2 = System.nanoTime();
        long waitingAndReceiveTime = t1-t0;
        long handleTime = t2-t1;
        _logger.info("{} : {} : update : {} : {}", t1, _port, waitingAndReceiveTime, handleTime);
    }
    catch(Exception e) {
        _logger.error("Exception while receiving multicast packet", e);
    }
}

handleTime平均为4000 ns,速度极快,无法对CPU使用率负责。waitingAndReceiveTime要高得多,从大约30,000 ns到几毫秒。我理解该方法是阻塞的,因此时间包括阻塞时间和接收时间。
我有几个问题:
1.我是不是应该怀疑有些事情很奇怪
1.我认为“receive()”是阻塞的,它不应该“浪费”CPU周期,所以等待部分不应该对高CPU使用率负责,对吗?
1.是否有一种方法可以将时间阻塞的测量与接收方法中接收数据报的时间分开?
1.那么是什么导致了如此高的CPU使用率呢?

EDIT:我调整了中断合并参数,将rx-usecs设置为0,将rx-frames设置为10。现在我可以看到以下内容:

  • UPD消息确实以10个为一组显示:对于每个组,第一条消息具有长的等待和接收时间(〉= 1 ms),并且接下来的9条消息具有短得多的等待和接收时间(~ 2000 ns)。(handleTime是相同的)
  • CPU使用率降低!前2个线程的CPU使用率降至约55%。

我还是不知道如何解决这个问题

ukqbszuj

ukqbszuj1#

不是真正的答案,而是:
有一点我可以向你保证,这不是Java代码。我用Python做了一个多线程UDP服务器,它做了同样的事情,CPU使用率在3到4秒内跳到100%。我猜这真的与UDP本身有关,因为我也做了一个多线程TCP服务器,它几乎没有达到10%的CPU使用率。
下面是代码:

import socket
from _thread import*
import threading
import time
def threaded(s,serverIP,serverPort):
    while True:
        try:
            d = s.recvfrom(128)
            data = d[0]
            addr = d[1]
            message= str(data)
            if (message== "b'1'"):
                time.sleep(5)
            s.sendto(str.encode(message) , addr)
            print(message)
        except:
            break
    s.close()

def Main():
    serverPort = 11000
    serverIP= "127.0.0.1"
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.bind((serverIP, serverPort))

    while True:
        start_new_thread(threaded, (s,serverIP,serverPort))
    s.close)

if __name__ == '__main__':
    Main()

注:
如果你找到了答案,请告诉我。祝你好运。

ecbunoof

ecbunoof2#

11年来,我一直在为这个问题绞尽脑汁。我写了一个家庭自动化应用程序,它使用UDP与各个房间的控制器进行通信。这浪费了一个完整的处理器内核(100%处理器使用率)来接收UDP数据报,无论我做什么。我去做了一个完整的分析调查,它看起来像是如何UDP接收是在Java中实现的。我已经更新了Java无数次(实际上已经11年了),最近还将其从PC迁移到Raspberry Pi 4。即使在Raspberry上,它仍然使用一个完整的核心来接收UDP数据包...这可能是一个简单的错误,这些年来一直没有被检测到,或者可能是有原因的,你可以'我不是一个合适的开发人员,所以我不敢向Oracle发布一个合适的bug报告。

相关问题