在C#中增加WCF Web服务的超时时间

17

我目前有一个应用程序,调用服务器上的Web服务进行搜索。我们可以期望返回大量数据,因此搜索需要花费超过一分钟的时间是常见的。

对于这样的大容量搜索,我们收到了以下错误消息:

  

请求通道在等待00:00:59.7350618后等待答复时超时。增加传递给Request调用的超时值或增加Binding的SendTimeout值。分配给此操作的时间可能是较长超时的一部分。

这是我们在已发布的多个问题中看到的内容,不幸的是,所有可用解决方案都没有帮助我解决问题,甚至无法配置超时窗口。

我们已更改客户端的app.config,并增加了所有涉及的超时时间(CloseTimeout,OpenTimeout,ReceiveTimeout和SendTimeout),以及服务器上服务的所有web.config值(closeTimeout,openTimeout和SendTimeout)。

但是这些更改都没有任何效果,我仍然收到一分钟超时。 有什么想法为什么更改这些值没有任何效果?

在下面的示例中,我们缩短了时间,以避免在测试期间必须等待整个分钟。

Web.config:

<configuration>
  <system.web>
    <compilation targetFramework="4.0" />
  </system.web>
  <system.diagnostics>
    <trace autoflush="true" />
    <sources>
      <source name="System.Net">
        <listeners>
          <add name="TraceFile" />
        </listeners>
      </source>
      <source name="System.Net.Sockets">
        <listeners>
          <add name="TraceFile" />
        </listeners>
      </source>
    </sources>
    <sharedListeners>
      <add name="TraceFile" type="System.Diagnostics.TextWriterTraceListener"
           initializeData="trace.log" />
    </sharedListeners>
    <switches>
      <add name="System.Net" value="Verbose" />
      <add name="System.Net.Sockets" value="Verbose" />
    </switches>
  </system.diagnostics>
  <system.serviceModel>
    <diagnostics>
      <messageLogging logMalformedMessages="false" logMessagesAtServiceLevel="false"
                      logMessagesAtTransportLevel="false" />
    </diagnostics>
    <services>
      <service behaviorConfiguration="SearchIndexServiceBehavior" name="SearchIndex.Service">
        <endpoint address="" binding="basicHttpBinding" contract="ISearchIndexServices" />
        <host>
          <timeouts closeTimeout="00:00:10" />
        </host>
      </service>
    </services>
    <bindings>
      <basicHttpBinding>
        <binding closeTimeout="00:00:10" openTimeout="00:00:15" sendTimeout="00:00:20"
                 receiveTimeout="00:00:25" maxBufferSize="2147483647" maxBufferPoolSize="2147483647"
                 maxReceivedMessageSize="2147483647">
          <readerQuotas maxDepth="2147483647" maxStringContentLength="2147483647"
                        maxArrayLength="2147483647" maxBytesPerRead="2147483647"
                        maxNameTableCharCount="2147483647" />
        </binding>
      </basicHttpBinding>
    </bindings>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SearchIndexServiceBehavior">
          <serviceMetadata httpGetEnabled="false" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
  <system.transactions>
    <defaultSettings timeout="00:05:00" />
  </system.transactions>
</configuration>

应用程序配置

<configuration>
  <configSections>
  </configSections>
  <startup>
  </startup>
  <system.serviceModel>
    <bindings>
      <basicHttpBinding>
        <binding name="BasicHttpBinding_ISearchIndexServices" closeTimeout="00:00:10" openTimeout="00:00:15" receiveTimeout="00:10:00" sendTimeout="00:00:20" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="5242880" maxBufferPoolSize="524288" maxReceivedMessageSize="5242880" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true"> 
          <readerQuotas maxDepth="32" maxStringContentLength="5242880"maxArrayLength="16384" maxBytesPerRead="4096" maxNameTableCharCount="16384" />
          <security mode="None">
            <transport clientCredentialType="None" proxyCredentialType="None" realm="" />
            <message clientCredentialType="UserName" algorithmSuite="Default" />
          </security>
        </binding>
      </basicHttpBinding>
    </bindings>
    <client>
      <endpoint address="http://MyServer/SearchIndexService/SearchIndexServices.svc" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISearchIndexServices" contract="WFC.ISearchIndexServices" name="BasicHttpBinding_ISearchIndexServices" />
    </client>
  </system.serviceModel>
</configuration>

你是如何连接到WCF服务的?你是使用生成的客户端(添加服务引用)、ChannelFactory<T>还是实现了ClientBase<T>? - cadrell0
问题描述在这里,可能表明你的“maxReceivedMessageSize”可能太小了。虽然那时错误信息会很奇怪。但我记得有一段时间我也遇到过这个问题。 - Clemens
1
我刚刚向我的服务器添加了一个服务引用。 - user1183014
6个回答

24

我认为你可能会遇到客户端请求通道的操作超时问题,由于某些原因,这个问题不容易通过标准配置属性进行调整。

在调用长时间运行的操作之前,请尝试在客户端代码中使用以下代码:

((IContextChannel)clientProxy.InnerChannel).OperationTimeout = new TimeSpan(0,30,0); // For 30 minute timeout - adjust as necessary

其中clientProxy是服务引用生成的客户端类的实例(该类派生自ClientBase<ISearchIndexService>)。


8
对于其他人来说,我的服务客户端实现了 IClientChannel 接口的 InnerChannel 属性拥有相同的 OperationTimeout 设置。最终我只需要使用 myClient.InnerChannel.OperationTimeout = new TimeSpan(0,30,0);。没有查明原因。 - Eirik H

6
你可以尝试这个。
 SeriveClient client=new ServiceClient();
    var time = new TimeSpan(0, 3, 0);
    client.Endpoint.Binding.CloseTimeout = time;
                client.Endpoint.Binding.OpenTimeout = time;
                client.Endpoint.Binding.ReceiveTimeout = time;
                client.Endpoint.Binding.SendTimeout = time;

1
OP说他已经尝试过了,但没有效果。那是因为触发的是不同的超时时间,即OperationTimeout。 - Chris Dickson
这个答案很有用,因为它展示了一种不使用app.config来访问WCF客户端超时的不同方式。在我的情况下,它对我很有帮助,因为我只想快速测试我的WebService [所以设置了5秒的超时时间]。一旦我知道WebService正常工作, - Simon
多年后,微软在https://learn.microsoft.com/en-us/dotnet/framework/wcf/feature-details/configuring-timeout-values-on-a-binding上声称:“在客户端方面:SendTimeout - 用于初始化OperationTimeout,它管理发送消息的整个过程,包括为请求/响应服务操作接收回复消息。” - Art Schmidt

5
这个服务模型示例展示了需要在客户端web.config中设置的绑定元素超时属性。
请注意,所有超时属性都设置为10分钟。此示例使用一个绑定配置元素来设置"maxItemsInObjectGraph"属性。通常,如果您需要延长超时时间,那么这意味着您可能正在传输大量数据。还要注意,要传输的数据大小都设置为可处理高达2 GB的大小。
<?xml version="1.0" encoding="UTF-8"?>
<system.serviceModel>
   <bindings>
      <basicHttpBinding>
         <binding name="BasicHttpBinding_ISearchIndexServices" closeTimeout="00:10:00" openTimeout="00:10:00" receiveTimeout="00:10:00" sendTimeout="00:10:00" allowCookies="false" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferSize="2147483647" maxBufferPoolSize="2147483647" maxReceivedMessageSize="2147483647" messageEncoding="Text" textEncoding="utf-8" transferMode="Buffered" useDefaultWebProxy="true">
            <readerQuotas maxDepth="32" maxStringContentLength="2147483647" maxArrayLength="2147483647" maxBytesPerRead="2147483647" maxNameTableCharCount="2147483647" />
            <security mode="None">
               <transport clientCredentialType="None" proxyCredentialType="None" realm="" />
               <message clientCredentialType="UserName" algorithmSuite="Default" />
            </security>
         </binding>
      </basicHttpBinding>
   </bindings>
   <client>
      <endpoint address="http://MyServer/SearchIndexService/SearchIndexServices.svc" behaviorConfiguration="SearchIndexServicesBehavior" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_ISearchIndexServices" contract="WFC.ISearchIndexServices" name="BasicHttpBinding_ISearchIndexServices" />
   </client>
   <behaviors>
      <endpointBehaviors>
         <behavior name="SearchIndexServicesBehavior">
            <dataContractSerializer maxItemsInObjectGraph="2147483647" />
         </behavior>
      </endpointBehaviors>
   </behaviors>
</system.serviceModel>

0

调试时超时是个麻烦事。谁能在一分钟内完成调试!我使用了上面的Junior M/BuzzWilder,并在app.config中的服务调用中添加了以下内容:

            <binding name="WSHttpBinding_bindingname"
                     openTimeout="01:00:00"
                     closeTimeout="01:00:00"
                     receiveTimeout="01:00:00"
                     sendTimeout="01:00:00"
                     >

再见服务超时。 :)


0

超时时间是客户端的发送超时时间。这是客户端准备等待响应的时间,因此必须在客户端上设置而不是服务端。


客户端的 app.config 上的 SendTimeout 设置为 20 秒。即使设置得这么低,请求仍需要一分钟才能出错。我已经在客户端和服务上都将 sendTimeout 设置为 20 秒。 - user1183014
听起来好像你的配置设置没有被捕获 - 你是否在调试器中检查过客户端代理绑定是否确实为20s? - Richard Blewett
1
SendTimeout仅涵盖传输协议完成将请求消息发送到服务器的时间。如果响应消息需要很长时间才能到达,此超时不会被触发。 - Chris Dickson

0
截至目前,越来越多的WCF用户转向ASP.Net Core,因此我在这里添加了一个指南,介绍如何使用CoreWCF来增加超时时间,尽管这种方法也适用于WCF。
以下是您如何更轻松地测试和配置WCF中的超时时间。超时时间在客户端项目中进行配置。
首先,要有图片证明,否则就没有发生过。在这里,您可以看到我在15秒后就超时了,那我是如何做到的呢?

Quick timeout for WCF client

注意,我在客户端使用以下Nuget包,它是一个.NET 6控制台应用程序:
<ItemGroup>
    <PackageReference Include="System.ServiceModel.Duplex" Version="4.10.*" />
    <PackageReference Include="System.ServiceModel.Federation" Version="4.10.*" />
    <PackageReference Include="System.ServiceModel.Http" Version="4.10.*" />
    <PackageReference Include="System.ServiceModel.NetTcp" Version="4.10.*" />
    <PackageReference Include="System.ServiceModel.Security" Version="4.10.*" />
  </ItemGroup>

服务器项目使用了这些Nuget包,我已经从CoreWCF模板中创建了客户端和服务端的WCF。
  <ItemGroup>
    <PackageReference Include="CoreWCF.Primitives" Version="1.*" />
    <PackageReference Include="CoreWCF.Http" Version="1.*" />
  </ItemGroup>

在客户端的自动生成文件Reference.cs中,我们得到了这个方法:
   [System.Diagnostics.DebuggerStepThroughAttribute()]
    [System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.Tools.ServiceModel.Svcutil", "2.1.0")]
    public partial class ServiceClient : System.ServiceModel.ClientBase<MyService.IService>, MyService.IService
    {
        
        /// <summary>
        /// Implement this partial method to configure the service endpoint.
        /// </summary>
        /// <param name="serviceEndpoint">The endpoint to configure</param>
        /// <param name="clientCredentials">The client credentials</param>
        static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials);

//more code 

我们记录客户端的命名空间,并添加以下配置了一组超时时间的部分类。根据需要调整这些超时时间。
我在这里添加了一个名为ServiceClient的文件,类名与Reference.cs中的ServiceClient类名相匹配。
namespace MyService
{
    public partial class ServiceClient
    {

        /// <summary>
        /// Implement this partial method to configure the service endpoint.
        /// </summary>
        /// <param name="serviceEndpoint">The endpoint to configure</param>
        /// <param name="clientCredentials">The client credentials</param>
        static partial void ConfigureEndpoint(System.ServiceModel.Description.ServiceEndpoint serviceEndpoint, System.ServiceModel.Description.ClientCredentials clientCredentials)
        {
            serviceEndpoint.Binding.OpenTimeout 
                = serviceEndpoint.Binding.CloseTimeout
                = serviceEndpoint.Binding.ReceiveTimeout
                = serviceEndpoint.Binding.SendTimeout = TimeSpan.FromSeconds(15);
        }

    }
}

设置的超时时间为15秒。显然,您可能希望这些超时时间稍长一些,我只输入了一个小值来快速测试。默认值通常为30或60秒,在生产环境中,如果您的服务方法需要执行长时间运行的作业,您可能需要考虑几分钟的超时时间,或者考虑使用类似Hangfire的框架。
这种方法的好处是,如果您更新Reference.cs(在VS 2022中仍然有点麻烦,不像在以前的WCF和.NET Framework中那样容易),您配置的设置仍然保持不变。通过设置终结点绑定,您还可以进行许多其他配置。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接