Friday, May 25, 2012

WCF - Throttling


Throttling

WCF throttling provides some properties that user can use to limit how many instances or sessions are created at the application level.
Performance of the WCF service can be improved by creating proper instance
maxConcurrentCalls  : It limits the number of concurrent requests or calls that can be processed by WCF service instances,default value is 16.
maxConcurrentInstances :It limits the number of service instances that can be allocated at a given time  or processed by service host.Default value =32
maxConcurrentSessions : It limits the number of active sessions allowed for the service. Default Value=10

WebConfig Example :

<system.serviceModel>
    <services>
      <service behaviorConfiguration="DuplexWcfService.Service1Behavior" name="CallBackOperation.DuplexCallBack">
        <endpoint address="http://localhost:62345/DuplexCallBack.svc" binding="wsDualHttpBinding" contract="CallBackOperation.IDuplexServiceContract">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="DuplexWcfService.Service1Behavior">
           <serviceMetadata httpGetEnabled="true" />
            <serviceDebug includeExceptionDetailInFaults="true" />

<serviceThrottling maxConcurrentCalls="500" maxConcurrentInstances ="100" maxConcurrentSessions ="200"/>

        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>


Programically :
class Program
    {
        static void Main(string[] args)
        {
            ServiceHost myService = new ServiceHost(typeof(WCFCallbacks.MessageService));

                          
            ServiceThrottlingBehavior throttleBehavior = new ServiceThrottlingBehavior
            {
                MaxConcurrentCalls = 40,
                MaxConcurrentInstances = 20,
                MaxConcurrentSessions = 20,
            };
            myService.Description.Behaviors.Add(throttleBehavior);
            myService.Open();
            Console.WriteLine("Press enter to stop.");         
            myService.Close();


            myService.Open();
            Console.WriteLine("Press enter to stop.");
             myService.Close();
        }
    }




Concurrency Mode


Concurrency issues arise when multiple threads attempt to access the same resources at run time.
if multiple clients call the same service, multiple concurrent request threads can arrive for a service.
Service object handling each request is based on the instancing mode for the service.
PerCall services : A new service object is granted for each request.
 PerSession : One service object receives requests from the same client (or, proxy).
Single instancing mode : All client requests are sent to the same singleton service object.
There are three ways by which you can handle concurrency in WCF:

Single: Only one request will be processed at any given moment of time. The other requests have to wait until the request processed by the WCF service is completed. By default, services are configured for Single concurrency mode.
This means that a lock is acquired for the service object while a request is being processed by that object. Other calls to the same object are queued. When the request that owns the lock has completed, and thus released the lock, the next request in the queue can acquire the lock and begin processing. This configuration reduces the potential throughput at the service, when sessions or singletons are involved, but it also yields the least risk for concurrency issues.
Eg :
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Single)]
Configuring services for Single access doesn’t impact PerCall services because a new service instance is allocated for each request.

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession ,ConcurrencyMode=ConcurrencyMode.Single)]
  In  Single instancing mode, only one request can be processed across all clients so it doesn’t  throughput of multiple clients.

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single ,ConcurrencyMode=ConcurrencyMode.Single)]
In this combination, only one instance of a WCF service instance is created which serves all requests which are sent from  clients.
Multiple: In this scenario, multiple requests can be handled by the WCF service object at any given moment of time
 [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall,ConcurrencyMode=ConcurrencyMode.Multiple)]
 In this combination singlt threads for all clients requests

 [ServiceBehavior(InstanceContextMode =InstanceContextMode.PerSession,ConcurrencyMode=ConcurrencyMode.Multiple)]
  In this combination Multiple threads for every request by same client or proxy

 [ServiceBehavior(InstanceContextMode = InstanceContextMode.Single,ConcurrencyMode=ConcurrencyMode.Multiple)]
  In this combination Multiple threads for all clients requests handle by single service instance

Reentrant: A single request thread has access to the WCF service object, but the thread can exit the WCF service to call another WCF service or can also call a WCF client through callback and reenter without deadlock.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Reentrant)]
Single threads for all clients, locks are released when calls diverted to other WCF services
[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession, ConcurrencyMode = ConcurrencyMode.Reentrant)]
Single threads for all clients, locks are released when calls diverted to other WCF services.
[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode = ConcurrencyMode.Reentrant)]
Single threads for all clients, locks are released when calls diverted to other WCF services.


WCF-Callback Contract

Callback Contract :

Callback operations are part of the service contract  and   service contract can have at most one callback contract
Once callback contract defined, the clients are required to support the callback and provide the callback endpoint to the service in every call.
To define a callback contract, the ServiceContract attribute offers the CallbackContract property



[AttributeUsage(AttributeTargets.Interface | AttributeTargets.Class)]
        public sealed class ServiceContractAttribute : Attribute
        {
            public Type CallbackContract
            { get; set; }
            //More members
        }

Example :
       interface IMyFirstContractCallback
        {
            [OperationContract]
            void OnCallback();
        }

        [ServiceContract(CallbackContract = typeof(IMyFirstContractCallback))]
        interface IMyFirstInterface
        {
            [OperationContract]
            void AnyMethod();
        }

Note :
 Callback contract need not be defined with a ServiceContract  attribute  and it will be included in the service metadata.  
 All methods in callback interface defined with with the OperationContract attribute.
Once the client imports the metadata of the callback contract, the imported callback interface will not have the same name as in the original service-side definition. It will become interface suffixed with the word Callback.

Example :
Interface :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;

namespace CallBackOperation
{

    //Interface IDuplexServiceContract which is our service contract
    //In service Contract need to make sure WCF maintains session information for the duration of a      client's connection.
    //Secondly, we need to specify that which interface will be handling the callback contract eg :IDuplexCallback.
    [ServiceContract(SessionMode = SessionMode.Required, CallbackContract = typeof(IDuplexCallback))]
    public interface IDuplexServiceContract
    {
        [OperationContract(IsOneWay = true)]
        void Stringmanagement(string value);

        [OperationContract]
         List<string> GetData();
    }


    //Callback contract which client will call, creating a new interface IDuplexCallback
    //This interface has two Call Back methods ,In order that it should be asynchronous,
    //we have defined the one-way attribute on the doHugeTask method.
   
    public interface IDuplexCallback
    {
        [OperationContract(IsOneWay = true)]
        void CallBacktestingFunction(string requestString);

        [OperationContract(IsOneWay = true)]
        void LodingDataStatus(string[] data);
    }
}


Service :
using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.ServiceModel;
using System.Text;
using System.Text;
using System.Threading;
using System.Data.SqlClient;
using System.Data;

namespace CallBackOperation
{
    //InstanceContextMode is PerSession because the use of callback contracts requires a session
    [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerSession)]
    public class DuplexCallBack : IDuplexServiceContract
    {
       
        SqlConnection Conn;    
        IDuplexCallback _callback;
        string _requestString = String.Empty;

        public DuplexCallBack()
        {
            //The OperationContext.Current.GetCallBackChannel will be used to make callback to the client.
            //When the service receives a call, OperationContext.Current.GetCallbackChannel returns a channel to just that caller.
            //It does not return a channel that broadcasts to all of the clients.
            _callback = OperationContext.Current.GetCallbackChannel<IDuplexCallback>();
            Conn = new SqlConnection("Connection String");
     
        }


        public void Stringmanagement(string value)
        {          
            _requestString = string.Format("Hey, Welcome {0} , So finally you did it.", value);    
            // CallBack Function
            _callback.CallBacktestingFunction(_requestString);
        }



        List<string> IDuplexServiceContract.GetData()
        {
            List<string> lstFunds = new List<string>();
            Conn.Open();
            SqlCommand Cmd = new SqlCommand("Select top 10 FundName from tblfund order by FundID", Conn);
            SqlDataReader Reader = Cmd.ExecuteReader();
            while (Reader.Read())
            {
                lstFunds.Add(Reader["FundName"].ToString());
            }
            Reader.Close();
            Conn.Close();
            string[] returndataArray = lstFunds.ToArray();
            // CallBack Function
            _callback.LodingDataStatus(returndataArray);
            return lstFunds;
        }
    }
}



WebConfig :

<system.webServer>
    <validation validateIntegratedModeConfiguration="false" />
    <modules>
      <add name="ScriptModule" preCondition="integratedMode" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </modules>
    <handlers>
      <remove name="WebServiceHandlerFactory-Integrated" />
      <add name="ScriptHandlerFactory" verb="*" path="*.asmx" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add name="ScriptHandlerFactoryAppServices" verb="*" path="*_AppService.axd" preCondition="integratedMode" type="System.Web.Script.Services.ScriptHandlerFactory, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
      <add name="ScriptResource" preCondition="integratedMode" verb="GET,HEAD" path="ScriptResource.axd" type="System.Web.Handlers.ScriptResourceHandler, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35" />
    </handlers>
    <directoryBrowse enabled="true" />
  </system.webServer>
  <system.serviceModel>
    <services>
      <service behaviorConfiguration="DuplexWcfService.Service1Behavior" name="CallBackOperation.DuplexCallBack">
        <endpoint address="http://localhost:62345/DuplexCallBack.svc" binding="wsDualHttpBinding" contract="CallBackOperation.IDuplexServiceContract">
        </endpoint>
      </service>
    </services>
    <behaviors>
      <serviceBehaviors>
        <behavior name="DuplexWcfService.Service1Behavior">
          <!-- To avoid disclosing metadata information, set the value below to false and remove the metadata endpoint above before deployment -->
          <serviceMetadata httpGetEnabled="true" />
          <!-- To receive exception details in faults for debugging purposes, set the value below to true.  Set to false before deployment to avoid disclosing exception information -->
          <serviceDebug includeExceptionDetailInFaults="true" />
        </behavior>
      </serviceBehaviors>
    </behaviors>
  </system.serviceModel>



Add Service reference :



 

 




Client WebConfig :
  
<?xml version="1.0" encoding="utf-8" ?>
<configuration>
    <system.serviceModel>
        <bindings>
            <wsDualHttpBinding>
                <binding name="WSDualHttpBinding_IDuplexServiceContract" 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">
                    <readerQuotas maxDepth="32" maxStringContentLength="8192" maxArrayLength="16384"
                        maxBytesPerRead="4096" maxNameTableCharCount="16384" />
                    <reliableSession ordered="true" inactivityTimeout="00:10:00" />
                    <security mode="Message">
                        <message clientCredentialType="Windows" negotiateServiceCredential="true"
                            algorithmSuite="Default" />
                    </security>
                </binding>
            </wsDualHttpBinding>
        </bindings>
        <client>
            <endpoint address="http://localhost:62345/DuplexCallBack.svc"
                binding="wsDualHttpBinding" bindingConfiguration="WSDualHttpBinding_IDuplexServiceContract"
                contract="ServiceReference1.IDuplexServiceContract" name="WSDualHttpBinding_IDuplexServiceContract">
                <identity>
                    <userPrincipalName value="vikas.kumar@Vam.Local" />
                </identity>
            </endpoint>
        </client>
    </system.serviceModel>
</configuration>



Client Code :



using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.ServiceModel;
using Console_CallBack_Client.ServiceReference1;
namespace Console_CallBack_Client
{

    //Following methods are callback methods, which the server will call after service methods completed eg :  Stringmanagement() and GetData.
    class DuplexCallbackHandler : IDuplexServiceContractCallback
    {
        public void CallBacktestingFunction(string value)
        {
            Console.WriteLine("\n");
            Console.WriteLine("\t Callback returned from the server");
            Console.WriteLine("\n");
            Console.WriteLine(value);
            Console.WriteLine("\n");
          
        }

        public void LodingDataStatus(string[] data)
        {
            Console.WriteLine("\n");
            Console.WriteLine(string.Format("Total Length of array  : {0}", data.Length.ToString()));
            Console.WriteLine("\n");
        }

    }
     
   
    class Program
    {
        static void Main(string[] args)
        {

            //here I create object of the Instance Context class.Instance Context represents context information of a service.
            //The main use of Instance Context is to handle incoming messages.
            //In short, service reference or proxy is used to send messages to server and Instance Context is used to accept incoming messages.

              InstanceContext instanceContext = new InstanceContext(new DuplexCallbackHandler());
            //In this step we pass the Instance Context object to the constructor of service reference or proxy.
            //server will use the same channel to communicate to the client.

              DuplexServiceContractClient client = new DuplexServiceContractClient(instanceContext);
               client.Stringmanagement("Guugeeaa");               
                 string[] listvalues;
              
                 listvalues = client.GetData();

                 for (int i = 0; i <= listvalues.Length - 1; i++)
                 {
                     Console.WriteLine(listvalues[i].ToString());
                 }
                 Console.WriteLine("\n");
                 Console.ReadLine();

        }
    }
}








 OutPut  :