Очень задержанный ответ 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;
}