Анализируя, почему WCF намного медленнее, чем веб-сервис WSE
У нас есть веб-сервис с конечными точками WSE 3.0 и более новыми конечными точками WCF в.NET Framework 4.5.
WCF использует basicHttpBinding
,
Проблема в том, что новые привязки WCF выглядят значительно медленнее (~3 раза). Использует ли он тот же механизм под капотом?
Я много читал о включении трассировки WCF. Но когда я включаю это на производстве, я получаю много информации и не знаю, как читать, например, временную шкалу в Microsoft Trace Viewer.
Буду признателен за любую помощь
- Советы по поиску причин разницы в производительности
- Идея с теоретической точки зрения, например, есть ли какие-то серьезные различия в том, как WCF обрабатывает запрос?
- Любые инструменты, которые могут помочь профилировать сервер WCF
Заметки:
Проблема существует в производстве; на тестовых серверах все идет нормально. Сначала мы подозревали, что балансировщик нагрузки может быть фактором, но отключение балансировщика нагрузки не меняет производительность вообще
Замедление может быть связано с нашим уровнем приложений / домена, конечно. Возможно, какой-то поток / пул соединений блокируется, и сообщения попадают в очередь из-за этого.
В этом случае у кого-нибудь есть идея, почему поведение так отличается от WSE (который работает в том же пуле приложений)? Изменялись ли какие-либо размеры очереди / конфигурации по умолчанию для одновременной обработки между WSE3.0 и WCF?
Есть ли способ узнать, когда это происходит? Например, некоторые
perfmon
счетчики смотреть? Вperfmon
Я просто теряюсь, выбирая между огромным количеством доступных счетчиков производительности
Обновить
Вот анонимная версия нашего сервиса Web.config:
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<configSections>
<section name="microsoft.web.services2" type="Microsoft.Web.Services2.Configuration.WebServicesConfiguration, Microsoft.Web.Services2, Version=2.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" />
<section name="log4net" type="log4net.Config.Log4NetConfigurationSectionHandler, log4net" />
</configSections>
<system.web>
<httpRuntime executionTimeout="900" maxRequestLength="10240" />
<webServices>
<!--<wsdlHelpGenerator href="CustomizedWebServicePage.aspx" />-->
<protocols>
<add name="HttpGet" />
<add name="HttpPost" />
</protocols>
<soapExtensionTypes>
<add type="Microsoft.Web.Services2.WebServicesExtension, Microsoft.Web.Services2, Version=2.0.3.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" priority="1" group="0" />
</soapExtensionTypes>
</webServices>
<compilation defaultLanguage="cs" debug="true" targetFramework="4.5" />
<customErrors mode="RemoteOnly" />
<!-- dev only - application pool identity is configured on real environment -->
<identity impersonate="true" userName="ServiceIdentity" password="********" />
<authentication mode="Windows" />
<authorization>
<allow users="*" />
<!-- Allow all users -->
</authorization>
<trace enabled="false" requestLimit="10" pageOutput="false" traceMode="SortByTime" localOnly="true" />
<sessionState mode="InProc" cookieless="false" timeout="20" sqlConnectionString="data source=127.0.0.1;user id=someuserid;password=********;port=42424" />
<globalization requestEncoding="utf-8" responseEncoding="utf-8" />
<pages controlRenderingCompatibilityVersion="3.5" clientIDMode="AutoID" />
</system.web>
<microsoft.web.services2>
<diagnostics>
<detailedErrors enabled="true" />
</diagnostics>
<policy>
<cache name="policyCache.xml" />
</policy>
<security>
<timeToleranceInSeconds>43200</timeToleranceInSeconds>
<defaultTtlInSeconds>43200</defaultTtlInSeconds>
<x509 storeLocation="LocalMachine" verifyTrust="false" />
<securityTokenManager type="OurProduct.Business.Authentication.CustomUsernameTokenManager, OurProduct.Business, Version=5.0.2.11517, Culture=neutral" qname="wsse:UsernameToken" xmlns:wsse="http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd" />
</security>
<messaging>
<maxRequestLength>10240</maxRequestLength>
</messaging>
</microsoft.web.services2>
<startup>
<supportedRuntime version="v2.0.50727" />
</startup>
<system.serviceModel>
<diagnostics wmiProviderEnabled="true">
<messageLogging logMalformedMessages="true" logMessagesAtTransportLevel="true" />
</diagnostics>
<serviceHostingEnvironment aspNetCompatibilityEnabled="true" multipleSiteBindingsEnabled="true" />
<services>
<service behaviorConfiguration="OurServiceBehavior" name="OurProduct.Service.OurService">
<endpoint address="" binding="basicHttpBinding" bindingConfiguration="BasicHttpBinding_IXXXOurService" bindingNamespace="http://localhost/XXXOurService" contract="OurProduct.ServiceContracts.XXXOurService.IXXXOurService" />
</service>
</services>
<behaviors>
<serviceBehaviors>
<behavior name="OurServiceBehavior">
<dataContractSerializer maxItemsInObjectGraph="2147483647" />
<serviceMetadata httpGetEnabled="false" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="true" />
<serviceCredentials>
<userNameAuthentication userNamePasswordValidationMode="Custom" customUserNamePasswordValidatorType="OurProduct.Service.Validation.CustomUserNamePasswordValidator, OurProduct.Service" />
</serviceCredentials>
</behavior>
<behavior name="">
<serviceMetadata httpGetEnabled="true" httpsGetEnabled="true" />
<serviceDebug includeExceptionDetailInFaults="false" />
</behavior>
</serviceBehaviors>
</behaviors>
<bindings>
<basicHttpBinding>
<binding name="BasicHttpBinding_IXXXOurService" closeTimeout="00:01:00" openTimeout="00:01:00" receiveTimeout="00:15:00" sendTimeout="00:15:00" bypassProxyOnLocal="false" hostNameComparisonMode="StrongWildcard" maxBufferPoolSize="524288000" maxBufferSize="524288000" transferMode="Buffered" maxReceivedMessageSize="524288000" messageEncoding="Mtom" textEncoding="utf-8" useDefaultWebProxy="true" allowCookies="false">
<readerQuotas maxDepth="524288000" maxStringContentLength="524288000" maxArrayLength="524288000" maxBytesPerRead="524288000" maxNameTableCharCount="524288000" />
<security mode="TransportWithMessageCredential">
<transport clientCredentialType="None" />
<message clientCredentialType="UserName" />
</security>
</binding>
</basicHttpBinding>
</bindings>
</system.serviceModel>
<runtime>
<gcServer enabled="true" />
<gcConcurrent enabled="true" />
</runtime>
<system.webServer>
<security>
<requestFiltering>
<requestLimits maxAllowedContentLength="10485761" /> <!-- 10 megabytes -->
</requestFiltering>
</security>
</system.webServer>
</configuration>
4 ответа
В вашем файле конфигурации службы WCF явно не заданы значения регулирования. Возможно, вы захотите использовать монитор производительности, чтобы отслеживать ресурсы WCF и / или корректировать значения по умолчанию, чтобы убедиться, что вы не достигнете предела ограничения по умолчанию.
Сервисное регулирование (serviceThrottling
) позволяет выровнять нагрузку на внутренние серверы WCF и обеспечить распределение ресурсов. Поведение serviceThrottling для внутренних служб WCF настраивается путем изменения значений для maxConcurrentCalls
, maxConcurrentSessions
, а также maxConcurrentInstances
параметры в файле конфигурации для службы WCF.
<serviceThrottling
maxConcurrentCalls="200"
maxConcurrentSessions="200"
maxConcurrentInstances="200" />
https://msdn.microsoft.com/en-us/library/ee377061%28v=bts.70%29.aspx
Я бы предложил отладить это следующим образом:
временно удалите всю аутентификацию и логику безопасности из обоих сервисов и посмотрите, остается ли проблема
временно отключить любую бизнес-логику и, возможно, упростить схему до одной переменной
когда вы говорите, что производительность ниже, вы подразумеваете производительность одного пользователя или нагрузочный тест? когда вы проверяете одного пользователя, убедитесь, что сервер теплый?
Если вы рассчитываете время выполнения вашей логики (например, от начала до конца реализации вашего серверного метода) - то же самое?
не забудьте отменить любые записи / отслеживание во время бенчмаркинга
вы можете попытаться вернуть wcf для использования XmlSerializer вместо DataContract
Использование диагностики WCF - это здорово, но, насколько я знаю, вы не сможете получить аналогичную диагностику из веб-службы, поэтому вам не с чем будет сравнивать. Однако диагностика, которую вы готовите в своем ответе, даст вам представление об относительном времени, проведенном на каждом этапе вызова службы.
Я предложу альтернативу, которая должна быть очень простой, потому что вы используете http / text в обоих случаях. Просто поймайте оба ответа, используя Fiddler или ваш любимый прокси-инструмент, и сравните. И что важно - убедитесь, что вы смотрите на заголовок http, а не только на тело. Fiddler сообщит вам время прохождения туда-обратно и размер ответа, которого должно быть достаточно.
Что бы это могло быть? Очевидные вещи:
- Я испытал огромные потери производительности (да, примерно в 3 раза) при использовании аутентификации Windows с WCF. Я видел размер сообщения при использовании проверки подлинности Windows из-за большого зашифрованного большого двоичного объекта в заголовке (из памяти). Это стоит много времени только на передачу.
- Также в отношении безопасности шифруется ли запрос WCF? Если вы используете безопасность сообщений, то она будет упакована на стороне сервера и распакована на стороне клиента. Это тоже не бесплатно.
- Несколько сервисных экземпляров. Ваш сервис должен быть настроен на несколько экземпляров, что означает, что каждая операция создаст свой собственный экземпляр сервиса. Это поведение по умолчанию. Настраивается как атрибут самого класса обслуживания, например
[System.ServiceModel.ServiceBehavior(ConcurrencyMode = System.ServiceModel.ConcurrencyMode.Multiple)]
Вы правы в том, что для WCF существует много счетчиков производительности. Они сгруппированы по сервису, конечной точке и операции. Возможно, вам нужны счетчики услуг, так как они содержат больше информации. Проверить ServiceModelService 4.0
категория, и посмотрите на
- Звонки (очевидно)
- Звонков в секунду
- Инстансы
- Экземпляры, созданные в секунду
Извините за ответ в ответе, у меня недостаточно репутации для комментариев. Какие конкретные данные (следы) вы хотели бы видеть? Если у вас возникли проблемы с настройкой трассировки, я бы порекомендовал вам использовать инструмент с именем SvcConfigEditor.exe. В нем вы можете открыть файл App.Config вашей службы WCF и в разделе "Диагностика" включить трассировку. После этого вы можете выбрать, хотите ли вы отслеживать конкретную информацию - так называемый "Уровень трассировки" (дополнительная информация о конкретных уровнях - Настройка трассировки). Смотрите скриншот инструмента:
После того, как вы отследите требуемую информацию, вы можете открыть журнал в Microsoft Trace Viewer - в нем вы можете просмотреть длительность каждой активности: например, рассмотрите эту (извините, некоторые ярлыки на чешском языке):
Извините, картинка не читается, вот ссылка на картинку большего размера: средство просмотра трассировки
Слева вы можете выбрать конкретное занятие - если вы растянете панель, вы даже увидите время начала и окончания. Кроме того, вы видите общую продолжительность этой деятельности. После того, как вы выбрали его, в верхней левой панели вы можете увидеть все вызовы, которые относятся к этому действию, и вы также можете увидеть, какой вызов занял больше всего времени (в столбце "Время").