2016-04-04 48 views
0

我们有一个使用UDP多播发送日志事件的日志记录系统。事件发生率约为10,000次/秒,平均事件大小约为2Kb。Java NIO UDP多播 - 丢弃的数据包

在每次测试期间,应用程序的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版本错过了一些。这不是网络问题,因为即使我们将代码切换到计算机上的其他版本,也是如此。

+0

对不起,我错过了包括清除当我复制粘贴代码时,我有代码中的buffer.clear(),我编辑了张贴以反映它 – user2677485

回答

2

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

DatagramPacket大小写应在每次接收前重置数据包长度。

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

+0

我没有在Non NIO中每次使用新的DatagramPacket因为事件大小范围从200字节到64K(最大),平均值<2K,我不想在每个事件的队列中插入带有64K数据的数据包。然而,由于队列大小几乎不会超过几千(除非有更大的问题),我认为你每次使用新的DatagramPacket的建议性能会更好(支持更高的事件速率)。 – user2677485

+0

对于NIO的情况,当我发布问题时,我的clear(),复制粘贴失败,我将用DirectByteBuffer进行测试。 (我认为你的最后一句话是指非NIO案件而不是NIO) – user2677485

+0

它涉及两种情况。 – EJP

1

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

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