Showing posts with label WCF. Show all posts
Showing posts with label WCF. Show all posts

Saturday, August 16, 2014

WCF - Duplex Named Pipe Binding

Today topic cover about how to create a WCF service which uses named pipe communication channel but also support duplex communication. In a normal web service behavior, client sends request message to the server, the server process the request then respond the message back to the client, therefore, it always need to have a requester to call the web service. What if you need your service to automatically send response to the client, but the client does not require to send request as always? Duplex communication allow server to interact with the client.

For today blog post, I have created a simple Hello World WCF service that support duplex communication and uses NetNamedPipeBinding and then the service is hosted in IIS with WAS (Windows Process Activation Service). This Hello World service allows clients to subscribe itself to the service to allow the service spam them with "Hello World!" words.

First, let's create the service first, defining the service contracts and operation contracts. In order to create a duplex communication service, we need to create a duplex contract first. This duplex contract will be used by the client to implement the service callback method, which also mean the service will be firing this callback method to send response to the client. 

Server Side

For my case, my service is going to spam "Hello World!" string to the client, so I define my duplex contract as follow:

public interface IHelloWorldServiceCallback
{
    [OperationContract(IsOneWay=true)]
    void Spam(string message);
}

Note that I have set IsOneWay as true for the named parameter in my operation contract. It is because after the client subscribed itself to the server, server automatically send message to the client, server does not expect a reply from the client.

Then, define the normal service contract as follow:

[ServiceContract(
    SessionMode=SessionMode.Required,
    CallbackContract=typeof(IHelloWorldServiceCallback))]
public interface IHelloWorldService
{
    [OperationContract]
    void SpamMe();

    [OperationContract]
    void StopSpam();
}

Note that I have specified the Session Mode named parameter with SessionMode.Required. It is required when creating a duplex communication service because the server need to keep track the client session, and then during firing the callback method, the service know the response message should be sent to which correct client.

The following is the implementation of my Hello World service contract:

[ServiceBehavior(InstanceContextMode=InstanceContextMode.PerSession)]
public class HelloWorldService : IHelloWorldService
{
    private static Dictionary<string, CancellationTokenSource> _sessionCTSs;
    static HelloWorldService()
    {
        //Create a single dictionary to keep all cancellation token source for tasks
        _sessionCTSs = new Dictionary<string, CancellationTokenSource>();
    }

    public void SpamMe()
    {
        //Client register a callback channel session for itself to the server
        var callback = OperationContext.Current.GetCallbackChannel<IHelloWorldServiceCallback>();
           
        //Get the client session Id
        var sessionId = OperationContext.Current.SessionId;

        //Server spawn a new thread that process and invoke the callback method
        var spamTaskCTS = new CancellationTokenSource();
        Task.Run(() =>
        {
            for (int i = 0; i < 100; i++ )
            {
                if (spamTaskCTS.IsCancellationRequested)
                {
                    callback.Spam("Client request stop spam!");
                    break;
                }

                callback.Spam("Hello World!");
                Thread.Sleep(1000);
            }
        }, spamTaskCTS.Token);

        _sessionCTSs.Add(sessionId, spamTaskCTS);
    }

    public void StopSpam()
    {
        //Cancel the existing thread base on Session Id
        var sessionId = OperationContext.Current.SessionId;
        var spamTaskCTS = _sessionCTSs[sessionId];
        spamTaskCTS.Cancel();
    }
}

The SpamMe() operation contract is to allow client register itself to the server that it want to subscribe the spam, asking the server to send message to it automatically. When the client invokes the SpamMe() operation, the client session ID will be kept in a dictionary. Then, a new thread will be created, the new thread will do the processing and invoke the callback method. The code above is actually sending "Hello World!" message to the client every one second for 100 times.

The StopSpam() operation contract is to allow client to request server to stop sending message to it. The method will get the client session ID from the same dictionary, and then cancel the sending message task. Once the task is cancelled, the service will not invoke the callback method anymore.

Host

Now, host the above service in IIS. The following is how my configuration file look like:

<system.serviceModel>
  <serviceHostingEnvironment multipleSiteBindingsEnabled="true">
    <serviceActivations>
      <add relativeAddress="./HelloWorldService.svc"
            service="NamedPipeDuplexSample.Services.HelloWorldService"
            factory="System.ServiceModel.Activation.ServiceHostFactory" />
    </serviceActivations>
  </serviceHostingEnvironment>
  <services>
    <service name="NamedPipeDuplexSample.Services.HelloWorldService"
              behaviorConfiguration="">
      <endpoint address="net.pipe://localhost/NamedPipeDuplexServer/HelloWorldService.svc"
                binding="netNamedPipeBinding"
                contract="NamedPipeDuplexSample.Services.Contracts.IHelloWorldService">
      </endpoint>
      <endpoint address="mex" binding="mexHttpBinding" contract="IMetadataExchange"></endpoint>
    </service>
  </services>
  <behaviors>
    <serviceBehaviors>
      <behavior>
        <!-- To avoid disclosing metadata information, set the values below to false before deployment -->
        <serviceMetadata httpGetEnabled="true" httpsGetEnabled="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>

The following are the steps to configure IIS to enable named pipe protocol. Before that, you need to ensure you have install WAS (Windows Process Activation Service) in your server first.

At the website level, right click and open the Edit Bindings menu, make sure you have net.pipe binding enabled as you see in the screenshot below.


Setup and host your service at the website that you had configured just now.

At your application level, right click then go to Manage Application, open Advanced Settings menu. Enter net.pipe to enable the named pipe protocol in your application as you see in the screenshot below:



Done.

Client Side

For the client side, I have created a simple console application to test my duplex WCF service. We need to implement the callback interface which we had defined in the server side code above. 

public class HelloWorldServiceCallback : IHelloWorldServiceCallback
{
    public void Spam(string message)
    {
        Console.WriteLine(message);
    }
}

So, when the server invoke the callback method, this method at client side will be fired with the message from the server.

The following is the code of how to connect to the WCF service hosted in IIS with named pipe channel:

class Program
{
    static void Main(string[] args)
    {
        var address = new EndpointAddress("net.pipe://localhost/NamedPipeDuplexServer/HelloWorldService.svc");
        var binding = new NetNamedPipeBinding();
        var callback = new HelloWorldServiceCallback();
        var context = new InstanceContext(callback);
        var factory = new DuplexChannelFactory<IHelloWorldService>(context, binding, address);
        var service = factory.CreateChannel();

        service.SpamMe();
        Console.ReadKey();

        service.StopSpam();
        Console.ReadKey();
    }
}

Once done, simply run the console program, then you will get the result as follow:


You even can run multiple console program to test the WCF session. Simply hit one key in one of the console program, only the correct one console will stop, the rest are remain untouched.



If you are interested with my source code, feel free to get it from HERE.




Saturday, August 9, 2014

WCF - Limit Transaction Per Second With Semaphore

Continue more from my previous post about Semaphore, today I want to share about how to use semaphore to limit the request to be processed by WCF. If you had read my previous post, I used lock and count check to limit the request process, today you will find the similar functionality but using different method to do so.

In my previous way, I have to set the WCF concurrency mode to be single in order to have only one thread to control all the incoming requests like this:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.Single, ConcurrencyMode =ConcurrencyMode.Single)]

Hence, the requests are piling up in the IIS request queue, which the limit by default is 1000. When there are a lot of requests come in at the same time, potentially the queue will be full very quickly.

Now, I change the method to use Semaphore to handle the threads for me, and I can set the concurrency mode to multiple. You may refer to the original method code from HERE. The following are the changed code:

[ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall, ConcurrencyMode = ConcurrencyMode.Multiple)]

public class SampleService : ISampleService
{
    private static SemaphoreSlim semaphore;
    private static Stopwatch watch;

    static SampleService()
    {
        //Set maximum tps as 3
        semaphore = new SemaphoreSlim(0, 3);
        watch = new Stopwatch();
        watch.Start();

        Task timerTask = new Task(HandleTransactionLimit());
        timerTask.Start();
    }

    private static Action HandleTransactionLimit()
    {
        return async () =>
        {
            int counter = 1;
            Task sleep = Task.Delay(1000); //create a async sleep task to sleep for 1 second
               
            while (true)
            {
                try
                {
                    if (counter <= 3)
                    {
                        //release a thread if within the limit
                        semaphore.Release();
                        counter++;
                    }
                    else
                    {
                        //otherwise, do nothing and wait for the sleep task complete the 1 second sleep
                        counter = 1;
                        await sleep;
                    }

                    //when the 1 second sleep is completed, create a new sleep task again
                    if (sleep.IsCompleted)
                        sleep = Task.Delay(1000);
                }
                catch(Exception ex)
                {
                    Debug.WriteLine(ex);
                }
            }

        };
    }

    public string GetData(string value)
    {
        semaphore.Wait();

        //Do your process here

        return string.Format("You entered: {0}", value);
    }


}

What the above code do is actually when a user call the service, the code will be blocked by semaphore first at the semaphore.Wait() method. And then, there is another separate long running thread which was initialized from the static constructor, the TimerTask, its job is to do the request count check and also being a timer. When a request comes in within the 1 second window and also within the request limit, the code execution will be released by the semaphore. Otherwise, it will do nothing and wait for the next second.

The following is my program used to do testing:

class Program
{
    static void Main(string[] args)
    {
        //Parallelly or concurrently hitting my service
        //to test whether the service process more than 3 transactions per second
        Parallel.For(0, 100, i =>
        {
            SampleServiceClient proxy = new SampleServiceClient();
            Console.WriteLine(string.Format(
                "{0} Service call {1} : {2}",
                DateTime.Now.ToString("yyyy-MM-dd hh:mm:ss.fff"),
                i,
                proxy.GetData("Test")));
        });

        Console.WriteLine("Press any key to continue...");
        Console.ReadKey();
    }

}

The test result is still similar to the previous post. Many requests come in, each request is processed in different thread, the code execution in every new thread is blocked and only allow 3 requests are being processed in every one second. There is one concern about semaphore, it does not guarantee FIFO (first in first out), the request that come in earlier may not being processed first. Unless, you implement a queue to handle it. So, do you find by using semaphore, it can actually achieve the same thing with lesser code?

If you are interested with my source code, feel free to download it from HERE.




Thursday, March 13, 2014

Fault Contract for Workflow Service

Today I want to share how to define fault contract and throw fault exception from workflow service and how to handle it from the client.

Concept

A little bit of story why should we use fault contract. By default in WCF service, when we throw any exception from the service, WCF automatically know how to handle the exception for you after you have set the <serviceDebug includeExceptionDetailInFaults="true"/> in your service behavior. However, when we want to host the WCF service to the production environment and especially the service is going to be exposed to the public, I am sure you would not like to let people see the exception detail. Therefore, the best practice is to set includeExceptionDetailInFaults="false", and then limit the information expose to the public by using fault contract.

Implementation

I have created another simple leave application sample with workflow. All the code in this article comes from my sample application. You can download the sample code at the very bottom of this article.

First, we need to define the fault object first. This object is used to store the information that you want to share and it is going to be used by the client and send through the network, therefore, make sure it is serializable.

[DataContract]
public class InvalidApplicationDateFault
{
    [DataMember]
    public int ErrorCode { get; set; }

    [DataMember]
    public string Message { get; set; }

    public InvalidApplicationDateFault(int errorCode, string message)
    {
        this.ErrorCode = errorCode;
        this.Message = message;
    }

}

This is my business component code, I am throwing a custom exception. It is optional, it is still okay if you throw an application exception.

public void Apply(Leave leave)
{
    leave.Status = 1; //Apply state
           
    if (leave.StartDate < DateTime.Today)
        throw new InvalidApplicationDateException();

    if (leave.EndDate < leave.StartDate)
        throw new InvalidApplicationDateException();

    var leaveDAC = new LeaveDAC();
    leaveDAC.Create(leave);

}

Then, following is my code activity which call my business component.

public class Apply : CodeActivity
{
    public InArgument<Leave> Leave { get; set; }

    protected override void Execute(CodeActivityContext context)
    {
        Leave leave = context.GetValue(this.Leave);

        LeaveComponent bc = new LeaveComponent();
        bc.Apply(leave);
    }

}

Then, from my workflow, I will try to call my code activity.



In the TryCatch activity, after catching my custom exception, I create my fault object, stuffing the information that I want to share with my client into it. And then, create a new fault exception with fault object type specific (FaultException<InvalidApplicationDateFault>), then assign the fault object into it.

Notice that I have 2 SendReplyToReceive activities? The SendReplyToReceive inside the Catch is sending the fault exception to the client. But, before that, you need to duplicate the SendReplyToReceive activity from the same Receive activity by right clicking the Receive, then select Create SendReply.



Drag the new SendReplyToReceive activity into the catch, then open the Content Definition window. After that, set a parameter with the FaultException type and then assign the fault exception which I had created.



Now, if you have done it correctly, you will see Workflow Service automatically generate a fault contract for you as you can see it in the WSDL.

Note: If you do not see your fault message generated in WSDL, it is because you are not sending FaultException<T> type in SendReplyToReceive activity, and that your client will never be able to catch the correct fault exception.


Testing

Now, I want to test the service whether it is actually throwing the fault exception to my client correctly. This is how my unit test look like:

[TestMethod, ExpectedException(typeof(FaultException<InvalidApplicationDateFault>))]
public void ApplyTest()
{
    LeaveServiceClient proxy = new LeaveServiceClient();

    try
    {
        Leave leave = new Leave();
        leave.Employee = "Tweety Bird";
               
        //Apply leave with past date is not allowed.
        //This would trigger error
        leave.StartDate = new DateTime(2013, 1, 1);
        leave.EndDate = new DateTime(2013, 1, 2);

        leave.Duration = 2;
        leave.DateSubmitted = DateTime.Now;
        leave.CorrelationID = Guid.NewGuid();

        proxy.Apply(leave);
    }
    catch(FaultException<InvalidApplicationDateFault> fex)
    {
        Assert.IsTrue(fex.Detail.ErrorCode == 123);
        throw;
    }
    catch(Exception)
    {
        Debug.WriteLine("Failed to catch FaultException.");
        Assert.Fail();
    }
    finally
    {
        proxy.Close();
    }
}

When the code execution reach the Apply method, the FaultException<InvalidApplicationDateFault> had been thrown from the service and I am able to catch it correctly in my client.

So, when your service is ready to be exposed to public, you can set the following in your service behavior.

<serviceMetadata httpGetEnabled="false"/>
<
serviceDebug includeExceptionDetailInFaults="false"/>

Happy coding!

If you are interested with my source code, feel free to download it from HERE.




Send Transactional SMS with API

This post cover how to send transactional SMS using the Alibaba Cloud Short Message Service API. Transactional SMS usually come with One Tim...