Search code examples
wcfperfmon

WCF Message Security - PerCall


We have a website load balanced across two servers that calls into a WCF wsHttp service hosted on a single IIS7 application server.

Last week, the website was launched and we hit performance problems.

The system was built by an off-shore team but I was asked to investigate if I could help.

I loaded perfmon and used the asp.net counters to view current sessions. I could see that once this increased above about 25 then the website slowed greatly. It would continue to increase to approx 250 over the course of the following 10 minutes, it would then drop to 0 and performance of the site would be great.

This continued in a cycle - bad news!

The following day, the off-shore team informed me that they'd fixed the problem by tuning off security.

I have a theory that in disabling the security on the wsHttp binding WCF changed from creating an instance per session to a creating an instance per call - therefore allowing a far greater throughput of service requests. Is this a good theory?

I've built a simple model to test this, a couple of methods hosted in IIS and a simple client that generates multiple requests. This does seem to give the results I'd expected. Problem is, I'm struggling to find the correct perfmon counters to prove that fewer requests are queued and more concurrent instances created, when the secure binding is not used.

Could anyone please advise on the best perfmon counters to use?

OK, another day on this and a more questions!

In my test app, I now have 3 service classes with 3 different wsHttp bindings

  1. No security
  2. Message Security
  3. Message security but with [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)] set on the class

From within a loop of 40 on the client I start a new thread and call the service. When calling service 1, the service completes all requests within 1 second.

When calling service 2, the service completes all requests in 33 seconds.

When calling service 3, I'd expect it to be almost as quick as service 1 since I'd expect the service to instantiate a new service object for each of the 4 calls. However, it doesn't seem (I still don't have any meaningful perfmon counters!) to do this and total time to complete is also 33 seconds.

Here is the config from the service:

<?xml version="1.0"?>
<configuration>
  <system.diagnostics>
    <sources>
      <source name="System.ServiceModel"
              switchValue="Information, ActivityTracing"
              propagateActivity="true" >
        <listeners>
          <add name="traceListener"
              type="System.Diagnostics.XmlWriterTraceListener"
              initializeData="c:\WCFTrace\InstancingDemo.svclog" />
        </listeners>
      </source>
    </sources>
  </system.diagnostics>
  <system.serviceModel>
    <behaviors>
      <serviceBehaviors>
        <behavior name="SecPerCallBehaviour">
          <serviceThrottling maxConcurrentSessions="1000"
                              maxConcurrentCalls="30"
                              maxConcurrentInstances="30"/>
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
        <behavior name="">
          <serviceMetadata httpGetEnabled="true" />
          <serviceDebug includeExceptionDetailInFaults="false" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
    <serviceHostingEnvironment multipleSiteBindingsEnabled="true" />
    <bindings>
      <wsHttpBinding>
        <binding name="BindingNoSec">
          <security mode="None" />
        </binding>
        <binding name="BindingMessageSec">
          <security mode="Message">
            <message establishSecurityContext ="true"/>
          </security>
        </binding>
        <binding name="BindingMessageSecPerCall" >
          <security mode="Message">
            <message establishSecurityContext ="true"/>
          </security>
        </binding>
      </wsHttpBinding>
    </bindings>
    <services>
      <service name="ServiceInstancingDemo.Service1">
        <endpoint address="~/Service1.svc"
          binding="wsHttpBinding" bindingConfiguration="BindingNoSec"
          name="NoSecurity" contract="ServiceInstancingDemo.IService1" />
      </service>
      <service name="ServiceInstancingDemo.Service2">
        <endpoint address="~/Service2.svc"
          binding="wsHttpBinding" bindingConfiguration="BindingMessageSec"
          contract="ServiceInstancingDemo.IService2" />
      </service>
      <service name="ServiceInstancingDemo.Service3" behaviorConfiguration="SecPerCallBehaviour">
        <endpoint address="~/Service3.svc"
          binding="wsHttpBinding" bindingConfiguration="BindingMessageSecPerCall"
          contract="ServiceInstancingDemo.IService3" />
      </service>
    </services>
  </system.serviceModel>
</configuration>

And here's the config from the client:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsHttpBinding>
                <binding name="WSHttpBinding_IService2" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                            algorithmSuite="Default" establishSecurityContext="true" />
                    </security>
                </binding>
                <binding name="NoSecurity" closeTimeout="00:01:00" openTimeout="00:01:00"
                    receiveTimeout="00:10:00" sendTimeout="00:01:00" bypassProxyOnLocal="false"
                    transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="None">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true" />
                    </security>
                </binding>
                <binding name="WSHttpBinding_IService3" closeTimeout="00:01:00"
                    openTimeout="00:01:00" receiveTimeout="00:10:00" sendTimeout="00:01:00"
                    bypassProxyOnLocal="false" transactionFlow="false" hostNameComparisonMode="StrongWildcard"
                    maxBufferPoolSize="524288" maxReceivedMessageSize="65536"
                    messageEncoding="Text" textEncoding="utf-8" useDefaultWebProxy="true"
                    allowCookies="false">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00"
                        enabled="false" />
                    <security mode="Message">
                        <transport clientCredentialType="Windows" proxyCredentialType="None"
                            realm="" />
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                            algorithmSuite="Default" />
                    </security>
                </binding>
            </wsHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://rb-t510/NGCInstancing/Service2.svc/~/Service2.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService2"
                contract="NGCWithSec.IService2" name="WSHttpBinding_IService2">
                <identity>
                    <servicePrincipalName value="host/RB-T510" />
                </identity>
            </endpoint>
            <endpoint address="http://rb-t510/NGCInstancing/Service1.svc/~/Service1.svc"
                binding="wsHttpBinding" bindingConfiguration="NoSecurity"
                contract="NGC.IService1" name="NoSecurity" />
            <endpoint address="http://localhost/NGCInstancing/Service3.svc/~/Service3.svc"
                binding="wsHttpBinding" bindingConfiguration="WSHttpBinding_IService3"
                contract="NGCSecPerCall.IService3" name="WSHttpBinding_IService3">
                <identity>
                    <servicePrincipalName value="host/RB-T510" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>

I guess that I'm missing a config setting? Or maybe mutiple calls using message security over wsHttp are always going to be very slow because the server object must be instantiated per session and only a single session is created for each client?

Many thanks

Rob.


Solution

  • The counters you want have to be explicitly enabled in your service:

    <configuration>
        <system.serviceModel>
            <diagnostics performanceCounters="All" />
        </system.serviceModel>
    </configuration>
    

    Obviously it can be more granular too. This is what you want to read: WCF Performance Counters

    Update: A better link: How to use performance counters to diagnose performance of WCF applications