实现高性能稳定的socket tcp通讯经验分享
其实在.net socket编写高性能稳定方面的资料真的比较少,一个实质性的测试数据结果对比就更少了.我们可以从http://msdn.microsoft.com/zh-cn/magazine/cc163356.aspx看到MS说net 2.0 sp1后的socket通讯能力非常强劲,可以同时挂起6W个IO(可以简单地认为可以在一秒内send+receive可以达到6W或更高),但要找这个数据的测试似乎很难.但在一篇MS关于WCF的性能测试中基于tcp部署的性能在一台4路服务器中可以达到这个量(http://msdn.microsoft.com/en-us/library/bb310550.aspx).那WCF可以达到那基于c# socket实现达到这样的IO处理能力应该完成没问题.经常一段时间的努力即使在一台core e4300 5年前的PC上完全胜任每秒6W IO的处理能力,还包括数据接收,协议分析对像序列化写入.
以下是一些经验总结:
Buffer Pool
这个相信不用说,MSDN上也介绍这种用法.但如何分配这个buffer大小呢?我们一般会一条消息用一个buffer,这个时候我们就很难分配了,不得不按最大消息长度来定义buffer长度,这样做从内存分配上来说是极其不合理的.不过现在内存多不用计较(也许).但还存在一种问题就是send的时候一般针对buffer的,当消息小的时候就不得不按消息的数量来操作IO,如果buffer的灵活性更好如小的消息可以多个写入一个buffer,大的消息可以写入几个buffer.这样即能达到内存使用合理.也能控制最少的IO处理最多的信息达到更好的性能.
SocketAsyncEventArgs池需要吗?
这个MSDN上也有介绍这种用法,但就存在一个如何分配的问题,连接产生的时候分配,连接断开的时候回收?如果是这个SAEA怎样分配buffer大小(别轻易地使用SetBuffer(byte[],int,int)方法);不得不面对以上Buffer说的问题.所以SEAE和Buffer Pool一样大小来分配池结合使用,用的时候拿用完回收到池. 其实SEAE最好是和BUFFER整合在一起,这样做的好处就是在高并发的时候可以节省大量的byte[] Copy.
队列的使用
队列的使用就是更好控制线程处理数据,既然一个线程就能更好地完成工作,可必要用更多的线程去做呢.记住用最少的线程完成最多的事情.
在运行期内你能做到最少化内存分配吗?
你能让一些getByes方法不产生new byte[]吗?好好看下MSDN相关对像方法看有什么途径来达到这种效果.
不于过于自信多用些分析工具
也许你有着很多年的经验,也用一些计时器来衡量过代码的执行行性能.但有时候你真的很难发现原来性能并不存在于你测的地方.不要只关注于代码的CPU执行时间,别忘了.NET下还有一个巨头GC.用性能分析工具分析代码的执行时间同时,不要忘了分析一下代码在某些情况下的内存分配情况.VS2010就提供了这些方便的分析工具.
以下是这段时间优化的测试情况
测试结果一:
1K连接分别获取一个对像和一个列表对像
单一对像信息
class GetResponse : IMessage { public User User; public void Save(BufferWriter writer) } class User : IMessage { public string Name; public string EMail; public string City; public string Counrty; public void Save(BufferWriter writer) { writer.Write(Name); writer.Write(EMail); writer.Write(City); writer.Write(Counrty); } public void Load(BufferReader reader) { Name = reader.ReadString(); EMail = reader.ReadString(); City = reader.ReadString(); Counrty = reader.ReadString(); } }
列表对像信息(5条)
class Response : IMessage { private IList<Order> mOrders = new List<Order>(); public IList<Order> Orders { get { return mOrders; } } } class Order : IMessage { public int OrderID; public string CustomerID; public int EmployeeID; public long OrderDate; public long RequiredDate; public string ShipName; public string ShipAddress; public string ShipCity; public string ShipRegion; public string ShipPostalCode; public string ShipCountry; public void Save(BufferWriter writer) { writer.Write(OrderID); writer.Write(CustomerID); writer.Write(EmployeeID); writer.Write(OrderDate); writer.Write(RequiredDate); writer.Write(ShipName); writer.Write(ShipAddress); writer.Write(ShipCity); writer.Write(ShipRegion); writer.Write(ShipPostalCode); writer.Write(ShipCountry); } public void Load(BufferReader reader) { OrderID = reader.ReadInt32(); CustomerID = reader.ReadString(); EmployeeID = reader.ReadInt32(); OrderDate = reader.ReadInt64(); RequiredDate = reader.ReadInt64(); ShipName = reader.ReadString(); ShipAddress = reader.ReadString(); ShipCity = reader.ReadString(); ShipRegion = reader.ReadString(); ShipPostalCode = reader.ReadString(); ShipCountry = reader.ReadString(); } }
测试结果
测试结果2:
由于局域网带宽限制,所以只能测试2K和3K连接下的单一对象获取
除了以上测试结果外,还进行了同场景500物体状态变更广播,在core e4300也完全能胜任,每秒转发50W的消息量.每个client的消息延时在100ms以内.
同场景物体广播测试程序:http://www.henryfan.net/file.axd?file=2012%2f3%2fBroadcastTest.rar (想测试效果请分开电脑运行,如果你的网络是100mb的话最大只能运行5个client,如果服务器的cpu低于core e4300也有可能支持不了500同屏)