閱讀142 返回首頁    go 阿裏雲 go 技術社區[雲棲]


WCF後續之旅(10): 通過WCF Extension實現以對象池的方式創建Service Instance

一、實現原理

和本係列前兩篇文章(WCF和Unity Appliation Block集成;WCF和Policy Injection Application Block集成)一樣,涉及的是service instance的提供的問題,所以,我們也是通過自定義InstanceProvider來實現以對象池的機製創建service instance的目的。

四、PooledInstnaceProvider的創建

   1: namespace Artech.WCFExtensions
   2: {
   3:     public interface IPooledObject
   4:     {
   5:         bool IsBusy
   6:         { get; set; }
   7:     }
   8: } 

II、WeakReferenceCollection和WeakReferenceDictionary

   1: namespace Artech.WCFExtensions
   2: {
   3:     public class WeakReferenceCollection:List<WeakReference>
   4:     {} 
   5:  
   6:     public class WeakReferenceDictionary : Dictionary<Type, WeakReferenceCollection>
   7:     {}
   8: } 

   1: namespace Artech.WCFExtensions
   2: {
   3:     public static class PooledInstanceLocator
   4:     {
   5:         internal static WeakReferenceDictionary ServiceInstancePool
   6:         { get; set; } 
   7:  
   8:         static PooledInstanceLocator()
   9:         {
  10:             ServiceInstancePool = new WeakReferenceDictionary();
  11:         } 
  12:  
  13:        public static IPooledObject GetInstanceFromPool(Type serviceType)
  14:         {
  15:             if(!serviceType.GetInterfaces().Contains(typeof(IPooledObject)))
  16:             {
  17:                 throw new InvalidCastException("InstanceType must implement Artech.WCFExtensions.IPooledInstance");
  18:             } 
  19:  
  20:             if (!ServiceInstancePool.ContainsKey(serviceType))
  21:             {
  22:                 ServiceInstancePool[serviceType] = new WeakReferenceCollection();
  23:             } 
  24:  
  25:             WeakReferenceCollection instanceReferenceList = ServiceInstancePool[serviceType] ; 
  26:  
  27:             lock (serviceType)
  28:             {
  29:                 IPooledObject serviceInstance =null;
  30:                 foreach (WeakReference weakReference in instanceReferenceList)
  31:                 {
  32:                     serviceInstance = weakReference.Target as IPooledObject;
  33:                     if (serviceInstance != null && !serviceInstance.IsBusy)
  34:                     {
  35:                         serviceInstance.IsBusy = true;
  36:                         return serviceInstance;
  37:                     }
  38:                 } 
  39:  
  40:                 serviceInstance = Activator.CreateInstance(serviceType) as IPooledObject;
  41:                 serviceInstance.IsBusy = true;
  42:                 instanceReferenceList.Add(new WeakReference(serviceInstance));
  43:                 return serviceInstance;
  44:             }            
  45:         } 
  46:  
  47:        public static void Scavenge()
  48:        {
  49:            foreach (Type serviceType in ServiceInstancePool.Keys)
  50:            {
  51:                lock (serviceType)
  52:                {
  53:                    WeakReferenceCollection instanceReferenceList = ServiceInstancePool[serviceType];
  54:                    for (int i = instanceReferenceList.Count - 1; i > -1; i--)
  55:                    {
  56:                        if (instanceReferenceList[i].Target == null)
  57:                        {
  58:                            instanceReferenceList.RemoveAt(i); 
  59:                        }
  60:                    } 
  61:  
  62:                }
  63:            }
  64:        } 
  65:  
  66:         public static void ReleaseInstanceToPool(IPooledObject instance)
  67:         {
  68:             instance.IsBusy = false;
  69:         }       
  70:     }
  71: } 
  72:  

   1: namespace Artech.WCFExtensions
   2: {
   3:     class PooledInstanceProvider : IInstanceProvider
   4:     {
   5:  
   6:         public object GetInstance(InstanceContext instanceContext, Message message)
   7:         {
   8:             return PooledInstanceLocator.GetInstanceFromPool(instanceContext.Host.Description.ServiceType);
   9:         }
  10:  
  11:         public object GetInstance(InstanceContext instanceContext)
  12:         {
  13:             return this.GetInstance(instanceContext, null);
  14:         }
  15:  
  16:         public void ReleaseInstance(InstanceContext instanceContext, object instance)
  17:         {
  18:             PooledInstanceLocator.ReleaseInstanceToPool(instance as IPooledObject);
  19:         }
  20:     }
  21: }

V、為自定義InstanceProvider定義Behavior

   1: namespace Artech.WCFExtensions
   2: {
   3:     public class PooledInstanceBehaviorAttribute : Attribute, IContractBehavior, IContractBehaviorAttribute
   4:     {
   5:         public void AddBindingParameters(ContractDescription contractDescription, ServiceEndpoint endpoint, BindingParameterCollection bindingParameters){ }
   6:         public void ApplyClientBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, ClientRuntime clientRuntime){ }
   7:         public void ApplyDispatchBehavior(ContractDescription contractDescription, ServiceEndpoint endpoint, DispatchRuntime dispatchRuntime)
   8:         {
   9:             dispatchRuntime.InstanceProvider = new PooledInstanceProvider();
  10:         }
  11:         public void Validate(ContractDescription contractDescription, ServiceEndpoint endpoint){ }
  12:         public Type TargetContract
  13:         {
  14:             get { return null; }
  15:         }
  16:     }
  17: } 

三、將PooledInstanceProvider應用到WCF應用中

image

I、Contract:Contracts.IService

   1: namespace Contracts
   2: {
   3:     [ServiceContract]
   4:     [PooledInstanceBehavior]
   5:     public interface IService : IPooledObject
   6:     {
   7:         [OperationContract(IsOneWay =true)]
   8:         void DoSomething();
   9:     }
  10: } 

   1: namespace Services
   2: {
   3:     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
   4:     public class Service : IService
   5:     {
   6:         static int count;
   7:         public Service()
   8:         {
   9:             Interlocked.Increment(ref count);
  10:             Console.WriteLine("{0}: Service instance is constructed!", count);
  11:         }
  12:         public void DoSomething(){ }
  13:         public bool IsBusy
  14:         { get; set; }
  15:     }
  16: } 

   1: public Service()
   2: {
   3:       Interlocked.Increment(ref count);
   4:       Console.WriteLine("{0}: Service instance is constructed!", count);
   5: } 

   1: namespace Hosting
   2: {
   3:     class Program
   4:     {
   5:         static Timer ScavengingTimer;
   6:  
   7:         static void Main(string[] args)
   8:         {
   9:             using (ServiceHost host = new ServiceHost(typeof(Service)))
  10:             {
  11:                 host.Opened += delegate
  12:                 {
  13:                     Console.WriteLine("Service has been started up!");
  14:                 }; 
  15:  
  16:                 host.Open(); 
  17:  
  18:                 ScavengingTimer = new Timer(delegate
  19:                     {
  20:                         PooledInstanceLocator.Scavenge();
  21:                     }, null, 0, 5000);                 
  22:  
  23:                 Console.Read(); 
  24:  
  25:             }
  26:         }
  27:     }
  28: } 
  29:  

除了對service進行Host之外,Main()方法還通過一個Timer對象實現對對象池的清理工作(調用PooledInstanceLocator.Scavenge();),時間間隔是5s。
下麵是configuration:

   1: <?xml version="1.0" encoding="utf-8" ?>
   2: <configuration>    
   3:     <system.serviceModel>
   4:         <services>
   5:             <service name="Services.Service">
   6:                 <endpoint binding="basicHttpBinding" contract="Contracts.IService" />
   7:                 <host>
   8:                     <baseAddresses>
   9:                         <add baseAddress="https://127.0.0.1/service" />
  10:                     </baseAddresses>
  11:                 </host>
  12:             </service>
  13:         </services>
  14:     </system.serviceModel>
  15: </configuration> 

   1: namespace Clients
   2: {
   3:     class Program
   4:     {
   5:         static void Main(string[] args)
   6:         {
   7:             using (ChannelFactory<IService> channelFactory = new ChannelFactory<IService>("service"))
   8:             {
   9:                 for (int i = 1; i <= 10; i++)
  10:                 {
  11:                     Console.WriteLine("{0}: invocate service!", i);
  12:                     channelFactory.CreateChannel().DoSomething();
  13:                     Thread.Sleep(1000);
  14:                 }
  15:             } 
  16:  
  17:             Console.Read();
  18:         }
  19:     }
  20: } 

image

image

   1: namespace Contracts
   2: {
   3:     [ServiceContract]
   4:     //[PooledInstanceBehavior]
   5:     public interface IService : IPooledObject
   6:     {
   7:         [OperationContract(IsOneWay =true)]
   8:         void DoSomething();
   9:     }
  10: } 

image

可見在沒有運用PooledInstanceBehavior情況下,service instance的創建真正使“PerCall”。我們將PooledInstanceBehavior重新加上,然後通過在DoSomething方法中加上下麵的代碼延長該方法執行的時間:

   1: namespace Services
   2: {    
   3:     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
   4:     public class Service:IService
   5:     { 
   6:         public void DoSomething()
   7:         {
   8:             Thread.Sleep(2000);
   9:         } 
  10:     }
  11: } 

image 
由於我們將DoSomething方法的執行延長至2s,在這種情況下,由於client端的service掉用的間隔是1s,所有當第二次service調用抵達之後,第一次創建的service instance還沒有被釋放,所以需要重新創建新的service instance。當第三次service調用時,第一個service instance已經釋放,以此類推,永遠隻需要兩個service instance。這和上麵的結果一致。

   1: namespace Hosting
   2: {
   3:     class Program
   4:     {
   5:         static Timer ScavengingTimer;
   6:         static Timer GCTimer;
   7:         static void Main(string[] args)
   8:         {
   9:             using (ServiceHost host = new ServiceHost(typeof(Service)))
  10:             {
  11:                 host.Opened += delegate
  12:                 {
  13:                     Console.WriteLine("Service has been started up!");
  14:                 }; 
  15:  
  16:                 host.Open(); 
  17:  
  18:                 ScavengingTimer = new Timer(delegate
  19:                     {
  20:                         PooledInstanceLocator.Scavenge();
  21:                     }, null, 0, 5000); 
  22:  
  23:                 GCTimer = new Timer(delegate
  24:                     {
  25:                         GC.Collect();
  26:                     }, null, 0, 500); 
  27:  
  28:                 Console.Read(); 
  29:  
  30:             }
  31:         }
  32:     }
  33: } 
  34:  

   1: namespace Services
   2: {    
   3:     [ServiceBehavior(InstanceContextMode = InstanceContextMode.PerCall)]
   4:     public class Service:IService
   5:     { 
   6:  
   7:         #region IService Members 
   8:  
   9:         public void DoSomething()
  10:         {
  11:             Thread.Sleep(500);
  12:         } 
  13:  
  14:         #endregion
  15:     }
  16: } 

那麼現在的輸出結果將會是這樣:

image

WCF後續之旅:

WCF後續之旅(1): WCF是如何通過Binding進行通信的
WCF後續之旅(2): 如何對Channel Layer進行擴展——創建自定義Channel
WCF後續之旅(3): WCF Service Mode Layer 的中樞—Dispatcher
WCF後續之旅(4):WCF Extension Point 概覽
WCF後續之旅(5): 通過WCF Extension實現Localization
WCF後續之旅(6): 通過WCF Extension實現Context信息的傳遞
WCF後續之旅(7):通過WCF Extension實現和Enterprise Library Unity Container的集成
WCF後續之旅(8):通過WCF Extension 實現與MS Enterprise Library Policy Injection Application Block 的集成
WCF後續之旅(9):通過WCF的雙向通信實現Session管理[Part I]
WCF後續之旅(9): 通過WCF雙向通信實現Session管理[Part II]
WCF後續之旅(10): 通過WCF Extension實現以對象池的方式創建Service Instance
WCF後續之旅(11): 關於並發、回調的線程關聯性(Thread Affinity)
WCF後續之旅(12): 線程關聯性(Thread Affinity)對WCF並發訪問的影響
WCF後續之旅(13): 創建一個簡單的WCF SOAP Message攔截、轉發工具[上篇]
WCF後續之旅(13):創建一個簡單的SOAP Message攔截、轉發工具[下篇]
WCF後續之旅(14):TCP端口共享
WCF後續之旅(15): 邏輯地址和物理地址
WCF後續之旅(16): 消息是如何分發到Endpoint的--消息篩選(Message Filter)
WCF後續之旅(17):通過tcpTracer進行消息的路由

 


作者:蔣金楠
微信公眾賬號:大內老A
微博:www.weibo.com/artech
如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識別二維碼)關注個人公眾號(原來公眾帳號蔣金楠的自媒體將會停用)。
本文版權歸作者和博客園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁麵明顯位置給出原文連接,否則保留追究法律責任的權利。
原文鏈接

最後更新:2017-10-30 17:04:34

  上一篇:go  WCF後續之旅(9): 通過WCF雙向通信實現Session管理[下篇]
  下一篇:go  Enterprise Library深入解析與靈活應用(1):通過Unity Extension實現和Policy Injection Application Block的集成