6

下面的文字是对这个问题的扩展和添加颜色的努力:

如何防止行为不端的客户取消整个服务?

我基本上有这种情况:WCF 服务启动并运行,客户端回调具有直接、简单的单向通信,与这个没有太大不同:

public interface IMyClientContract
{
  [OperationContract(IsOneWay = true)]
  void SomethingChanged(simpleObject myObj);
}

我从服务到最终将有大约 50 个并发连接的客户端每秒可能数千次调用此方法,并具有尽可能低的延迟(<15 ms 会很好)。这工作正常,直到我在连接到服务器的一个客户端应用程序上设置一个断点,然后在服务挂起大约 2-5 秒后一切都挂起,并且在服务挂起之前大约 30 秒左右没有其他客户端接收任何数据注册连接故障事件并断开有问题的客户端。在此之后,所有其他客户端继续以愉快的方式接收消息。

我已经对 serviceThrottling、并发调整、设置线程池最小线程、WCF 秘诀和整个 9 码进行了研究,但归根结底,这篇文章MSDN - WCF essentials, One-Way Calls, Callbacks and Events 准确地描述了我在没有真正提出建议的情况下遇到的问题。

允许服务安全回调客户端的第三种解决方案是将回调合约操作配置为单向操作。这样做可以使服务即使在并发设置为单线程时也可以回调,因为不会有任何回复消息来争夺锁。

但在本文前面,它仅从客户的角度描述了我所看到的问题

当单向调用到达服务时,它们可能不会一次全部调度,可能会在服务端排队等待一次调度,这一切都取决于服务配置的并发模式行为和会话模式。服务愿意排队多少条消息(无论是单向还是请求-回复)是配置的通道和可靠性模式的产物。如果排队的消息数量超过了队列的容量,那么客户端将阻塞,即使发出单向调用也是如此

我只能假设反之亦然,发送到客户端的排队消息数量已超过队列容量,并且线程池现在充满了试图调用该客户端的线程,这些线程现在都被阻塞了。

处理这个问题的正确方法是什么?我是否应该研究一种方法来检查每个客户端在服务通信层排队的消息数量,并在达到一定限制后中止它们的连接?

几乎看起来,如果 WCF 服务本身在队列填满时阻塞,那么每当一个客户端的队列满时,我可以在服务中实现的所有异步/单向/即发即弃策略仍然会被阻塞。

4

2 回答 2

1

不太了解客户端回调,但听起来类似于通用的 wcf 代码阻塞问题。我经常通过生成 BackgroundWorker 并在线程中执行客户端调用来解决这些问题。在此期间,主线程计算子线程花费的时间。如果子线程在几毫秒内没有完成,主线程就会继续前进并放弃线程(它最终会自行死亡,因此不会发生内存泄漏)。这基本上就是 Mr.Graves 用“即发即弃”一词所暗示的。

于 2011-07-14T15:49:23.603 回答
1

更新:

我实现了一个即发即弃的设置来调用客户端的回调通道,一旦缓冲区填充到客户端,服务器就不再阻塞

MyEvent 是一个具有与 WCF 客户端合同中定义的方法之一匹配的委托的事件,当它们连接时,我实际上是在将回调添加到事件

MyEvent += OperationContext.Current.GetCallbackChannel<IFancyClientContract>().SomethingChanged

等等...然后将此数据发送给所有客户端,我正在执行以下操作

//serialize using protobuff
using (var ms = new MemoryStream())
{
    ProtoBuf.Serializer.Serialize(ms, new SpecialDataTransferObject(inputData));
    byte[] data = ms.GetBuffer();
    Parallel.ForEach(MyEvent.GetInvocationList(), p => ThreadUtil.FireAndForget(p, data));
}

在 ThreadUtil 类中,我基本上对文章中定义的代码进行了以下更改

static void InvokeWrappedDelegate(Delegate d, object[] args)
{
    try
    {
        d.DynamicInvoke(args);
    }
    catch (Exception ex)
    {
        //THIS will eventually throw once the client's WCF callback channel has filled up and timed out, and it will throw once for every single time you ever tried sending them a payload, so do some smarter logging here!!
        Console.WriteLine("Error calling client, attempting to disconnect.");
        try
        {
            MyService.SingletonServiceController.TerminateClientChannelByHashcode(d.Target.GetHashCode());//this is an IContextChannel object, kept in a dictionary of active connections, cross referenced by hashcode just for this exact occasion
        }
        catch (Exception ex2)
        {
            Console.WriteLine("Attempt to disconnect client failed: " + ex2.ToString());
        }
    }
}

我没有任何好主意如何去杀死服务器仍在等待的所有未决数据包,以查看它们是否会被传递。一旦我得到第一个异常,理论上我应该能够去并终止某个队列中的所有其他请求,但是这个设置是有效的并且符合目标。

于 2011-07-14T16:17:24.437 回答