如何监控Linux UDP缓冲区可用空间?
我在 Linux 上有一个 Java 应用程序,它打开 UDP 套接字并等待消息。
在重负载下几个小时后,出现数据包丢失,即数据包由内核接收,但不是由我的应用程序接收(我们在嗅探器中看到丢失的数据包,我们在 netstat 中看到 UDP 数据包丢失,我们没有看到这些数据包)在我们的应用程序日志中)。
我们尝试扩大套接字缓冲区,但这没有帮助 - 我们开始丢失数据包,但仅此而已。
为了调试,我想知道在任何给定时刻操作系统 udp 缓冲区有多满。谷歌搜索,但没有找到任何东西。你能帮助我吗?
PS 各位,我知道 UDP 不可靠。但是 - 我的计算机接收所有 UDP 消息,而我的应用程序无法使用其中的一些消息。我想最大限度地优化我的应用程序,这就是问题的原因。谢谢。
I have a java app on linux which opens UDP socket and waits for messages.
After couple of hours under heavy load, there is a packet loss, i.e. the packets are received by kernel but not by my app (we see the lost packets in sniffer, we see UDP packets lost in netstat, we don't see those packets in our app logs).
We tried enlarging socket buffers but this didnt help - we started losing packets later then before, but that's it.
For debugging, I want to know how full the OS udp buffer is, at any given moment. Googled, but didn't find anything. Can you help me?
P.S. Guys, I'm aware that UDP is unreliable. However - my computer receives all UDP messages, while my app is unable to consume some of them. I want to optimize my app to the max, that's the reason for the question. Thanks.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(4)
UDP 是一个完全可行的协议。这是同样的老情况:正确的工具适合正确的工作!
如果您有一个程序等待 UDP 数据报,然后在返回等待另一个数据报之前处理它们,那么您的处理时间需要始终快于数据报最坏情况的到达率。如果不是,则 UDP 套接字接收队列将开始填满。
这对于短突发来说是可以容忍的。队列完全执行其应该执行的操作——对数据报进行排队,直到您准备好为止。但是,如果平均到达率经常导致队列积压,那么就需要重新设计您的程序了。这里有两个主要选择:通过巧妙的编程技术减少经过的处理时间,和/或多线程程序。还可以在程序的多个实例之间采用负载平衡。
如前所述,在 Linux 上,您可以检查 proc 文件系统以获取有关 UDP 最新情况的状态。例如,如果我
cat
/proc/net/udp
节点,我会得到如下内容:由此,我可以看到用户 id 1006 拥有的套接字,正在侦听端口 0x231D (8989),并且接收队列约为 128KB。由于 128KB 是我系统上的最大大小,这告诉我我的程序在跟上到达的数据报方面非常弱。到目前为止,已经发生了 2237 次丢弃,这意味着 UDP 层无法将更多数据报放入套接字队列中,而必须丢弃它们。
您可以观察程序随时间的行为,例如使用:
另请注意,netstat 命令执行相同的操作:
netstat -c --udp -an
我的 weenie 程序的解决方案将是多-线。
干杯!
UDP is a perfectly viable protocol. It is the same old case of the right tool for the right job!
If you have a program that waits for UDP datagrams, and then goes off to process them before returning to wait for another, then your elapsed processing time needs to always be faster than the worst case arrival rate of datagrams. If it is not, then the UDP socket receive queue will begin to fill.
This can be tolerated for short bursts. The queue does exactly what it is supposed to do – queue datagrams until you are ready. But if the average arrival rate regularly causes a backlog in the queue, it is time to redesign your program. There are two main choices here: reduce the elapsed processing time via crafty programming techniques, and/or multi-thread your program. Load balancing across multiple instances of your program may also be employed.
As mentioned, on Linux you can examine the proc filesystem to get status about what UDP is up to. For example, if I
cat
the/proc/net/udp
node, I get something like this:From this, I can see that a socket owned by user id 1006, is listening on port 0x231D (8989) and that the receive queue is at about 128KB. As 128KB is the max size on my system, this tells me my program is woefully weak at keeping up with the arriving datagrams. There have been 2237 drops so far, meaning the UDP layer cannot put any more datagrams into the socket queue, and must drop them.
You could watch your program's behaviour over time e.g. using:
Note also that the netstat command does about the same thing:
netstat -c --udp -an
My solution for my weenie program, will be to multi-thread.
Cheers!
Linux 提供了文件
/proc/net/udp
和/proc/net/udp6
,其中列出了所有打开的 UDP 套接字(分别适用于 IPv4 和 IPv6)。在它们中,tx_queue
和rx_queue
列以字节为单位显示传出和传入队列。如果一切按预期工作,您通常不会在这两列中看到任何不为零的值:应用程序生成数据包后,它们就会通过网络发送,一旦这些数据包从网络到达,您的应用程序就会唤醒并接收它们(
recv
调用立即返回)。如果您的应用程序打开了套接字,但未调用recv
来接收数据,或者处理此类数据的速度不够快,您可能会看到rx_queue
上升。Linux provides the files
/proc/net/udp
and/proc/net/udp6
, which lists all open UDP sockets (for IPv4 and IPv6, respectively). In both of them, the columnstx_queue
andrx_queue
show the outgoing and incoming queues in bytes.If everything is working as expected, you usually will not see any value different than zero in those two columns: as soon as your application generates packets they are sent through the network, and as soon those packets arrive from the network your application will wake up and receive them (the
recv
call immediately returns). You may see therx_queue
go up if your application has the socket open but is not invokingrecv
to receive the data, or if it is not processing such data fast enough.rx_queue 会告诉您任何给定时刻的队列长度,但它不会告诉您队列有多满,即高水位线。无法持续监控该值,也无法以编程方式获取该值(请参阅 如何获取 UDP 套接字的排队数据量?)。
我可以想象监控队列长度的唯一方法是将队列移动到您自己的程序中。换句话说,启动两个线程——一个以最快的速度读取套接字并将数据报转储到队列中;另一个以最快的速度读取套接字并将数据报转储到队列中。另一个是您的程序从该队列中拉出并处理数据包。当然,这是假设您可以确保每个线程都位于单独的 CPU 上。现在您可以监控自己队列的长度并跟踪最高水位线。
rx_queue will tell you the queue length at any given instant, but it will not tell you how full the queue has been, i.e. the highwater mark. There is no way to constantly monitor this value, and no way to get it programmatically (see How do I get amount of queued data for UDP socket?).
The only way I can imagine monitoring the queue length is to move the queue into your own program. In other words, start two threads -- one is reading the socket as fast as it can and dumping the datagrams into your queue; and the other one is your program pulling from this queue and processing the packets. This of course assumes that you can assure each thread is on a separate CPU. Now you can monitor the length of your own queue and keep track of the highwater mark.
该过程很简单:
如果需要,请暂停申请流程。
打开 UDP 套接字。如果需要,您可以使用
/proc//fd
从正在运行的进程中获取它。或者您可以将此代码添加到应用程序本身并向其发送一个信号——当然,它已经打开了套接字。尽快在紧密循环中调用
recvmsg
。计算您获得了多少数据包/字节。
这将丢弃当前缓冲的所有数据报,但如果这破坏了您的应用程序,则您的应用程序已经被破坏了。
The process is simple:
If desired, pause the application process.
Open the UDP socket. You can snag it from the running process using
/proc/<PID>/fd
if necessary. Or you can add this code to the application itself and send it a signal -- it will already have the socket open, of course.Call
recvmsg
in a tight loop as quickly as possible.Count how many packets/bytes you got.
This will discard any datagrams currently buffered, but if that breaks your application, your application was already broken.