# Tuesday, 23 August 2005

I’m not actually dead.  Here I am posting.  I’ve been doing a lot of design work lately, and have reached a generally introspective period, so not posting much lately. 

That said…

I was trying to come up with a way to make asynchronous requests from a web server to a back end system.  Like really asynchronous.  Fire and forget, no socket open on the network, etc.  Maybe implemented as two one-way web services, whatever.  The glaringly obvious fact is that given the nature of HTTP, the web client has to block, since an HTTP request/response is by its very nature synchronous and blocking.

So, the solution I came up with was to do the blocking in the one place where it matters, which is in the web client.  It depends on sending some kind of correlation ID between client and server, and back.  So sending the message from ASP.NET might look like this:

    public WaitHandle Send(Envelope env)

    {

      WaitHandle h = new AutoResetEvent(false);

      string correlation = Guid.NewGuid().ToString();

      env.CorrelationID = correlation;

      lock(correlationToWaitHandle.SyncRoot)

      {

        correlationToWaitHandle.Add(correlation, h);

      }

 

      ThreadPool.QueueUserWorkItem(new WaitCallback(DoSending), env);

 

      return h;

    }

 

This will do an async send to your back end, keeping track of a WaitHandle by the correlation ID.  That way, you hand the WaitHandle back to the client, who can wait on it as required by HTTP.

In the mean time, the DoSending method has gone and sent the Envelope to your back end.  When it’s done, it sends the response back to the client machine by whatever means (I’m using ASMX web services) and calls the PostResult method with the response…

    public static void PostResult(Envelope env)

    {

      if(correlationToWaitHandle.Contains(env.CorrelationID))

      {

        AutoResetEvent are = null;

        lock(correlationToWaitHandle.SyncRoot)

        {

          are = (AutoResetEvent)correlationToWaitHandle[env.CorrelationID];

          if(are == null)//this shouldn't happen, but would be bad.  Other option is to check Contains again after lock.

            throw new InvalidOperationException("Threading problem!  CorellationID got removed before lock!");

        }

        lock(waitHandleToResult.SyncRoot)

        {

          waitHandleToResult[are] = env;

        }

        are.Set();

      }

      else

      {

        throw new ArgumentException("unknown corellationID","env");

      }

    }

Look up the wait handle associated with the correlation ID, since we need it, and it’s all the client has at this point, then take the response we got back from the server and store it against the wait handle.  Finally, signal the wait handle, so the client will no we’re done.  When that happens, the client will stop waiting, and call the Receive method to get the results.

    public Envelope Receive(WaitHandle wh)

    {

      Envelope result = null;

 

 

      if(waitHandleToResult.Contains(wh))

      {

        lock(waitHandleToResult.SyncRoot)

        {

          if(waitHandleToResult.Contains(wh))

          {

            result = (Envelope)waitHandleToResult[wh];

            wh.Close();

            waitHandleToResult.Remove(wh);

          }

        }

        lock(correlationToWaitHandle.SyncRoot)

        {

          correlationToWaitHandle.Remove(result.CorrelationID);

        }

      }

      else

      {

        throw new InvalidOperationException("No response received.");

      }

     

      return result;

 

    }

We look up the stored result by the wait handle, clean up our hashtables, and return the result to the client.

For the sake of simplicity, we can go ahead and wrap this in a synchronous method for the client to call.

    public void SendAndWait(ref Envelope env)

    {

 

      WaitHandle h = Send(env);

      if(h.WaitOne(TimeSpan.FromSeconds(90.0),true))

      {

        env = Receive(h);

      }

      else

      {

        throw new RequestTimeoutException("Timed out waiting for server");

      }

 

    }

That way all the wait handle stuff is hidden, and the client just sees their response come back in the [ref] Envelope in a synchronous fashion. 

By decoupling the client from the server this way, we not only get better control over how many threads are running in our server side implementation (although there are other ways to do that) but we also aren’t dependent on the ability to hold open a network socket from client to server during the time it takes to process the request.  Normally that’s not an issue, but it can be a problem in certain circumstances. 

Tuesday, 23 August 2005 14:39:05 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  | 
# Thursday, 11 August 2005
My sister Erica delivered my first nephew yesterday afternoon.  Connor James Mullen, 7 lb. 15 oz.  Woo hoo!  Everyone is doing beautifully.
Thursday, 11 August 2005 11:02:43 (Pacific Daylight Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |