【问题标题】:WCF ServiceHost crashes when UserNamePasswordValidator takes too long to process当 UserNamePasswordValidator 处理时间过长时,WCF ServiceHost 崩溃
【发布时间】:2010-10-28 08:18:47
【问题描述】:

我发现了一种情况,如果 WCF ServiceHost 具有需要一段时间来处理的 UserNamePasswordValidator,WCF 客户端可能会导致 WCF ServiceHost 崩溃。在我向微软提出这个问题之前,我想听听有关解决方案的任何建议。

重现的步骤是:

  1. 打开一个频道
  2. 调用需要 10 秒返回的 API 方法。
  3. 在 API 方法返回之前关闭通道。
  4. API 方法返回。 ServiceHost 崩溃。

仅当 UserNamePasswordValidator 需要一段时间来处理(我的示例,2 秒睡眠)时才会发生上述情况。如果 UserNamePasswordValidator 立即返回,则上述情况发生变化:

  1. 打开一个频道
  2. 调用需要 10 秒返回的 API 方法。
  3. 在 API 方法返回之前关闭通道。
  4. 关闭通道等待挂起的 API 方法调用。
  5. API 方法返回。
  6. 频道已关闭。一切都很好。

客户端代码如下:

public static void Main(string[] args)
{
    var binding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
    binding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;
    var identity = new DnsEndpointIdentity("Dummy");
    var endpointAddress = new EndpointAddress(new Uri("net.tcp://localhost:5000/"), identity);
    var channelFactory = new ChannelFactory<IService>(binding, endpointAddress);

    channelFactory.Credentials.ServiceCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
    channelFactory.Credentials.UserName.UserName = "foo";
    channelFactory.Credentials.UserName.Password = "bar";

    channelFactory.Open();

    IService service = channelFactory.CreateChannel();

    ThreadPool.QueueUserWorkItem(CallPingOnChannel, service);

    Thread.Sleep(TimeSpan.FromSeconds(1));

    channelFactory.Close();
}

private static void CallPingOnChannel(object state)
{
    var result = ((state) as IService).Ping();
}

ServiceHost设置如下:

public static void Main(string[] args)
{
    var serviceHost = new ServiceHost(typeof(Service), TcpBaseAddress);
    serviceHost.Description.Behaviors.Add(new ErrorHandler());
    var netTcpBinding = new NetTcpBinding(SecurityMode.TransportWithMessageCredential);
    netTcpBinding.Security.Transport.ClientCredentialType = TcpClientCredentialType.Certificate;
    netTcpBinding.Security.Message.ClientCredentialType = MessageCredentialType.UserName;

    serviceHost.Credentials.UserNameAuthentication.UserNamePasswordValidationMode = UserNamePasswordValidationMode.Custom;
    serviceHost.Credentials.UserNameAuthentication.CustomUserNamePasswordValidator = new UserValidator();

    serviceHost.Credentials.ClientCertificate.Authentication.CertificateValidationMode = X509CertificateValidationMode.None;
    serviceHost.Credentials.ServiceCertificate.Certificate = GetCertificate();

    serviceHost.AddServiceEndpoint(typeof(IService), netTcpBinding, "");

    serviceHost.Open();

    Console.ReadLine();
}

而UserValidator的实现如下:

public class UserValidator : UserNamePasswordValidator
{
    public override void Validate(string userName, string password)
    {
        Thread.Sleep(TimeSpan.FromSeconds(2));
    }
}

导致主线程崩溃的未处理异常是:

System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '10675199.02:48:05.4775807'. ---> System.IO.IOException: The read operation failed, see inner exception. ---> System.ServiceModel.CommunicationException: The socket connection was aborted. This could be caused by an error processing your message or a receive timeout being exceeded by the remote host, or an underlying network resource issue. Local socket timeout was '10675199.02:48:05.4775807'. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host

【问题讨论】:

  • 建议...我不想说显而易见的,但是:验证密码不需要很长时间?
  • Marc:如你所知,密码验证可能依赖于外部资源,例如文件系统、数据库、缓存等。如果这些进程存在性能问题,它绝不会导致我的 API 崩溃 尤其不是手工制作的客户。

标签: c# .net wcf


【解决方案1】:

回答我自己的问题;我向 Microsoft 报告了此问题,他们已确认这是框架中的错误,将在即将发布的版本中修复。

链接:http://connect.microsoft.com/wcf/feedback/details/622164/wcf-servicehost-crashes-when-usernamepasswordvalidator-takes-too-long-to-process

【讨论】:

    【解决方案2】:

    首先,您应该在客户端代码中捕获 System.TimeOutException。

    接下来,您应该考虑扩展您的客户端和服务的 SendTimeoutRecieveTimeout

    看看这两个链接。

    SendTimeOut Property

    RecieveTimeOut Property

    【讨论】:

    • 客户端没有抛出TimeoutException,延长SendTimeout或ReceiveTimeout对问题没有影响。
    • 你指的是我对 SendTimeout 或 ReceiveTimeout 的使用吗?显然我将它设置在 NetTcpBinding 上。即使我将其设置为 15 分钟,问题仍然存在。不过,我不明白为什么超时设置会导致 ServiceHost 中出现未处理的异常。
    • 好的,你能不能给post一个trace,把它设置为verbose并发布结果。
    猜你喜欢
    • 2019-03-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2018-03-15
    • 1970-01-01
    • 1970-01-01
    • 2012-12-04
    • 2021-05-16
    相关资源
    最近更新 更多