【问题标题】:WCF - Client callback vs. polling for "keep list of subscribers"WCF - 客户端回调与“保留订阅者列表”的轮询
【发布时间】:2009-12-07 22:18:33
【问题描述】:

我想在 WCF 中创建一个简单的客户端-服务器示例。我对回调进行了一些测试,到目前为止效果很好。我用以下界面玩了一下:

[ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IStringCallback))]
public interface ISubscribeableService
{
    [OperationContract]
    void ExecuteStringCallBack(string value);

    [OperationContract]
    ServerInformation Subscribe(ClientInformation c);

    [OperationContract]
    ServerInformation Unsubscribe(ClientInformation c);
}

这是一个简单的例子。稍微调整了一下。您可以要求服务器“执行字符串回调”,在这种情况下,服务器会反转字符串并调用所有订阅的客户端回调。

现在,问题来了:如果我想实现一个所有客户端都向服务器“注册”的系统,并且服务器可以“询问”客户端是否还活着,你会用回调来实现吗(所以而不是这种“stringcallback”,一种 TellTheClientThatIAmSti​​llHereCallback)。通过检查回调上的通信状态,我还可以“知道”客户端是否已死。类似的东西:

Subscribers.ForEach(delegate(IStringCallback callback)
                    {
                        if (((ICommunicationObject)callback).State == CommunicationState.Opened)
                        {
                            callback.StringCallbackFunction(new string(retVal));
                        }
                        else
                        {
                            Subscribers.Remove(callback);
                        }
                    });

我的问题,换一种说法:

  • 服务器可能有 3 个客户端
  • 客户端 A 死机(我拔掉了笔记本电脑的插头)
  • 服务器死机并重新上线
  • 新客户出现

所以基本上,您会使用回调来验证客户端的“仍然存在的状态”,还是使用轮询并跟踪“我有多久没有听说过客户端”...

【问题讨论】:

  • 您如何连接到 WCF 服务器?你在使用 TCP 吗?这将改变实现池的方式。

标签: c# .net wcf


【解决方案1】:

您可以通过ICommunicationObjectClosedClosingFaulted 事件检测到连接状态的大多数更改。您可以在设置回调的同时挂钩它们。这绝对比轮询好。

IIRC,Faulted 事件只会在您实际尝试使用回调(未成功)之后触发。因此,如果客户端只是消失了——例如,硬重启或关机——那么您将不会立即收到通知。但是你需要吗?如果是这样,为什么?

WCF 回调可能随时失败,您始终需要牢记这一点。即使客户端和服务器都很好,您仍然可能由于异常或网络中断而导致通道出现故障。或者,客户可能在您上次民意调查和您当前操作之间的某个时间下线。关键是,只要您对回调操作进行防御性编码(无论如何这都是很好的做法),那么挂钩上述事件对于大多数设计通常就足够了。如果由于任何原因发生错误(包括客户端未能响应),Faulted 事件将启动并运行您的清理代码。

这就是我所说的被动/惰性方法,与轮询或保持活动方法相比,它需要更少的编码和网络聊天。

【讨论】:

  • 每当我需要类似的功能(这有点频繁)时,我都会这样做。
【解决方案2】:

如果启用可靠会话,WCF 会在内部维护一个保持活动控制机制。它通过隐藏的基础设施测试消息定期检查另一端是否仍然存在。这些检查的时间间隔可以通过 ReliableSession.InactivityTimeout 属性来影响。如果您将该属性设置为 20 秒,则 ICommunicationObject.Faulted 事件将在另一端发生服务故障后大约 20 到 30(最大)秒引发。

如果您想确保客户端应用程序始终保持“自动连接”,即使在临时服务中断之后,您可能希望使用工作线程(来自线程池)反复尝试在客户端,并在此处引发故障事件后调用会话启动操作。

作为第二种方法,由于无论如何您都在实现工作线程机制,因此您也可能会忽略故障事件并让工作线程在客户端应用程序的整个生命周期中循环。您让线程反复检查代理状态,并在状态出现故障时尝试进行修复工作。

使用第一种或第二种方法,您可以实现服务总线架构(中介模式),保证所有客户端应用程序实例在服务运行时随时准备好接收“自发”服务消息。

当然,这仅在可靠会话“本身”配置正确的情况下才有效(使用支持会话的绑定,并应用 ServiceContractAttribute.SessionMode、ServiceBehaviorAttribute.InstanceContextMode、OperationContractAttribute.IsInitiating 和 OperationContractAttribute.IsTerminating以有意义的方式属性)。

【讨论】:

    【解决方案3】:

    我在使用 WCF 和回调时遇到了类似的情况。我不想使用轮询,但我使用的是“reilable”协议,所以如果客户端死了,它会挂起服务器,直到它超时并崩溃。

    我不知道这是否是最正确或最优雅的解决方案,但我所做的是在服务中创建一个类来表示客户端代理。此类的每个实例都包含对客户端代理的引用,并且只要服务器设置该类的“消息”属性,就会执行回调函数。通过这样做,当客户端断开连接时,单个包装类将获得超时异常,并将自己从服务器的侦听器列表中删除,但服务不必等待它。这实际上并不能回答您关于确定客户端是否还活着的问题,但它是构建服务以解决问题的另一种方式。如果您需要知道客户端何时死亡,您可以在客户端包装器从侦听器列表中删除自身时获取。

    【讨论】:

      【解决方案4】:

      我没有尝试通过网络使用 WCF 回调,但我已将它们用于进程间通信。我遇到了一个问题,即正在发送的调用最终在同一个线程上结束,并且当有依赖于同一个线程的调用时使服务死锁。

      这可能适用于您当前遇到的问题,所以这是我必须采取的措施来解决问题。

      把这个属性放到WCF服务端实现类的服务端和客户端上

      [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple)]
      public class WCFServerClass
      

      ConcurrencyMode.Multiple 使每个调用进程都在自己的线程上进行,这应该有助于您在客户端死亡时锁定服务器直到超时。

      我还确保在客户端使用线程池以确保客户端没有线程问题

      【讨论】:

        猜你喜欢
        • 2018-07-08
        • 1970-01-01
        • 1970-01-01
        • 2015-11-11
        • 1970-01-01
        • 1970-01-01
        • 2012-02-04
        • 1970-01-01
        • 2011-04-16
        相关资源
        最近更新 更多