Подтвердить что ты не робот

Почему первый вызов WCF-клиента замедляется?

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

Что я сделал, чтобы проверить это:

  • Реализован простой сам сервер WCF и консольный клиент.
  • Сервер разогревается. Я запускаю его и вызываю метод несколько раз перед запуском теста.
  • Связывание basicHttpBinding уменьшает затраты на сеть и безопасность.
  • Сценарий тестирования - запустите консольное клиентское приложение, выполнив два одинаковых вызова службы WCF в строке.

В моих тестах я вижу ~ 700 миллисекунд для первого вызова и ~ 3 миллисекунды для второго вызова.

Кажется, что для JIT-компилятора слишком много времени. Я бы согласился, если это время используется для инициализации некоторой сложной инфраструктуры, такой как ObjectContext в Entity Framework, но мой код очень прост и прокси-классы уже скомпилированы.

Я также пробовал привязку netNamedPipeBinding. Результат доказывает шаблон - первый вызов занимает ~ 800 мс, второй вызов занимает ~ 8 мс.

Поймите, если кто-нибудь сможет объяснить, почему первый вызов службы занимает так много времени.

Протестировано в Win 7 64 бит.

Моя реализация ниже.

Contract:

[ServiceContract]
public interface ICounter
{
        [OperationContract]
        int Add(int num);
}

Реализация службы:

public class CounterService: ICounter
{
        private int _value = 0;

        public int Add(int num)
        {
            _value += num;
            Console.WriteLine("Method Add called with argument {0}. Method  returned {1}", num, _value);
            return _value;
        }
}

Реализация сервера:

class Program
{
    static void Main(string[] args)
    {
        Uri baseAddress = new Uri("http://localhost:8080/Service");

        // Create the ServiceHost.
        using (ServiceHost host = new ServiceHost(typeof(CounterService), baseAddress))
        {
            host.Open();

            Console.WriteLine("The service is ready at {0}", baseAddress);
            Console.WriteLine("Press <Enter> to stop the service.");
            Console.ReadLine();

            // Close the ServiceHost.
            host.Close();
        }
    }
}

Конфигурация сервера:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <services>
      <service name="Server.CounterService">
        <endpoint address="base" binding="basicHttpBinding" name="baseDefault"
          contract="Contract.ICounter" />
        <endpoint address="net.pipe://localhost/Service/netNamedPipe"
          binding="netNamedPipeBinding" name="netNamedPipeDefault" contract="Contract.ICounter" />
        <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange" />
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>
</configuration>

Реализация клиента (CounterProxy создается из справки службы):

Stopwatch stopWatch = new Stopwatch();
stopWatch.Start();

using (var proxy = new CounterProxy.CounterClient(_endpointConfigurationName))
{
    output = proxy.Add(1);
}

stopWatch.Stop();
// Get the elapsed time as a TimeSpan value.
TimeSpan ts = stopWatch.Elapsed;

Функция, которая содержит этот код, который два раза подряд указан.

Конфигурация клиента:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <system.serviceModel>
    <client>
      <endpoint address="http://localhost:8080/Service/base" binding="basicHttpBinding"
          contract="CounterProxy.ICounter"
          name="baseDefault" />
    </client>
  </system.serviceModel>
</configuration>
4b9b3361

Ответ 1

Обычно первый вызов занимает больше времени, потому что в этом вызове Channel Factory создается и готовится к сообщению, и это требует времени. Созданный Channel Factory будет кэшироваться и повторно использоваться в последующих вызовах, поэтому время будет меньше.

http://social.msdn.microsoft.com/Forums/en/wcf/thread/43f89088-546b-46b0-adf8-214deb1741bd

Ответ 2

У меня схожая проблема. Итак, что мы на самом деле сделали, мы написали сервис, который вызывает службу WCF за некоторый интервал. Я знаю, что это не элегантное решение, но оно работает.

Ответ 3

Если вы звоните в ваш WCF-сервис реже, чем за 15 секунд (мы наблюдали, что вам нужно ждать около 20 секунд в нашем приложении), этот блог Microsoft объясняет вашу проблему: http://blogs.msdn.com/b/wenlong/archive/2010/02/11/why-does-wcf-become-slow-after-being-idle-for-15-seconds.aspx

Статья также ссылается на эту запись, в которой упоминается исправление для SetMinThreads(), которое также представляется важной проблемой: http://blogs.msdn.com/b/wenlong/archive/2010/02/11/why-are-wcf-responses-slow-and-setminthreads-does-not-work.aspx

Ответ 4

Я видел задержки в диапазоне 30 секунд, когда я впервые создавал экземпляр прокси-сервера службы, который, как я знал, должен быть связан с каким-то сетевым таймаутом.

В конце концов, это было фактически проверка списка отзыва сертификатов, который был заблокирован или расстроен корпоративным прокси (yay Websense), как указано здесь: Запуск службы WCF слишком медленный? Вы считали, что CRL проверяет?.

Для справок в будущем и в случае, если ссылка остыла, она сводилась к добавлению следующего в конфигурацию клиента:

<configuration>
  <runtime>
    <generatePublisherEvidence enabled="false"/>
  </runtime>
</configuration>