Очень задержанный ответ IIS от службы WCF при возврате довольно большого набора данных

У меня есть служба WCF, размещенная на IIS 7, которая в основном используется для возврата данных через структуру сущностей. Один из методов контрактов / сервисных операций возвращает список из примерно 17000 очень простых и довольно небольших объектов. Размер ответа составляет примерно 6,5 МБ, поэтому он не очень большой. Когда я размещаю службу на своем компьютере для разработки с IIS Express, вызывается служба, и данные отправляются обратно быстро (в течение 10 секунд). Когда я отправляю сервис на наш сервер, ответ занимает в среднем 1:46 секунды. Проведя некоторую трассировку на веб-сервере, я обнаружил, что метод, который извлекает данные, занимает всего 6 секунд, чтобы вернуться. В этом случае серверу требуется около 1:40 секунд для подготовки, а затем отправки ответа (что я подтвердил в веб-журналах, так что это не проблема задержки в сети).

Это единственные конфигурации, которые у меня есть в определении сервиса:

  [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single)]
  [AspNetCompatibilityRequirements(RequirementsMode = AspNetCompatibilityRequirementsMode.Required)]

Вот как выглядит мой web.config:

<?xml version="1.0"?>
<configuration>
  <system.web>
    <compilation debug="true" targetFramework="4.0"/>
    <httpRuntime executionTimeout="600" />
  </system.web>
  <system.serviceModel>

    <services>
      <service behaviorConfiguration="RGDataBehavior" name="WCFData.Web.Services.DataAccess">
        <endpoint address="" binding="basicHttpBinding" bindingConfiguration="LargeBuffer" name="RGData_http" contract="WCFData.Web.Services.IDataAccess" listenUriMode="Explicit"/>
        <host>
          <baseAddresses>
            <add baseAddress="http://myurl.com/Services/Data.svc"/>
          </baseAddresses>
        </host>
      </service>
    </services>

    <bindings>
      <basicHttpBinding>
        <binding name="LargeBuffer"
                 closeTimeout="00:10:00"
                 openTimeout="00:10:00"
                 receiveTimeout="00:10:00"
                 sendTimeout="00:10:00"
                 transferMode="StreamedResponse"
                 maxBufferPoolSize="2147483647"
                 maxBufferSize="2147483647"                 
                 maxReceivedMessageSize="2147483647">
          <readerQuotas maxDepth="2147483647"
                        maxArrayLength="2147483647"
                        maxBytesPerRead="2147483647"
                        maxStringContentLength="2147483647"
                        maxNameTableCharCount="2147483647"/>
        </binding>
      </basicHttpBinding>
    </bindings>

    <behaviors>
      <serviceBehaviors>
        <behavior name="RGDataBehavior">
          <dataContractSerializer maxItemsInObjectGraph="2147483647"/>
          <serviceMetadata httpGetEnabled="false"/>
          <serviceDebug includeExceptionDetailInFaults="true"/>
          <serviceAuthorization
            serviceAuthorizationManagerType="WCFData.Web.Authentication.WCFAuthenticator,
                                             Data.Web.Authentication"/>
        </behavior>
      </serviceBehaviors>
    </behaviors>

    <serviceHostingEnvironment aspNetCompatibilityEnabled="true" />
  </system.serviceModel>
  <system.webServer>
    <modules runAllManagedModulesForAllRequests="true"/>
  </system.webServer>
</configuration>

Я ломал голову над этим, и большая часть того, что я нашел, кажется, не совсем отвечает всем требованиям, поэтому я хотел бы получить некоторую помощь, если у кого-то есть предложения. Заранее спасибо.

2 ответа

Решение

Ответ на мой вопрос - скорее работа вокруг, чем истинное решение проблемы. Изменение метода службы таким образом, чтобы он возвращал байтовый массив, а не коллекцию, чтобы IIS не приходилось записывать сериализованную коллекцию XML в ответ, и это позаботилось об этой проблеме, но она все еще не объясняет, почему в первую очередь произошла задержка, Я использовал библиотеку protobuf-net для сериализации своей возвращаемой коллекции в байтовый массив, а затем снова на стороне клиента для десериализации массива.

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

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

Если все остальное терпит неудачу, вы всегда можете взять некоторые дампы потоков во время обработки сервером (в течение этого времени обработки 1m40s) и посмотреть трассировку стека для каждого потока, чтобы увидеть, что может застрять. Обычно я делаю это с помощью отладчика командной строки ntsd.exe, который устанавливается вместе с пакетом средств отладки для Windows.


Редактировать:

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

[OperationContract]
public YourData[] YourServiceMethod()
{
    YourData[] data = ... // get all your data here as usual.

    // lets serialize it and time how long it takes
    var timer = new System.Diagnostics.Stopwatch();
    timer.Start();
    var dataContractSerializer = new System.Runtime.Serialization.DataContractSerializer(data.GetType());
    using (var memoryStream = new System.IO.MemoryStream())
    {
        dataContractSerializer.WriteObject(memoryStream, data);
    }
    timer.Stop();

    System.Console.WriteLine(timer.ElapsedMilliseconds); // Log this, or whatever...

    // return now as normal (WCF will re-serialize, talking even longer, but this is just a test anyway)
    return data;
}
Другие вопросы по тегам