WCF терпит неудачу каждые 10 звонков бесшумным и приводящим в бешенство способом - PullRequest
0 голосов
/ 07 января 2011

Итак, у меня есть простой тестовый сервис WCF.Идея состоит в том, что один процесс вызывает другой и регистрируется для обратных вызовов, а другой затем вызывает исходный вызывающий объект, когда происходят события.Собственные обратные вызовы WCF не работают должным образом или для этого приложения, так что не беспокойтесь об этом.У меня есть очень простая тестовая программа, которая демонстрирует поведение.Каждые 10 попыток, он приостанавливается на некоторое время (период ожидания) и восстанавливается после него.Он восстанавливается без запуска каких-либо событий канала - открыть / закрыть / что угодно.Я должен что-то упустить, очевидно, но что ...

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using System.Threading;

namespace WCFTest
{
 [ServiceContract(Namespace = "http://WCF.WTF")]
 public interface IServerEvents
 {
  [OperationContract(IsOneWay = true)]
  void Heartbeat();
 }

 [ServiceContract(Namespace = "http://WCF.WTF")]
 public interface ICallbackEvents
 {
  [OperationContract(IsOneWay = true)]
  void HeartbeatAck();
 }

 [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode =     InstanceContextMode.Single)]
 [CallbackBehavior(IncludeExceptionDetailInFaults = true)]
 class Program : IServerEvents, ICallbackEvents
 {
  static AutoResetEvent CalledBack = new AutoResetEvent(false);
  static ChannelFactory<IServerEvents> ServerChannelFactory;
  static ChannelFactory<ICallbackEvents> ClientChannelFactory;
  static ServiceHost ServerHost;
  static ServiceHost ClientHost;

  static int Timeout = 5;

  private Program()
  {
  }

  static void Main(string[] args)
  {
   NetTcpBinding binding = new NetTcpBinding(SecurityMode.None)
    {
     OpenTimeout = new TimeSpan(0, 0, Timeout),
     SendTimeout = new TimeSpan(0, 0, Timeout),
     ReceiveTimeout = new TimeSpan(0, 0, Timeout),
     MaxConnections = 50,
     ListenBacklog = 50     
    };
   Uri serverUri = new Uri("net.tcp://localhost:3123/WTF");
   ServerChannelFactory = new ChannelFactory<IServerEvents>(binding, new     EndpointAddress(serverUri));
   ClientChannelFactory = new ChannelFactory<ICallbackEvents>(binding, new     EndpointAddress("net.tcp://localhost:3123/WTF/Client"));
   ServerChannelFactory.Closing += new EventHandler((s,x) =>     Console.WriteLine("SClosing"));
   ServerChannelFactory.Opening += new EventHandler((s, x) => Console.WriteLine("SOpening"));
   ServerChannelFactory.Faulted += new EventHandler((s,x)=> Console.WriteLine("SFaulted"));
   ClientChannelFactory.Closing += new EventHandler((s, x) => Console.WriteLine("CClosing"));
   ClientChannelFactory.Opening += new EventHandler((s, x) => Console.WriteLine("COpening"));
   ClientChannelFactory.Faulted += new EventHandler((s, x) => Console.WriteLine("CFaulted"));

   ServerHost = StartServer("/Server", new Program(), typeof(IServerEvents));
   ClientHost = StartServer("/Client", new Program(), typeof(ICallbackEvents));

   while (true)
   {
    Thread.Sleep(100);
    try
    {
     new Program().Heartbeat();
     if (!CalledBack.WaitOne(2500, true))
     {
      throw new TimeoutException("Epic fail.");
     }
    }
    catch (Exception x)
    {
     Console.WriteLine("Failed heartbeat.\n{0}", x);
    }
   }
  }

  public void Heartbeat()
  {
   Console.Write(".");
   Console.Out.Flush();
   try
   {
    ClientChannelFactory.CreateChannel().HeartbeatAck();
   }
   catch (Exception x)
   {
    Console.WriteLine("Couldn't ACK heartbeat.\n{0}", x);
   }
  }

  public void HeartbeatAck()
  {
   Console.Write("!");
   Console.Out.Flush();
   CalledBack.Set();
  }

  private static ServiceHost StartServer<T>(string fragment, T remoteObject, Type interfaceType)
  {
   ServiceHost retHost = null;
   using (AutoResetEvent revent = new AutoResetEvent(false))
   {
bool hostOk = false;
// The service host has to be started on a non-sync-context thread or bad things (tm) will happen.
ThreadPool.QueueUserWorkItem((oo) =>
{
 try
 {
  retHost = new ServiceHost(remoteObject, new Uri("net.tcp://localhost:3123/WTF"));
  var binding = new NetTcpBinding(SecurityMode.None)
      {
       OpenTimeout = new TimeSpan(0, 0, Timeout),
       SendTimeout = new TimeSpan(0, 0, Timeout),
       ReceiveTimeout = new TimeSpan(0, 0, Timeout),
       MaxConnections = 50,
       ListenBacklog = 50           
      };
  retHost.AddServiceEndpoint(interfaceType, binding, fragment);
      retHost.Open();
      hostOk = true;
     }
     catch (Exception xc)
     {
      Console.WriteLine("Couldn't start WCF Service Host!\n{0}", xc);
     }
     finally
     {
      try { revent.Set(); }
      catch { }
     }
    });
    revent.WaitOne(5000, true);
    return hostOk ? retHost : null;
   }
  }
 }
}

Ответы [ 2 ]

0 голосов
/ 09 января 2011

Так что я думаю, что основная проблема здесь - плохая документация и плохие отчеты во время выполнения.Хотя это было непросто подтвердить, похоже, что существует некое ограничение (не MaxConnections / ListenBackLog) для 10 активных соединений.Так как Dispose вызывается всякий раз, ChannelFactory будет освобождать свое соединение всякий раз, когда.В результате 10 подключений израсходованы, и он приостанавливается (при выполнении запроса сокета, а НЕ при выполнении действий «реального WCF», которые будут зависеть от заданных тайм-аутов), пока не выполнится Dispose.Таким образом, это означает, что вы не можете хранить фабрики открытых каналов в долгосрочной перспективе в WCF, что я считаю плохим, тем более что вы не можете открывать и закрывать фабрики несколько раз.Поэтому лучшее, что вы можете сделать, - это хранить привязки / конечные адреса.

0 голосов
/ 07 января 2011

[Это не ответ, но слишком длинный, чтобы вместить его в комментарий.]

Тестовый пример отличается от исходного кода. В тех случаях, когда вы не совсем уверены, где существует проблема, совершенно бесполезно изменять кодовую базу, поскольку она искажает результаты.

Вместо этого смоделируйте клиент и сервер, чтобы вы могли проверить их по отдельности. WcfTestClient действует на клиента довольно хорошо.

В связи с характером сервисов я рекомендую хранить около 0 логики на первичном уровне и вместо этого перекидывать реализации в повторно используемую DLL (сочетание MVC и n-уровня). Это упрощает создание нового интерфейса службы WCF (например, консольного приложения) для целей отладки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...