C# 在.NET/Mono中减少UDP消息丢失
我们目前正在为一个开源学术项目执行一些基准测试。它基本上通过UDP(RFC 5426)和TLS(RFC 5425)实现Syslog协议。 我们知道TLS的优点是可靠性(即我们不会丢失消息),但也有性能方面的缺点 我们有一个基准测试客户端,还有一个特殊的伪造Apache安装,可以高速发送消息。 我们的目标是将UDP数据包的丢失降至最低。Apache 1.3.41已被检测,以便通过UDP发送特殊日志消息(不是Syslog格式,而是我们在服务器端解析的特殊简短语法),这种检测使它在httpd启动时发送2000多条消息,我们希望它发生。:) 此外,我可以告诉您,在Apache的提升阶段,少量消息(与我们提交给日志服务器的其他工作负载相比)以极高的速率发送,可能会淹没UDP 现在,日志服务器与HTTP服务器位于不同的机器上,并且两者都几乎没有像样的硬件(甚至没有双核CPU,而是带有HyperRead的奔腾4)。日志服务器代码是C#。以下方法由4个线程以高于正常优先级运行C# 在.NET/Mono中减少UDP消息丢失,c#,mono,udp,C#,Mono,Udp,我们目前正在为一个开源学术项目执行一些基准测试。它基本上通过UDP(RFC 5426)和TLS(RFC 5425)实现Syslog协议。 我们知道TLS的优点是可靠性(即我们不会丢失消息),但也有性能方面的缺点 我们有一个基准测试客户端,还有一个特殊的伪造Apache安装,可以高速发送消息。 我们的目标是将UDP数据包的丢失降至最低。Apache 1.3.41已被检测,以便通过UDP发送特殊日志消息(不是Syslog格式,而是我们在服务器端解析的特殊简短语法),这种检测使它在httpd启动时发送
UdpClient _client;
IQueue<T>[] _byteQueues; //not really IQueue, but a special FIFO queue class that reduces overhead to the minimum
private void ListenerLoop()
{
IPEndPoint remoteEndpoint = new IPEndPoint(IPAddress.Any, 0);
while (_listen)
{
try
{
byte[] payload = _client.Receive(ref remoteEndpoint);
_byteQueues[
(((Interlocked.Increment(ref _currentQueue))%WORKER_THREADS) + WORKER_THREADS)%WORKER_THREADS].
Enqueue(payload);
}
catch (SocketException)
{
}
catch (Exception)
{
} //Really do nothing? Shouldn't we stop the service?
}
}
ReceiveBufferSize是在运行时配置的
Apache发送的每个日志消息都非常短,我认为不超过50字节。我们在实验室里运行千兆以太网
在上一次使用这种配置的实验中,日志服务器只接收到生成的2900多个日志中的700多个。Wireshark报告了UDP套接字上超过2900条消息,但Logbus的日志跟踪(将所有接收到的消息存储到一个文件中)只报告了这700/800条消息。执行cat/proc/net/udp
并使用lsof
进行欺骗,以找到正确的行报告大量丢弃的数据包。日志肯定是以非常高的速率发送的。如果我们在每次日志调用后将apachecore修改为睡眠一段短时间(略小于一毫秒),我们会将损失减少到零,但性能也会降低到几乎零。我们将进行此类测试,但我们必须证明Logbus ng在现实场景中的有效性:(
我直截了当的问题是
提前感谢您的帮助。我希望已经说清楚了。如果有帮助,您可以在上找到UDP接收器的源代码。ReceiveBufferSize肯定会影响UDP套接字(即UdpClient),如果数据包丢失是由于缓冲区溢出,那么增加ReceiveBufferSize将有帮助 请记住,如果数据速率太高,以至于无法从缓冲区快速读取足够长的时间,那么即使是最大的缓冲区,也不可避免地会溢出 我已经在Ubuntu上运行的Mono 2.6.7上有效地使用了UdpClient.Client.ReceiveBufferSize,所以我相信Mono的实现很好,当然我还没有在Mono 2.8上使用它 根据我的经验,以极高的速率向本地主机发送UDP数据包时,可能会出现一些数据包丢失,尽管我在现实世界的应用程序中从未遇到过这种数据包丢失的情况。因此,使用这种方法可能会取得一些成功 您还需要查看是否发生了数据包丢失,数据包丢失可能是由于网络基础设施、数据包冲突、交换机可能由于交换机上的某些限制而丢弃数据包造成的
简单地说,在使用UDP时,您需要准备好处理和预期数据包丢失。ReceiveBufferSize会影响UDP套接字(即UdpClient),如果数据包丢失是由于缓冲区溢出造成的,则增加ReceiveBufferSize将有所帮助 请记住,如果数据速率太高,以至于无法从缓冲区快速读取足够长的时间,那么即使是最大的缓冲区,也不可避免地会溢出 我已经在Ubuntu上运行的Mono 2.6.7上有效地使用了UdpClient.Client.ReceiveBufferSize,所以我相信Mono的实现很好,当然我还没有在Mono 2.8上使用它 根据我的经验,以极高的速率向本地主机发送UDP数据包时,可能会出现一些数据包丢失,尽管我在现实世界的应用程序中从未遇到过这种数据包丢失的情况。因此,使用这种方法可能会取得一些成功 您还需要查看是否发生了数据包丢失,数据包丢失可能是由于网络基础设施、数据包冲突、交换机可能由于交换机上的某些限制而丢弃数据包造成的
简单地说,在使用UDP时,您需要准备好处理和预期数据包丢失。如果您使用UDP,您必须预期数据包丢失。数据包丢失的原因有很多。对您来说,最有可能是因为通道上任何位置的数据包溢出。可能是您的交换机,也可能是您的接收器。溢出的原因是因为UDP没有任何类型的拥塞控制。Tcp有拥塞控制(慢启动),这就是为什么对于Tcp,它永远不会(理论上在完美的环境下)溢出 手动采用tcp慢启动拥塞控制策略是防止udp传输溢出的有效方法
try
{
Socket clientSock = new Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp)
{
#if !MONO
//Related to Mono bug 643475
ExclusiveAddressUse = true,
#endif
};
if (ReceiveBufferSize >= 0) clientSock.ReceiveBufferSize = ReceiveBufferSize;
clientSock.Bind(localEp);
_client = new UdpClient {Client = clientSock};
}
catch (SocketException ex)
{
throw new LogbusException("Cannot start UDP listener", ex);
}