WCF 心跳包导致server端性能差的问题
各位大神,我最近在设计一个wcf的程序,但是遇到了点问题。小弟在设计一个基于观察者模式的程序,需要涉及到多个客户端和一个server端相连,所有客户端每隔5秒发个心跳包给server。在server端我用System.Collections.Concurrent.ConcurrentDictionary容器来保存对客户端连接的引用和客户端对应的那个心跳包。由于每个客户端会不停的发数据包,然后就会不停的去往这个容器里读写东西,我很担心会引起性能问题,有大神能给我解释下ConcurrentDictionary的锁的机制是什么样的么?
PS:或者,我是不是可以抛弃wcf,通过SOCKET来实现比较好一点。
不知道我描述清楚了么有,在此先谢谢各位大神了。
server端代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Threading;
using Server;
using System.Collections.Concurrent;
using System.ServiceModel.Channels;
namespace WCFLib
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class Publisher : IPublisher
{
private static readonly ConcurrentDictionary<iCallBack, Package> Clients = new ConcurrentDictionary<iCallBack, Package>();
public void Register()
{
var client = OperationContext.Current.GetCallbackChannel<iCallBack>();
if (!Clients.ContainsKey(client))
{
Clients.TryAdd(client, new Package(DateTime.Now));
}
}
public void SendHeartbeatPackage()
{
var client = OperationContext.Current.GetCallbackChannel<iCallBack>();
if (Clients.ContainsKey(client))
{
Clients[client] = new Package(DateTime.Now);
}
Console.WriteLine("Begin to update heartbeat package.");
Console.WriteLine("Updated time is {0}", (Clients[client] as Package).Time);
}
}
}
client端代码如下:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Threading;
using System.Net;
namespace ConsoleClient2
{
class Program
{
static void Main(string[] args)
{
InstanceContext callback = new InstanceContext(new Callback());
ServerProxy.PublisherClient proxy = new ServerProxy.PublisherClient(callback);
proxy.Register();
new Thread(() =>
{
while (true)
{
Thread.Sleep(5000);
proxy.SendHeartbeatPackage();
}
}).Start();
Console.Read();
}
}
}
--------------------编程问答-------------------- 如果你的客户数不过百,5秒发个心跳包,
那你想的太多了,把电脑的速度想的太差了 --------------------编程问答-------------------- rtdb,谢谢你的答复,我的客户端应该在10个左右,我很担心在往concurrentdictionary里面会写数据的时候会不停的有加锁解锁的过程,这样不会造成性能问题么? --------------------编程问答-------------------- 我说的数据还是考虑到了网络及系统整体响应速度。
若只是concurrentdictionary的加锁解锁,
呵呵你太小看现在的CPU速度了,
你可以自己测试一下,我相信每秒十万上下只是起步。 --------------------编程问答-------------------- RTDB,那我这样设计没问题吧,或者你还有什么更好的建议么?我自己纠结了这个问题好久,一直觉得性能没达到最佳。 --------------------编程问答-------------------- 不精通SOCKET的话,还是用WCF,小规模应用(10客户端)根本不用考虑太多,但回发需要注意,不能使用单线程(可用线程池或多线程),否则中途某个客户端的网络故障会导致后面其他客户端晋重延迟。 --------------------编程问答--------------------
YCG_893,谢谢,我明白了,我不用担心性能问题了。还有个附加问题,你说的回发时使用多线程是不是指在server端当我callback每个客户端时都使用独立的线程来完成针对每个客户端的调用? --------------------编程问答-------------------- Hi,ycg_893
我在server端有个方法实现的就是遍历所有客户端,然后给所有客户端发命令,代码如下
public void KickoffTest()
{
foreach (var item in Clients)
{
//我该在这里用多线程来实现么?
item.Key.SendRequestToClient("Begin Testing");
}
}
不是在这里发命令时候要用多线程去实现? --------------------编程问答-------------------- 人都不在了么?大神们再给我解释下这个小问题,我就结贴给分了。 --------------------编程问答-------------------- 如果你的客户端都只向服务器获取数据,这个就不存在回发问题,因为是单向的,若需要服务器也向客户端发送数据则是双向的(WCF双工通信),服务器主动发给客户端就需要注意,假设100个客户端,发到50个的时候阻塞了或者需要待到超时异常(如果不处理异常,那每51个以后都收不到)后第51个才开始发送,阻塞时间根据网络情况和超时时间确定,即使没有发生这种情况,若每一个客户端需要2秒,是不是第100个客户端需要几分钟后才接到;
使用多线程或线程池也取决于你的客户端规模,若有几千个客户端,每次每个需要一个线程的话,估计你的服务器无法承受,会因线程耗尽而崩溃。
关于线程问题不是几句话就完全说明清楚,这跟规模、设计、需求有关;
至于配置WCF服务器也有关,如单实例、一个会话一个实例、还是每次都是一个实例、要不要同步上下文等;详看WCF配置
你上面的代码可以简单地在循环内加线程,但这种仅作为测试还可以,实际部署应用你可以多尝试各种故障测试就知道不可行。 --------------------编程问答-------------------- 还需要做线程安全方面,如发送时有100个,但正在发送的,有注销退出的。按你的代码就会产生“枚举的集合已变动的异常”。也不应该说等我发完了你再退出,这当然不合理,可以简单地解决就是复制一个数组出来,实际上复制数据时就需要线程安全。如下简单示例:
iCallBack[] ClientKeys;
lock(lockObject)
{
ClientKeys = Clients.Keys.ToArray();
}
foreach (var item in ClientKeys)
{
//新线程或加入线程池
item.SendRequestToClient("Begin Testing");
}
--------------------编程问答-------------------- ycg_893 , 谢谢你不厌其烦的指点,我大概知道怎么弄了,能不能等我实现了再给我看下?我能晚点结分给你行么? --------------------编程问答--------------------
namespace WCFLib
{
// NOTE: You can use the "Rename" command on the "Refactor" menu to change the class name "Service1" in both code and config file together.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Reentrant)]
public class Publisher : IPublisher
{
private static readonly ConcurrentDictionary<iCallBack, Package> Clients = new ConcurrentDictionary<iCallBack, Package>();
public void Register()
{
var client = OperationContext.Current.GetCallbackChannel<iCallBack>();
if (!Clients.ContainsKey(client))
{
Clients.TryAdd(client, new Package(DateTime.Now));
}
}
public void SendHeartbeatPackage()
{
var client = OperationContext.Current.GetCallbackChannel<iCallBack>();
if (Clients.ContainsKey(client))
{
Clients[client] = new Package(DateTime.Now);
}
Console.WriteLine("Begin to update heartbeat package.");
Console.WriteLine("Updated time is {0}", (Clients[client] as Package).Time);
}
}
}
Reentrant 模式下 并不是真正的并发吧? 改成multi的 你的Register 方法就不对了 --------------------编程问答--------------------
能说的详细点么?不对在哪里,大哥 --------------------编程问答-------------------- 这。。。。。。。
不会的 只要不是
while(true){
//心跳包发送
}
就不会出问题。。。。。。
另外一分钟发一个也没有问题
补充:.NET技术 , C#