方案
我支持/增强了一个Windows窗体应用程序,它通过使用ServiceHostFactory类在IIS中托管的WCF服务进行通信。
对于我想解决的问题,我正在处理ServiceContract中的这两个方法。 ServiceContract标记为PerCall。
[OperationContract(IsOneWay = true)]
void RunJob(int jobId);
[OperationContract]
byte[] GetUserJobs(int userID);
用户将通过 RunJob 方法以火灾和忘记的方式提交作业。这个工作将对另一个服务进行WCF调用,获取一些数据,用它做很多事情并将其存储在数据库中。这项特殊工作大约需要62分钟才能完成。
当作业处于Running状态时,客户端每10秒异步调用 GetUserJobs 方法来检查作业状态并相应地更新GUI。客户端通过netTcpBinding与WCF服务进行通信。
问题
在1小时左右,GUI变得完全没有响应。仍在进行异步调用,但永远不会调用已完成的事件。在我看来,某些东西已陷入僵局或阻塞,我无法确切地知道为什么会发生这种情况。在RunJob(OneWay)实际在服务器上完成之前,GUI可能开始变得无法响应,但是作业本身总是完成并且数据被保存到数据库中。
因此即使GUI不可用,服务器仍然可以工作,除非它不响应任何WCF调用。
如果我在服务器上编辑web.config以回收IIS,则GUI会再次响应。
我绝不是WCF专家,我真的很想找到解决这个问题的方法。由于异步调用,我很确定它正在发生,但我还无法查明确切的原因。根据以下代码,异步调用将被关闭。
根据我的跟踪日志,当GUI无响应时,我看到以下错误:
<ExceptionType>System.ServiceModel.CommunicationException, System.ServiceModel, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</ExceptionType>
<Message>There was an error writing to the pipe: The pipe is being closed. (232, 0xe8).</Message>
然后,对于活动http://tempuri.org/IConnectionRegister/ValidateUriRoute,我会看到:
<DataItem> <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Warning"> <TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.CommunicationObjectFaulted.aspx</TraceIdentifier> <Description>Faulted System.ServiceModel.Channels.ClientFramingDuplexSessionChannel</Description> <AppDomain>/LM/W3SVC/2/ROOT/Service_mt-4-130395564562210673</AppDomain> <Source>System.ServiceModel.Channels.ClientFramingDuplexSessionChannel/56837067</Source> </TraceRecord> </DataItem>
<DataItem> <TraceRecord xmlns="http://schemas.microsoft.com/2004/10/E2ETraceEvent/TraceRecord" Severity="Warning"> <TraceIdentifier>http://msdn.microsoft.com/en-US/library/System.ServiceModel.CommunicationObjectFaulted.aspx</TraceIdentifier> <Description>Faulted System.ServiceModel.Channels.ServiceChannel</Description> <AppDomain>/LM/W3SVC/2/ROOT/Service_mt-4-130395564562210673</AppDomain> <Source>System.ServiceModel.Channels.ServiceChannel/3537205</Source> </TraceRecord> </DataItem>
相关代码/配置(有点消毒)
客户端绑定配置
我试图将所有超时设置为“无限”,只是为了排除存在一些奇怪的超时行为。我尝试了各种超时设置,但似乎没有任何效果。
<binding name="MyEndpoint" closeTimeout="infinite"
openTimeout="infinite" receiveTimeout="infinite" sendTimeout="infinite"
transactionFlow="false" transferMode="Buffered" transactionProtocol="OleTransactions"
hostNameComparisonMode="StrongWildcard" listenBacklog="10"
maxBufferPoolSize="2147483647" maxBufferSize="2147483647"
maxConnections="100" maxReceivedMessageSize="2147483647">
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
maxArrayLength="2147483647" maxBytesPerRead="2147483647"
maxNameTableCharCount="2147483647" />
<reliableSession ordered="true" inactivityTimeout="infinite"
enabled="false" />
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
</binding>
服务器绑定配置
<netTcpBinding>
<binding name="MyBinding" portSharingEnabled="true" transferMode="Buffered" closeTimeout="infinite" openTimeout="infinite" receiveTimeout="infinite" sendTimeout="infinite" maxBufferPoolSize="524288" maxBufferSize="2147483647" maxConnections="10" listenBacklog="200" maxReceivedMessageSize="2147483647">
<security mode="Transport">
<transport clientCredentialType="Windows" protectionLevel="EncryptAndSign" />
<message clientCredentialType="Windows" />
</security>
<reliableSession ordered="true"
inactivityTimeout="infinite"
enabled="false" />
<readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
</binding>
<netTcpBinding>
<serviceBehaviors>
<behavior name="MyServiceBehavior">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<ServiceErrorHandler />
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
</behavior>
</serviceBehaviors>
GUI执行类似于以下操作以异步调用WCF服务:
public void RefreshJobs()
{
//When the GUI becomes unresponsive, a lot of these log statements start piling up in the log file, until service is restarted
Logger.GetInstance().Info("Begin RefreshJobs");
ServiceClient svc = new ServiceClient("MyEndpoint", url);
try
{
svc.GetUserJobsCompleted += new EventHandler<GetUserJobsCompletedEventArgs>(svc_GetJobsCompleted);
}
catch (Exception e)
{
throw e;
}
svc.GetUserJobsAsync(SomeSingleton().CurrentUser.UserID, false, svc);
Logger.GetInstance().Info("End RefreshJobs");
}
private void svc_GetJobsCompleted(object sender, GetUserJobsCompletedEventArgs e)
{
Logger.GetInstance().Info("Start GetJobsCompleted");
if (e.Result != null)
{
//Do Stuff to Update GUI
}
//Close the connection
if (e.UserState is ServiceClient)
{
((ServiceClient)e.UserState).Close();
}
Logger.GetInstance().Info("End GetJobsCompleted");
}