“丢失”的UDP数据包(JBoss + DatagramSocket)

5
我参与开发了一款基于JBoss+EJB的企业应用程序。我的模块需要处理大量的UDP数据包。我进行了一些负载测试,发现当发送11ms时间间隔的数据包时,一切都很正常,但是当发送10ms时间间隔的数据包时,有一些数据包会丢失。在我看来这相当奇怪,但我已经多次进行了10ms和11ms时间间隔的负载测试比较,结果总是一样的(10ms - 一些“丢失”的数据包,11ms - 一切正常)。
如果同步出了问题,我预计在11ms测试中也会有类似的情况(至少会丢失一个数据包,或者至少会有一个错误的计数器值)。因此,如果不是因为同步问题,那么也许我接收数据包的DatagramSocket没有按照预期工作。
我发现接收缓冲区大小(SO_RCVBUF)默认为57344(可能是基于底层IO网络缓冲区的依赖)。我怀疑,当该缓冲区满时,新到达的UDP数据报将被拒绝。我尝试将此值设置得更高,但我注意到如果我夸大其词,缓冲区会返回其默认大小。如果它是基于底层层的依赖,那么我如何从JBoss层找到特定操作系统/网络卡的最大缓冲区大小?
这是否可能是接收缓冲区大小引起的问题,或者57344的值已足够处理大多数情况?您是否有类似问题的经验?
我的DatagramSocket上没有设置超时。我的UDP数据报包含约70个字节的数据(不包括数据报头)。
[编辑] 我必须使用UDP,因为我接收Cisco Netflow数据 - 这是网络设备用于发送一些流量统计信息的协议。此外,我无法影响发送字节的格式(例如,我无法添加数据包计数器等)。不是所有数据包都会被处理(某些数据报可能会丢失),但我希望能够处理大部分数据包。在10ms时间间隔测试期间,约30%的数据包丢失。
很少有可能是慢速处理导致了这个问题。当前的单例组件持有对DatagramSocket的引用,在循环中调用receive方法。当接收到数据包时,它将被传递到队列,并由从池中选择的无状态组件进行处理。“Facade” Singleton只负责接收数据包并将其传递给处理(它不等待处理完成事件)。
谢谢, Piotr

为什么需要使用UDP?在有性能分析表明需要转向UDP的饱和层之前,我会一直使用TCP。此外,我的经验是UDP的数据通常会被复制。“这是当前状态。”因此,如果你错过了这个数据包,不要担心,因为很快就会有另一个数据包到来! - corsiKa
“处理速度慢导致此问题的可能性不是很大。” -- 我认为是这样的,因为10毫秒很短,大约与线程时间片的持续时间相同。 - ChrisW
那个10毫秒的时间可能因不同的操作系统/处理器而有所不同。你能否降低数据发送频率,比如100毫秒? - Jeff Storey
@ChrisW 也许你是对的,但如果是这样的话,11毫秒间隔的情况下应该也会出现数据包丢失吧?(我没有观察到任何情况) @Jeff Storey 数据包发送的速度只取决于外部网络设备。我只负责接收和处理。 - omnomnom
4个回答

5
UDP不保证传递,因此您可以调整参数,但不能保证消息的传递,特别是在大数据传输的情况下。如果您需要保证传递,请改用TCP。如果需要(或希望)使用UDP,则可以为每个数据包编码一个编号,并发送预期的数据包数量。例如,如果您发送了10个大数据包,可以包括信息:包1/10,包2/10等等。这样,您至少可以知道是否没有接收到所有数据包。如果没有收到它们,您可以发送请求重新发送缺失的数据包。

是的,一旦您进行大量传输,数据包将超过MTU。在这种情况下,数据包会被分段。使用UDP,不能保证片段按顺序接收,但底层协议(IP)将丢弃一个数据包,如果不是这种情况,因此UDP永远不会单独接收数据包。这就是为什么使用UDP的大型数据包要不可靠得多 - 因为UDP保留边界时会发生更多的丢弃 :) - Chris Dennett
谢谢回答,不幸的是我无法影响数据包的生成方式(也不能切换到TCP)- 请查看我编辑过的帖子。 - omnomnom

3

UDP天生是不可靠的。

数据报可以在发送方和接收方之间的任何位置被丢弃,甚至在接收方低于您的代码的级别内部。将recv缓冲区设置为更大的大小可能有助于网络代码在您的机器中缓冲更多数据报,但您应该预计仍然会丢失一些数据报。

如果您的recv逻辑需要太长时间(即比一个新数据报到达所需的时间长),那么您将永远落后,并且最终会错过数据报。您能做的就是确保您的recv代码尽可能快地运行,可以将入站数据报移动到队列中并“稍后”或在另一个线程上处理它,但这只会使您的问题变成一个队列不断增长的问题。

[关于您的编辑...] 那你的队列是如何处理的,生产者和消费者之间的锁定是如何工作的?将您的代码更改为recv逻辑仅增加计数并丢弃数据,然后循环回来,看看是否丢失了较少的数据报;无论如何,UDP都是不可靠的,您将有一些数据报被丢弃,您应该对此有所预期并进行处理。担心它意味着您正在关注错误的问题;利用您获得的数据并假设您不会获得太多数据,那么即使网络拥塞并且大多数数据报被丢弃,您的程序也将正常工作。

总之,这就是UDP的本质。


感谢您的回答。生产者持有对DatagramSocket的引用,生产者是JBossEJB3ext @Service bean(单例)。它在while(true)循环中调用阻塞接收方法。当它接收到数据包时,通过AsyncUtils将DatagramSocket传递给池化的无状态bean之一(异步调用)。我将尝试禁用处理并仅计算传入的数据包,然后会给出反馈。 - omnomnom

2

在你的测试中,似乎缓冲区只能容纳最多两个数据包,因此如果每个数据包小于28KB,那么这应该是可以的。

正如您所知,UDP是有丢包的情况,但您应该能够在10毫秒内发送多个数据包。我建议您编写一个简单的接收器,仅侦听数据包以确定是您的应用程序还是网络/操作系统层面的问题。(我怀疑后者)


@GeorgeThomas 你可能需要在可以监控UDP数据包的地方进行测试,例如使用wireshark。没有什么好的理由让UDP数据包停止传输。只有在负载下才会看到随机数据包丢失。 - Peter Lawrey
当您尝试重新连接时会发生什么?例如,当您在不同的端口上多次连接时会发生什么?它们都同时停止,还是只有一个停止? - Peter Lawrey
当我杀掉应用并重新打开应用时,它又可以正常工作了。有没有办法知道连接是否丢失并重新连接呢?我还没有尝试不同的端口。 - George Thomas
我会尝试使用Wireshark。 - George Thomas
1
服务器仍在发送,我猜是因为iOS应用程序已经成功接收数据包,所以我不认为这是服务器的问题。 - George Thomas
显示剩余6条评论

1

我不懂Java,但是...这个API是否允许您调用异步监听/接收数据报:

  • 使用操作系统API进行接收(将应用程序级缓冲区作为参数传递)
  • (等待没有接收到任何内容...)
  • (操作系统从网络接收到一些内容...)
  • 操作系统将接收到的数据包放入缓冲区并完成/返回您的API调用

如果是这样的话,我建议您执行多个并发的API调用实例,以便有多个并发的应用程序级缓冲区可以接收多个数据包。


感谢您的回答。目前,使用单例门面模式 - 它持有对DatagramSocket的引用。它在循环中调用receive()方法 - 这是一个阻塞操作。如果接收到数据报,则将其传递给处理组件(池化无状态bean)。容器管理同步。我认为这种方法可以防止同步问题,并且还具有可扩展性。只允许一个DatagramSocket实例(每个IP和端口绑定)。 - omnomnom
@Piotrek 我建议你尝试更改这个:不要使用一个同步/阻塞操作,而是尝试进行多个异步/非阻塞操作... 这样操作系统就可以拥有多个并发的应用程序级缓冲区来接收多个数据包。 - ChrisW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接