我有一个托管在Windows服务中的WCF服务。它使用Publish Subscribe模式向多个订阅者发布事件。然而,我发现,经过一段时间的不活动,连接失败,我得到可靠的会话错误。我试图通过更改非活动超时和接收超时设置来解决这个问题,但这没有什么区别。老实说,这快把我逼疯了。
我看到的一个解决方案是每隔一段时间"ping"所有订阅者。(例如超时时间的一半)。
像这样设置一个"ping"的最好方法是什么?也就是说,我如何添加定时器到服务,如何让定时器调用ping函数?
编辑:我对ping解决方案不是很满意,想进一步调查为什么我的可靠会话总是超时。下面是该服务的绑定配置
服务器app.config<binding name="WSDualHttpBinding_IError" receiveTimeout="24.20:31:23.6470000">
<reliableSession inactivityTimeout="24.20:31:23.6470000" />
</binding>
客户机app.config <binding name="WSDualHttpBinding_IError" closeTimeout="00:01:00"
openTimeout="00:01:00" receiveTimeout="24.20:31:23.6470000"
sendTimeout="00:01:00" bypassProxyOnLocal="false" transactionFlow="false"
hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288"
maxReceivedMessageSize="65536" messageEncoding="Text" textEncoding="utf-8"
useDefaultWebProxy="true">
<readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
maxBytesPerRead="4096" maxNameTableCharCount="16384" />
<reliableSession ordered="true" inactivityTimeout="24.20:31:23.6470000" />
<security mode="Message">
<message clientCredentialType="Windows" negotiateServiceCredential="true"
algorithmSuite="Default" />
</security>
</binding>
我尝试了"infinite"而不是这个值,但它不喜欢它。表示该值无效,尽管谷歌搜索的几个页面建议这样做。我只能假设这在早期版本的WCF/中是有效的。净
EDIT2:
这里是异常日志。这绝对是一段随机的时间。
************** Exception Text **************
System.TimeoutException: The operation did not complete within the allotted timeout of 00:00:59.4979498. The time allotted to this operation may have been a portion of a longer timeout.
Server stack trace:
at System.ServiceModel.Channels.InterruptibleWaitObject.Wait(TimeSpan timeout, Boolean throwTimeoutException)
at System.ServiceModel.Channels.ReliableInputConnection.Close(TimeSpan timeout)
at System.ServiceModel.Channels.ReliableDuplexSessionChannel.OnClose(TimeSpan timeout)
at System.ServiceModel.Channels.ClientReliableDuplexSessionChannel.OnClose(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout)
at System.ServiceModel.Channels.ServiceChannel.OnClose(TimeSpan timeout)
at System.ServiceModel.Channels.CommunicationObject.Close(TimeSpan timeout)
Exception rethrown at [0]:
at System.Runtime.Remoting.Proxies.RealProxy.HandleReturnMessage(IMessage reqMsg, IMessage retMsg)
at System.Runtime.Remoting.Proxies.RealProxy.PrivateInvoke(MessageData& msgData, Int32 type)
at System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout)
at System.ServiceModel.ClientBase`1.System.ServiceModel.ICommunicationObject.Close(TimeSpan timeout)
at System.ServiceModel.ClientBase`1.Close()
at MyApp.Form1.buttonError_Click(Object sender, EventArgs e)
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message& m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message& m)
at System.Windows.Forms.ButtonBase.WndProc(Message& m)
at System.Windows.Forms.Button.WndProc(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message& m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message& m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)
要实现客户端的ping,您可能需要遵循以下步骤列表:
-
用一个Ping方法扩展你的回调契约(你应该已经有一个回调契约,因为你写你使用的是发布订阅模式,不是吗?):
[OperationContract] void Ping();
-
在客户端连接后首先调用的操作中,将对客户端操作上下文的引用存储在集合中:
List<OperationContext> _clientCtxList = new List<OperationContext>(); void IMyService.InitSession() { _clientCtxList.Add(OperationContext.Current); }
-
定义一个函数,由定时器调用来ping所有客户端:
void tmrPing (object state) { foreach (var ctx in _clientCtxList) { // todo: catch exceptions and remove client context // from list in case of failure ctx.GetCallbackChannel<IMyCallbackContract>().Ping(); } // restart timer _timer.Change(10000, Timeout.Infinite); }
-
当你的服务启动时定义并启动一个计时器(我不知道IIS托管,所以你必须自己找到正确的位置):
System.Threading.Timer _timer; void Startup() { // call my function in 10 seconds _timer = new System.Threading.Timer( tmrPing, null, 10000, Timeout.Infinte); }
备注:
- 这是记事本代码-它可能包含语法错误,可能无法编译。
- 不包含异常处理
- 不包括_clineCtxList的锁定,但强烈推荐