Java NIO UDP多播 - 丢弃的数据包

时间:2016-04-04 22:47:55

标签: java udp nio multicast

我们有一个使用UDP多播发送日志事件的日志系统。事件发生率约为10,000个事件/秒,平均事件大小约为2Kb。

在每次测试期间,应用程序的NIO版本会丢失一小部分事件(大约12M内的约2000个事件)。有没有人在这方面有任何见解?

示例代码: 没有NIO:

    byte[] buf = new byte[65535];
    DatagramPacket packet = new DatagramPacket(buf, buf.length);

    try {
        while (!Thread.currentThread().isInterrupted()) {

            socket.receive(packet);

            final byte[] tmpBuffer = new byte[packet.getLength()];
            System.arraycopy(packet.getData(), 0, tmpBuffer, 0,
                    tmpBuffer.length);

            insertToNonBlockingQueue(tmpBuffer, packet.getSocketAddress());
        }
    } catch (Throwable t) {
        throw new RuntimeException("Encountered exception in Acceptor", t);
    } finally {
        Util.closeQuietly(socket);
    }

使用NIO:

    ByteBuffer inBuffer = ByteBuffer.allocate(65535);
    try {
        while (!Thread.currentThread().isInterrupted()) {

            SocketAddress addr = channel.receive(inBuffer);

            inBuffer.flip();

            final byte[] tmpBuffer = new byte[inBuffer.limit()];
            inBuffer.get(tmpBuffer);

            inBuffer.clear();

            insertToNonBlockingQueue(tmpBuffer, addr);
        }
    } catch (ClosedByInterruptException ex) {
        log.info("Channel closed by interrupt"); // normal shutdown
    } catch (Throwable t) {
        throw new RuntimeException("Encountered exception in Acceptor", t);
    } finally {
        Util.closeQuietly(channel);
    }

两个侦听器都在同一时间运行,每次非NIO版本捕获所有日志事件,而NIO版本错过了一些。这不是网络问题,因为即使我们将代码切换到计算机上的其他版本,它也会出现相同的行为。

2 个答案:

答案 0 :(得分:2)

您忘记了compact()之后的clear()get()缓冲区。缓冲区填满后,此代码将立即开始丢弃数据包。

DatagramPacket情况应该在每次接收之前重置数据包长度。

将实际的DatagramPacket插入队列并在每次接收时使用新的,或在NIO情况下合成一个新的更简单。这样你就不需要新的数据结构了。

答案 1 :(得分:1)

除了EJP所说的,你应该使用一个直接字节缓冲区作为读缓冲区,否则套接字将在内部分配一个DBB,然后从那里复制到你的BB中,然后你将它从那里复制到数组中。即这是一个多余的复制操作。

此外,您可能希望将套接字的接收缓冲区配置为可容纳多个数据包的大小。