Showing posts with label service. Show all posts
Showing posts with label service. Show all posts

Monday, 9 March 2015

Azure API Management - APIM, consuming a SOAP WCF service over HTTP


Update 26/March/20 - In the intervening years Azure APIM has come a long way - it's now quite common to see customers of APIM facading SOAP webservices - using both Liquid and XSLT to transform legacy webservices to a more modern RESTful experience. 
Please do checkout the Azure APIM docs.

***

At the time of writing:

I've been looking at the excellent API-Management feature of Azure .. I understand this functionality has come about through an acquisition (Apiphany). 

I think Azure APIM could offer some great benefits. 

After becoming familiar with the product through the documentation I was still a little confused about how or in fact whether a WCF-SOAP service was supported as the back-end service. The APIM documentation focuses very much on a REST based example back-end service.

(Note: I assume the readers familiarity with APIM and a previous walk through of the documented examples).

To add to my confusion one of the feedback comments for the APIM product appeared to suggest SOAP was not in fact YET supported: feedback-for-api-management-suggestions 

However logic suggests it should work, a SOAP envelope submission over HTTP is via a POST, there are really only three prerequisites 

1. Valid SOAP envelope XML which contains an XML body which the target Web service will be able to de-serialize. 
2. A matching SOAPAction request header
3. A Content-Type header of "text/xml"

One further consideration is how the consumer obtains the contract for the service. It's an implementation detail, but it's possible to define an http and/or https endpoint from which the consumer of the service can acquire the service definition in the form of a WSDL. From .net v4.5 onward a "singleWSDL" option is supported which I believe to be generally preferable and better for interop.

There were a couple of goals:

1. Firstly I wanted to look at options around surfacing some existing internally facing WCF implemented basic-http (SOAP) services for the future

2. I wanted policy and the end user experience to centre around both endpoint and operation.

In my test I published a simple test WCF service to a public facing server, I didn't add security to this service - wishing to keep it simple initially - but the service was restricted to TLS from the front end load balancer out. So any test would be over https.

The URL for my test back-end service was something like this

https://ourdomain.com/helloworld/service1.svc?singlewsdl

The service had two methods:

[ServiceContract]
public interface IService1
{
    [OperationContract]
    string HelloWorldOperation1(string value1, int value2, bool value3);
 
    [OperationContract]
    CompositeType HelloWorldOperation2(CompositeType composite);
}

(Composite type wrapped the same basic types passed in operation 1 as params)

After creating an API instance on Azure within our account I added some users, a product and a new API!

1. Publisher Portal, API Management, APIs: Settings

The settings of the API were very basic - I did try and import the WSDL definition from file, out of curiosity and after playing with the encoding to get that right (it didn't like the initial file we copied out of Visual Studio). However quickly got an error around the WSDL not being understood. 
It could have been me, so it might work, I didn't take more than a couple of minutes trying. Certainly at the time of writing WADL and SWAGGER were the two radio option buttons to describe the definition contents type.

Web API Name = "Hello World" 
Description =
Connection = Directly (in a real scenario I'd explore VPN between APIM and back-end, which is beyond this post)

Web Service URL = https://ourdomain.com/helloworld/service1.svc
Web API URL Suffix = service1Endpoint
Https = [ticked]

This was the resulting Web API URL

https://[ourazuredomain]helloworld.azure-api.net/service1Endpoint

2. Publisher Portal, API Management, APIs: Operations

Here's the interesting bit, APIM really doesn't do much until you add at least one operation and doesn't seem to surface at all right now if you publish an API without any (which is logical).

This is the bit I got stuck with, what to put in the operation section for a SOAP endpoint method?

What I began by doing was adding a single POST method to cover all endpoint methods, thus:

Http Verb = "POST" (I want to post my soap envelope)
Url Template = "/"
Rewrite URL tempate = ""
Display Name = "All operations"

But, I want to be in a position to add a POST operation to the API for each endpoint method, and this endpoint encapsulates ALL the methods, it's the SOAPAction at the back-end service which determines which method gets invoked on the endpoint and then the request XML within the SOAP body must match!

So I went back to the Web Service URL from step 1 and changed it to this:

Web Service URL = https://ourdomain.com/helloworld

(note I've lopped off the "/service1.svc" endpoint part)

Then I went back and edited my API method to look like this:-

Http Verb = "POST" 
Url Template = "/operation1"
Rewrite URL tempate = "/service1.svc"
Display Name = "Operation 1"

The key point here is that we now specify a URL segment to represent the method but we ALWAYS rewrite it to the service endpoint.

The reason for doing this is so that I can specify distinct operations.

But now I've got a further problem - there's nothing that can enforce the fact that I need the right payload for this operation to go to the right method....Yet...

Next I added an additional operation in for the second web service method, identical to that above except for the URL Template ("/operation2") and the Display name "Operation 2".

Finally I added a third method, a GET this time, to allow my consumers a chance to see the WSDL

Http Verb = "GET" 
Url Template = "/GetWsdl"
Rewrite URL tempate = "/service1.svc?wsdl" 
Display Name = "Get WSDL"

(Note: My test server was net4.0 otherwise it'd have been ?singleWsdl)
The important point is that my GET to the exposed URL segment "/GetWsdl" will always return the wsdl for this endpoint as it's re-rewritten as "/service1.svc?wsdl". I.e. with the wsdl URL query param.

Finally, one more step before I publish; need a way to associate the SOAP header to the correct operation - and one more thing I need to ensure I'm sending and receiving with a content type of "text/xml". And not "text/plain".

3. Publisher Portal, API Management, Policies: Policy Scope

I added a policy using the wizard for each of the endpoint operations defined in the steps above, the XML ended up looking like this:

GetWsdl
  


<policies>
 <inbound>
  <set-header exists-action="override" name="content-type">
   <value>text/xml</value>
  </set-header>
  <base></base>
  <rewrite-uri template="/service1.svc?wsdl">
 </rewrite-uri></inbound>
 <outbound>
  <set-header exists-action="override" name="content-type">
   <value>text/xml</value>
  </set-header>
  <base></base>
 </outbound>
</policies>





Operation1
  


<policies>
 <inbound>
  <set-header exists-action="override" name="content-type">
   <value>text/xml</value>
  </set-header>
  <set-header exists-action="override" name="SOAPAction">
   <value>http://tempuri.org/IService1/HelloWorldOperation1</value>
  </set-header>
  <base></base>
  <rewrite-uri template="/service1.svc">
 </rewrite-uri></inbound>
 <outbound>
  <set-header exists-action="override" name="content-type">
   <value>text/xml</value>
  </set-header>
  <base></base>
 </outbound>
</policies>




Operation2
  

<policies>
 <inbound>
  <base></base>
  <set-header exists-action="override" name="content-type">
   <value>text/xml</value>
  </set-header>
  <set-header exists-action="override" name="SOAPAction">
   <value>http://tempuri.org/IService1/HelloWorldOperation2</value>
  </set-header>
  <rewrite-uri template="/service1.svc">
 </rewrite-uri></inbound>
 <outbound>
  <set-header exists-action="override" name="content-type">
   <value>text/xml</value>
  </set-header>
  <base></base>
 </outbound>
</policies>




Given the fact the policies are hierarchical I could have optimized this by moving the content type header up a level.

The next most important part is the SOAPAction for the POST operations.

This ensures the right action goes with the right operation. There's nothing stopping the end user from sending a different action and request content but note the policy is set to OVERRIDE whatever the users sending which means they'd get an error if they sent the wrong payload to the wrong URL.

Clearly there's one quite big problem with all of this: When the user imports the WSDL into a .net application it's going to want to do so under a single endpoint - whereas APIM will want to a distinct URL per method. So there's a mismatch.

One approach could be to define the operations with the differentiator as a query string argument (of the SOAPAction name) instead of the URL segment, here's how operation1 might look like

Http Verb = "POST" 
Url Template = "/?op=HelloWorldOperation1"
Rewrite URL tempate = "/service1.svc"
Display Name = "Operation 1"


Later on that would be called, along with the subscription key instead of:

https://[ourazuresomain]helloworld.azure-api.net/service1Endpoint/operation1/?subscription-key=[ourkey]

It would be


https://[ourazuredomain]helloworld.azure-api.net/service1Endpoint/?op=HelloWorldOperation1&subscription-key=[ourkey]


Now the caller only has to manage the one URL in their web.config (let's assume they're using .net for now).

However this raises yet another point, which is how does the customer ensure that the subscription key gets added in the first place so the APIM façade allows the call through... 

We could ditch the subscription key, it's optional within APIM, but it's one of the things I'd want to use.

As it turns out, the subscription key doesn't have to be passed as a query string param, it can instead be passed via a request header.
The subscription key request header is "ocp-apim-subscription-key".

This might make more sense for a consuming customer, who could configure a set outbound header against their caller/client app to be attached for outgoing requests to our API.

In Summary
The exercise has proved we can relatively easily façade a wcf http SOAP service through APIM and go onto craft a neat developer portal experience, but, it doesn't feel quite right yet. The APIM is currently REST centric and requires a unique URL per operation. This is at odds with SOAP which is a single URL and utilises a SOAP Action to route to the correct operation for the contract the endpoint URL represents. 
The steps detailed above put the onus on the APIM façade consumer to modify the outbound call from client to service [façade] and that doesn't feel right. Also posted a comment here: feedback-azure-api-management-suggestions-soap-support



Friday, 27 February 2015

Multi threaded windows service

We recently had a requirement to host an internal windows service which would continuously process small, but relatively long (for example 15-90 seconds) running packets of work obtained from a queue.

 We also wanted a degree of parallelism for efficiency, the service being hosted on a relatively powerful multi-core windows server.

 Each packet of work would represent a business critical process - so we were keen to ensure that the design of the service was robust and that stopping the service for any reason would not cause worker threads to be terminated abruptly and in addition there was sufficient scope for error handling and compensation within the service.

 We did not have a requirement to cluster an individual service instance. It quickly became apparent that we needed to carefully consider how the windows Service-Control-Manager interacted with our service.

 What started off as a relatively trivial bit of code ended up being slightly more complex. I couldn't find very many good examples, so here's an idea of how the orchestrating service start and stop logic could be implemented, should it be useful.

  Note: This is an example and not production quality code. All comments welcomed.

 1. Starting
  • The first goal with this example is to ensure that the service OnStart() method clears quickly (it isn't blocked) 
  • That we start a "foreground thread" to manage the background threads. Foreground threads are less likely to be abruptly terminated by windows.
In our service example we can start by defining some low level constructs to help us manage the threads and provide a wait event to trigger stopping (this could equally have been implemented as a TPL cancellation token) This code goes at the root of the service class inheriting from ServiceBase

  private const int ThreadCount = 3;
  private readonly CountdownEvent _threadCounter = new CountdownEvent(1);
  private readonly ManualResetEvent _stopWork = new ManualResetEvent(false);

Next we define the overridden OnStart method implementation
  protected override void OnStart(string[] args)
  {
   Debug.WriteLine("OnStart() - Max background threads allowed ={0}" , ThreadCount);

   var thread = new Thread(OrchestrateBackgroundTasks) { Name = "Foreground", IsBackground = false };
   var pool = new Semaphore(ThreadCount, ThreadCount + 1);
            thread.Start(new WaitHandle[] { pool, _stopWork }); // pass argument object here

   Debug.WriteLine("OnStart() - complete");
  }

Note the method doesn't get blocked, the thread is defined as foreground (it's not background!) and set to work. The thread which entered the OnStart() method then exits.

 2. Stopping.
  • The second goal is to ensure that the OnStop() method has a way of signalling the orchestrating [foreground] thread that it must wind up without starting any more workers
  • The OnStop() method should also wait for any currently running background threads to complete gracefully.
  • It should also keep SCM (Service Control Manager) informed periodically
  • It shouldn't run forever, there should be a time out
  protected override void OnStop()
  {
   Debug.WriteLine("OnStop()");
   // signal stop
   _stopWork.Set();
   // wait on any still active threads, comment this out if waiting on the pool 
   // in OrchestrateBackgroundTasks() 
   // however doing that means that SCM could try and teardown the service instance
   // before it completes as it won't receive regular feedback
   WaitOnRunningTasksToComplete();
   Debug.WriteLine("OnStop() - complete");
  }


The OnStop Implementation signals the stop event then blocks waiting on running tasks to complete. Only when all background threads have completed gracefully or the time out reached will this method complete.

The OrchestrateBackgroundTasks method, which is run on our foreground thread, acquires two objects.

1. The "pool" represented here by a Semaphore
2. A reference to the stop event

As each task completes (but only to the maximum number of tasks allowed by the pool) a new task is scheduled. The task continuation is used to handle / log worker errors and signal that a new task can be enqueued.

Once the stop event is set/triggered the foreground thread and this procedure will exit.

  private void OrchestrateBackgroundTasks(object args)
  {
   // TODO: type and null checks required
   var handles = args as WaitHandle[];

   Debug.WriteLine("DoWork()");

   // wait on pool, or stop
   while (WaitHandle.WaitAny(handles) == 0)
   {
    // maintain pool
    var task = Task.Factory.StartNew(LongRunningBackgroundTask, _threadCounter.CurrentCount);

    //Increment the worked thread counter
    _threadCounter.AddCount();

    // once the background task is complete, handle any error and
    // signal the pool so a new worker task can be added (above)
    task.ContinueWith(t =>
    {
     // this code only runs when the background task completes
     if (t.IsFaulted)
     {
      // faulted 
      // log exception etc
     }

     Debug.WriteLine(string.Format("DoWork() - A background Task completed"));
     // release from pool and signal complete
     (handles[0] as Semaphore).Release();
     _threadCounter.Signal();
     
    });
   }

   // Indicate that the main thread is exiting.
   Debug.WriteLine("DoWork() signals main thread complete");
   _threadCounter.Signal();

   // Note: 
   // Could wait *here once stop has been signalled
   // rather than calling WaitOnRunningTasksToComplete() from OnStop()
   // ...by adding this line
   //_threadCounter.Wait();
   // However, within WaitOnRunningTasksToComplete() we can add code to
   // report back to the Service Control Manager (SCM) which in long
   // running scenarios helps windows understand the service is waiting and hasn't died.

   Debug.WriteLine("DoWork() - complete");
  }



The WaitOnRunningTasksToComplete method simply waits on the background thread counter or until time out, whichever happens sooner. Periodically it will signal back to the SCM via a ServiceBase method!
This helps with the end user experience.

Note: Thread sleep is used here, because we want the thread that entered OnStop() to be blocked.

  private void WaitOnRunningTasksToComplete()
  {
   Debug.WriteLine("Cleanup()");
   const int timeout = 5000; // 5 secs
   const int spinTotal = 5;
   var spinCounter = 0;

   while (_threadCounter.CurrentCount > 0 && spinCounter < spinTotal)
   {
    Debug.WriteLine("Cleanup() - blocking {0}ms", timeout);
    Thread.Sleep(timeout);// block thread
    if (_threadCounter.CurrentCount > 0)
    {
     Debug.WriteLine("Cleanup() - Work outstanding, ask SCM for {0}ms additional time", timeout);
     base.RequestAdditionalTime(timeout);
    }
    spinCounter++;
   }
   if (_threadCounter.CurrentCount > 0)
   {
    Debug.WriteLine("Cleanup() - complete, work outstanding");
   }
   else
   {
    Debug.WriteLine("Cleanup() - complete");
   }
  }

Might be a nicer way to implement this using TPL and certainly it could be engineered to be more reusable. TPL wraps a lot of the lower level constructs, but does have the advantage of being easier to read / maintain.


Here's the debug output for a Start, followed by a Stop...

OnStart() - Max background threads allowed =3
OnStart() - complete
DoWork()
LongRunningTask() - New #1
LongRunningTask() - New #2
LongRunningTask() - New #3
LongRunningTask() - #2 complete
LongRunningTask() - #1 complete
DoWork() - A background Task completed
LongRunningTask() - New #4
LongRunningTask() - #3 complete
DoWork() - A background Task completed
LongRunningTask() - New #3
DoWork() - A background Task completed
LongRunningTask() - New #4
OnStop()
DoWork() signals main thread complete
DoWork() - complete
Cleanup()
Cleanup() - blocking 5000ms
Cleanup() - Work outstanding, ask SCM for 5000ms additional time
Cleanup() - blocking 5000ms
LongRunningTask() - #4 complete
LongRunningTask() - #3 complete
DoWork() - A background Task completed
LongRunningTask() - #4 complete
DoWork() - A background Task completed
DoWork() - A background Task completed
Cleanup() - complete
OnStop() - complete