我们目前正在为开源学术项目Logbus-ng执行一些基准测试。它基本上通过UDP(RFC 5426)和TLS(RFC 5425)实现Syslog协议。 我们知道TLS的优势在于可靠性(即我们不会丢失消息)但具有性能的缺点。
我们有一个基准测试客户端,还有一个特殊的伪造Apache安装,可以高速发送消息。 我们的目标是将UDP数据包的丢失降至最低。 Apache 1.3.41已经过检测,以便通过UDP发送特殊的日志消息(不是Syslog格式,而是我们在服务器端解析的特殊短语法),这样的检测使它在httpd启动时发送超过2000条消息,并且我们希望它发生:)
更多,我可以告诉你,在Apache的加速阶段,这个小数量的消息(与我们提交给日志服务器的其他工作负载相比)是在发送的。高速率,可能泛洪UDP。
现在,日志服务器位于与HTTP服务器不同的机器上,并且两者都具有相当不错的硬件(甚至不是双核CPU,而是具有HyperThread的Pentium 4)。日志服务器代码在C#中。以下方法由AboveNormal priority
中的4个线程运行 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?
}
}
为了减少线程在Receive方法之外花费的时间,我们不会在收到消息后解析消息,而是将其存储在其他工作线程将读取的4个特殊队列之一中。据我所知,.NET调度程序是贪心的,所以无论线程等待多长时间,优先级较高的线程都会提前调度并可能导致饥饿,所以这就是为什么我们现在不关心线程数量的增加应用程序(全球约20个)。
我们不仅增加了线程优先级,还尝试将UDP缓冲区大小增加到1MB。这是初始化代码的片段
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);
}
在运行时配置ReceiveBufferSize ...
Apache发送的每条日志消息都很短,我认为不超过50个字节。我们在实验室中运行千兆以太网。
在上一次使用此类配置的实验中,日志服务器仅收到超过2900个生成的700多个。 Wireshark在UDP套接字上报告了超过2900条消息,但Logbus的日志跟踪(将所有收到的消息存储到文件中)仅报告这些700/800。执行cat /proc/net/udp
并使用lsof
进行操作以查找正确的行报告大量丢弃的数据包。原木肯定以非常高的速度发送。如果我们在每次调用日志后修改Apache内核以暂时休眠(少于一毫秒),我们会将损耗降低到零,但性能也会降低到几乎为零。我们将进行这样的测试,但我们必须证明Logbus-ng在现实场景中的有效性:(
我们当前必须使用Apache执行特殊测试,我们仅使用UDP来传递消息。我们不能选择TLS,因为我们只有C#API。
提前感谢您的帮助。我希望已经清楚了。如果有帮助,你可以在SVN上找到UDP接收器的源代码
答案 0 :(得分:1)
ReceiveBufferSize肯定会影响UDP套接字(即UdpClient),如果数据包丢失是由于缓冲区溢出造成的,那么增加ReceiveBufferSize会有所帮助。
请记住,如果数据速率如此之高以至于您无法快速读取缓冲区足够长的时间,那么即使是最大的缓冲区也不可避免地会溢出。
我在Ubuntu上运行的Mono 2.6.7上有效地使用了UdpClient.Client.ReceiveBufferSize,所以我相信Mono的实现很好,当然我还没有将它用于Mono 2.8。
根据我的经验,以极高的速率向本地主机发送UDP数据包,可能会丢失一些数据包,但我从未在真实世界的应用程序中遇到过这种丢包现象。所以你可以用这种方法取得一些成功。
您还需要查看数据包丢失是否正在发生,可能是数据包丢失是由于网络基础设施,数据包冲突,交换机可能因为交换机的某些限制而丢弃数据包。
简而言之,您需要准备好处理并期望在使用UDP时丢包。
答案 1 :(得分:0)
如果您使用的是udp,则必定会丢失数据包。丢包有很多原因。对你来说,很可能是因为频道上任何地方的数据包溢流。可能是你的开关或可能是你的接收器。过度泛滥的原因是因为UDP没有任何类型的拥塞控制。 Tcp确实具有拥塞控制(慢启动),这就是为什么使用tcp,它从不(理论上在完美的环境下)过度驱动。
防止udp传输过度溢出的有效方法是手动采用tcp慢启动拥塞控制策略。
回答你的直截了当的问题 1.否。请参阅tcp慢启动算法。 2.最有可能的不是虫子。 Udp就是这样设计的。这是一个原因,并且需要这样做。 3.号码代理不起作用。 4.最简单的实现,以防止由于过度编码导致的数据包丢失,是在发送更多数据包之前等待接收方的确认(注意接收方已成功接收到数据包)。当然,由于其他原因,这对于防止数据包丢失没有帮助。
答案 2 :(得分:0)
显然也应该在Mono中工作,但你知道该属性可能存在的错误吗?我的意思是,有没有人报告过一个错误? (单声道2.8)
显然,设置套接字接收缓冲区大小在单声道版本3.2.7中严重受损:而不是指定的大小,它将被设置为随机值,因此尝试增加缓冲区大小实际上可能会使性能变差: - (
https://github.com/mono/mono/commit/bbd4ee4181787189fbb1f8ba6364afdd982ae706