Simple web services gateway

I wanted a way of limiting the number of clients that could call a long running and memory intensive web method which exported or imported several thousand lines of data into my database ....so a colleague of mine suggested semaphores. I implemented a singleton class which looked like code snippet B. It all seemed honkey dorey only that we found it turned out to be a pointless use of semaphores.

Semaphores add value when there was a requirement to queue up threads so that if there were 10 threads trying to access the service via a semaphore which allowed only 3 threads, the other threads will be queued up and will wait until the executing threads completed.

Using this singleton semaphore class to limit the number of clients that could call my long running and memory intensive web method was not fully taking advantage of the queuing ability of semaphores. I was monitoring the count of threads and throwing an exception when the limit I set was reached - which is why I said it was pointless.

One other issue with using semaphores for this purpose is that if the IIS application pool was limited to say 10 threads, and 10 users attempted to call my slow running web method call, the threads will be queued which means that if an 11th thread which wanted to make a really fast web method call tried to make its request, it will get a thread aborted exception because the number of threads allowed for the app pool has been exceeded.

So .......what I did instead was to implement a really simple web services gateway which implemented IDisposable so that when called within a using, the count was incremented and on Disposal of the instance, the count is decremented. I created a static instance of it within my web service and just used it when I needed to. It works like a treat and is dead easy to implement. See code snippet A_1 and code snippet A_2.

================ CODE SNIPPET A_1 =========================

   1: private static WebServicesGateway webServicesGateway = new WebServicesGateway(["noOfThreadsPermittedForWebCall"]); 
   2:  
   3: [WebMethod] 
   4: public byte[] RunMyLongRunningMemoryIntesiveMethod() 
   5: { 
   6:     using (webServicesGateway.GatewayRequest()) 
   7:     { 
   8:  
   9:     //do something 
  10:  
  11:     } 
  12: }

================ CODE SNIPPET A_2 =========================

   1: using System; 
   2: using System.Data; 
   3: using System.Configuration; 
   4: using System.Web; 
   5: using System.Web.Security; 
   6: using System.Web.UI; 
   7: using System.Web.UI.HtmlControls; 
   8: using System.Web.UI.WebControls; 
   9: using System.Web.UI.WebControls.WebParts; 
  10:  
  11: namespace Server 
  12: { 
  13:    /// <summary> 
  14:    /// 
  15:    /// </summary> 
  16:    public class WebServicesGateway 
  17:    { 
  18:       private int maxThreadCount = 3; 
  19:       private static int currentThreadCount; 
  20:       private static Gateway gateway; 
  21:       private object instanceLock = new object(); 
  22:  
  23:       public WebServicesGateway(int threadCount) 
  24:       { 
  25:          maxThreadCount = threadCount; 
  26:          gateway = new Gateway(this); 
  27:       } 
  28:  
  29:       private void DecrementCount() 
  30:       { 
  31:          lock (instanceLock) 
  32:          { 
  33:             currentThreadCount--; 
  34:          } 
  35:       } 
  36:  
  37:       public IDisposable GatewayRequest() 
  38:       { 
  39:          lock (instanceLock) 
  40:          { 
  41:             if (currentThreadCount >= maxThreadCount) 
  42:             { 
  43:                throw new WebServicesGatewayLimitException("Gateway has reached its limit"); 
  44:             } 
  45:             currentThreadCount++; 
  46:          } 
  47:          return gateway; 
  48:       } 
  49:  
  50:       private class Gateway : IDisposable 
  51:       { 
  52:          WebServicesGateway webServicesGateway; 
  53:  
  54:          public Gateway(WebServicesGateway webServicesGateway) 
  55:          { 
  56:             this.webServicesGateway = webServicesGateway; 
  57:          } 
  58:  
  59:          public void Dispose() 
  60:          { 
  61:             this.webServicesGateway.DecrementCount(); 
  62:          } 
  63:       } 
  64:    } 
  65:  
  66:    public class WebServicesGatewayLimitException : Exception 
  67:    { 
  68:       private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
  69:  
  70:       public WebServicesGatewayLimitException(string message) 
  71:          : base(message) 
  72:       { 
  73:          log.Error(message); 
  74:       } 
  75:       public WebServicesGatewayLimitException(string message, Exception exception) 
  76:          : base(message, exception) 
  77:       { 
  78:          log.Error(message, exception); 
  79:       } 
  80:    } 
  81: }
  82:  

================ CODE SNIPPET B =========================

   1: using System; 
   2: using System.Data; 
   3: using System.Configuration; 
   4: using System.Web; 
   5: using System.Threading; 
   6: using System.Web.Security; 
   7: using System.Web.UI; 
   8: using System.Web.UI.HtmlControls; 
   9: using System.Web.UI.WebControls; 
  10: using System.Web.UI.WebControls.WebParts; 
  11: namespace Server 
  12: { 
  13:    /// <summary> 
  14:    /// 
  15:    /// </summary> 
  16:    public class WebMethodCallLimiter : IDisposable 
  17:    { 
  18:       static WebMethodCallLimiter instance = null; 
  19:       static readonly object instanceLock = new object(); 
  20:       private static Semaphore semaphore = null; 
  21:       private static int currentThreadCount = 0; 
  22:       private static int maxThreadCount = 3;
  23:  
  24:       private WebMethodCallLimiter() 
  25:       { 
  26:         semaphore = new Semaphore(0, maxThreadCount);         
  27:       } 
  28:       public void Pool() 
  29:       {        
  30:          if (currentThreadCount >= maxThreadCount) 
  31:          { 
  32:             throw new SemaphoreReachedLimitException("Semaphore has reached its limit"); 
  33:          }
  34:  
  35:          lock (instanceLock) 
  36:          { 
  37:             currentThreadCount++; 
  38:          } 
  39:          semaphore.WaitOne(); 
  40:       }
  41:  
  42:       public void Dispose() 
  43:       { 
  44:          currentThreadCount--; 
  45:          semaphore.Release(); 
  46:       }
  47:  
  48:       public static WebMethodCallLimiter Instance 
  49:       { 
  50:          get 
  51:          { 
  52:             lock (instanceLock) 
  53:             { 
  54:                if (instance == null) 
  55:                { 
  56:                   instance = new WebMethodCallLimiter(); 
  57:                } 
  58:                return instance; 
  59:             } 
  60:          } 
  61:       }
  62:  
  63:    } 
  64:    public class SemaphoreReachedLimitException : Exception 
  65:    { 
  66:       private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); 
  67:       public SemaphoreReachedLimitException(string message) 
  68:          : base(message) 
  69:       { 
  70:          log.Error(message); 
  71:       } 
  72:       public SemaphoreReachedLimitException(string message, Exception exception) 
  73:          : base(message, exception) 
  74:       { 
  75:          log.Error(message, exception); 
  76:       } 
  77:    }
  78:  
  79: }

Comments

No comments posted yet.

Leave Your Comment

Title*
Name*
Email (never displayed)
 (will show your gravatar)
Url
Comment*

Please add 8 and 3 and type the answer here:

Preview Your Comment.