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應用中
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: }
1: namespace Contracts
2: {
3: [ServiceContract]
4: //[PooledInstanceBehavior]
5: public interface IService : IPooledObject
6: {
7: [OperationContract(IsOneWay =true)]
8: void DoSomething();
9: }
10: }
可見在沒有運用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: }
由於我們將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: }
那麼現在的輸出結果將會是這樣:
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