C# 服务器500:挂起的安全对话太多

C# 服务器500:挂起的安全对话太多,c#,.net,wcf,C#,.net,Wcf,我有一个相当简单的WCF web服务,使用.NET3.5托管在IIS Express(最终成为完整的IIS)中。服务方法相当乏味 [ServiceContract] public class MySvc { [OperationContract] public Stuff MyMethod(string input) { Stuff result = DoSomething(); return result; } } 服务配置也相

我有一个相当简单的WCF web服务,使用.NET3.5托管在IIS Express(最终成为完整的IIS)中。服务方法相当乏味

[ServiceContract]
public class MySvc
{
    [OperationContract]
    public Stuff MyMethod(string input)
    {
        Stuff result = DoSomething();
        return result;
    }
}
服务配置也相当通用:

<system.serviceModel>
    <services>
        <service behaviorConfiguration="MySvcBehavior" name="MySvc">
            <endpoint address="" binding="wsHttpBinding" contract="MySvc">
                <identity>
                    <dns value="localhost"/>
                </identity>
            </endpoint>
            <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"/>
        </service>
    </services>
    <behaviors>
        <serviceBehaviors>
            <behavior name="MySvcBehavior">
                <serviceMetadata httpGetEnabled="true"/>
                <serviceDebug includeExceptionDetailInFaults="false"/>
            </behavior>
        </serviceBehaviors>
    </behaviors>
</system.serviceModel>
只要一次只有一个请求,一切都正常工作,客户端代码就会得到预期的结果。耶

当我做一些非常原始的压力测试时,问题就来了。我在浏览器中加载客户端ASPX页面,然后按住F5键。观察IIS Express窗口,起初结果显示为状态200,但几分钟后,我开始看到状态500。此时,在我重新启动IIS Express之前,服务将仅以状态500响应。(基于等待约10分钟。)

在客户端代码中设置断点,我看到完整的返回消息是“服务器上有太多挂起的安全对话。请稍后重试。”

在服务器代码中设置断点,我发现我的代码甚至没有被调用。所以它在调用和代码实际开始之间的某个地方失败了

我的在线搜索不是很有希望,主要是为了覆盖maxPendingSessions属性和一个以“有人告诉我有一个[unnamed]配置文件设置”开头的线程,然后导致一个断开的链接,声称微软已经承认这是一个bug


关于maxPendingSessions属性的链接确实提到了128个连接的限制,超时时间为2分钟,我当然可以看到我的测试方法将在哪里中断一些连接。这是公认的糟糕测试方法的预期结果吗?或者可以在配置中做些什么来改善这一点?

听起来您在服务器上有无数个打开的连接-因为客户端没有使用“using”来连接。

到目标服务器的连接太多了。有一些方法可以正确地关闭连接。已经提到的是,在WCF代理周围应该始终有一个
using
语句

除此之外,还有一个bug要求您在打开代理时发出请求。在创建代理时,代理大约有两分钟的时间向服务器发出请求。如果确实如此,则会使计时器无效,从而允许清理代理。如果未发出请求,则代理在实际关闭连接之前仍会等待超时。这次可能不是这样,但为了完整性增加了

解决这个问题的一种方法是使用(ASP.NET)缓存
System.Web.Caching.Cache
是一个很好的选择。它允许您缓存给定(最终滑动)时间段的结果。这样,每2分钟只发出一个请求。这使得网站/Web服务具有很强的可伸缩性。例如,如果每分钟有1000个请求,则同一服务调用只使用了一个连接

另一种方法是,如果请求很昂贵且不值得等待,则提前发出请求并缓存响应(alacron、任务调度器或其他线程)。这将允许快速检索页面

除此之外,还应正确配置服务器。不需要从一个客户端打开128个并发连接。尝试将此限制为每个客户端8个连接。这允许同时处理多个客户端。同时尝试设置一个合适的超时。等待5分钟让客户端关闭连接并不能真正提高吞吐量


也可以考虑。它使用服务必须等待I/O的时间来处理附加连接。我看不到WCF服务的功能,因此这可能与此无关。

我建议首先在服务器类计算机上的完整IIS上测试此功能。我不会信任客户端操作系统上的IIS Express进行性能测试。

如果仍然很糟糕,那么请查看限制maxConcurrentCalls的节流

对于推荐使用声明的人:您不应该使用使用声明来关闭客户。

要关闭客户端,应使用以下代码段:

try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}
如果您不想缓存显式异常,也可以简单地使用普通捕获。有时,您可能希望捕获显式异常,因为如果通信对象没有出现故障,您可以重试您的操作,以提供资源的最佳利用

对于服务器上挂起的对话问题,一旦正确关闭客户端,这种情况将变得不常见,但是如果服务器端代码运行时间较长,并且客户端正在排队,则仍然会看到这种情况。在这种情况下,要增加MaxPendingChannel的值,您必须在客户端和接收方配置文件中使用自定义绑定,或者在启动服务之前以编程方式修改接收方的绑定。如果使用.NET 3.5,MaxPendingChannel的默认值实际上是4。对于.NET 3.0,该值据称为128。在我看来,两者都很低


请参见

您是否使用了语句?该连接将保持打开状态,直到处理完毕。除非有
using
语句,否则连接的清理是由垃圾收集器决定的。这确实是我的客户端代码的问题。但是状态500消息意味着服务器端的情况不好。我不能反驳这种说法。:-)但是,既然我不能强迫客户机(我自己的客户机除外)写得很好,那么我该如何最好地防范这个问题呢?不幸的是,这基本上是(无意中)一种DOS攻击(拒绝服务),防范它的唯一方法是在一段时间的不活动后断开连接。在您的测试案例中,我怀疑这是否会有帮助,除非您能够将超时设置为几秒钟。这基本上也是我得出的结论。同意,这些都是正确的
try
{
    ...
    client.Close();
}
catch (CommunicationException e)
{
    ...
    client.Abort();
}
catch (TimeoutException e)
{
    ...
    client.Abort();
}
catch (Exception e)
{
    ...
    client.Abort();
    throw;
}