2012-10-08更新內容
1,因為對象池採用的是Stack,並且沒有“對象在使用中,但仍在池中”這種情況,都是直接pop出來的,所以就廢棄掉了idle狀態,但是加入了“工作對象數量”的概念。
2,順便把Stack改成了安全執行緒的ConcurrentStack,但這不是很重要,因為lock還是保留了下來
3,一些不需要lock的地方都去掉了
4,歸還對象時把串連關閉了(但不銷毀,第一版串連沒關閉,總是出問題),這就使得ValidateOnBorrow一定要置為true了
5,樣本中的process方法把Service對象用using包了起來,以及時釋放串連
原文:
項目決定採用Thrift,所有測試通過後,那就要解決串連池的問題了,網上一通海搜,難道是我用的關鍵詞不對?居然沒有C#對Thrift串連池的實現,沒辦法,只有根據對象池的模式自己寫一個,經過多人多線程測試後,基本沒有問題了,比較粗糙,歡迎拍磚
幾個原則:
1,能控制對象池中對象的總數
2,能控制對象池中空閑對象的總數(改為控制工作對象的總數)
3,提供一個借出對象的介面
4,提供一個歸還對象的介面
5,借出和歸還的對象有檢驗機制
這樣,大致就可以用了,未來可以加上逾時自動釋放的機制,目前未實現。
分如下幾部分
1:設定檔/設定物件
設定檔肯定要有,設定物件僅僅是為了操作方便(比如別的地方要用),如果沒有特殊的需求,直接把配置項寫到私人屬性裡就可以了
public class ThriftConfig{/// <summary>/// 伺服器位址/// </summary>public string Host { get; set; }/// <summary>/// 服務連接埠/// </summary>public int Port { get; set; }/// <summary>/// 傳輸編碼/// </summary>public Encoding Encode { get; set; }/// <summary>/// 是否啟用壓縮/// </summary>public bool Zipped { get; set; }/// <summary>/// 連線逾時/// </summary>public int Timeout { get; set; }/// <summary>/// 可以從緩衝池中指派至的最大數量/// </summary>public int MaxActive { get; set; }/// <summary>/// 緩衝池中最大空閑對象數量/// </summary>public int MaxIdle { get; set; }/// <summary>/// 緩衝池中最小空閑對象數量/// </summary>public int MinIdle { get; set; }/// <summary>/// 阻塞的最大數量/// </summary>public int MaxWait { get; set; }/// <summary>/// 從緩衝池中指派至時是否驗證對象/// </summary>public bool ValidateOnBorrow { get; set; }/// <summary>/// 從緩衝池中歸還對象時是否驗證對象/// </summary>public bool ValidateOnReturn { get; set; }/// <summary>/// 從緩衝池中掛起對象時是否驗證對象/// </summary>public bool ValidateWhiledIdle { get; set; }
設定檔不過就是上述內容的xml形式,不贅述
2,串連池類
public class ThriftPool{#region 屬性private ThriftConfig config;/// <summary>/// 對象緩衝池/// </summary>//private static Stack<TTransport> objectPool { get; set; }private static ConcurrentStack<TTransport> objectPool { get; set; }/// <summary>/// 同步對象/// </summary>private static AutoResetEvent resetEvent;/// <summary>/// 每取走一例,表示啟用物件加1,此屬性可控制對象池容量/// </summary>private static volatile int activedCount = 0;/// <summary>/// 同步對象鎖/// </summary>private static object locker = new object();#endregion#region 建構函式public ThriftPool(){config = GetConfig();CreateResetEvent();CreateThriftPool();}#endregion#region 公有方法/// <summary>/// 從對象池取出一個對象/// </summary>/// <returns></returns>public TTransport BorrowInstance(){lock (locker){//Console.WriteLine("借前對象池個數:{0},工作對象個數:{1}", objectPool.Count(), activedCount);TTransport transport;//對象池無空閑對象if (objectPool.Count() == 0){//對象池已啟用物件數達上限if (activedCount == config.MaxActive){resetEvent.WaitOne();}else{PushObject(CreateInstance());}}if (!objectPool.TryPop(out transport)) throw new Exception("串連池異常");//transport = objectPool.Pop();activedCount++;//檢查對象池存量//對象池存量小於最小空閑數,並且啟用數小於最大啟用數,添加一個對象到對象池if (objectPool.Count() < config.MinIdle && activedCount < config.MaxActive){PushObject(CreateInstance());}if (config.ValidateOnBorrow){ValidateOnBorrow(transport);}return transport;}}/// <summary>/// 歸還一個對象/// </summary>/// <param name="instance"></param>public void ReturnInstance(TTransport instance){//對象池容量達到上限,不再返回線程池,直接銷毀if (objectPool.Count() == config.MaxIdle){DestoryInstance(instance);}else{if (config.ValidateOnReturn){ValidateOnReturn(instance);}PushObject(instance);activedCount--;//發通知訊號,有對象歸還到對象池resetEvent.Set();}//Console.WriteLine("歸還後對象池個數:{0},歸還後工作對象個數:{1}", objectPool.Count(), activedCount);}#endregion#region 私人方法/// <summary>/// 建立線程同步對象/// </summary>private void CreateResetEvent(){if (resetEvent == null){resetEvent = new AutoResetEvent(false);}}/// <summary>/// 建立對象池/// </summary>private void CreateThriftPool(){if (objectPool == null){objectPool = new ConcurrentStack<TTransport>();// new Stack<TTransport>();}}/// <summary>/// 添加對象到對象池/// </summary>/// <param name="transport"></param>private void PushObject(TTransport transport){objectPool.Push(transport);}/// <summary>/// 建立一個對象/// </summary>/// <returns></returns>private TTransport CreateInstance(){TTransport transport = new TSocket(config.Host, config.Port);transport.Open();return transport;}/// <summary>/// 取出對象時校正對象/// </summary>private void ValidateOnBorrow(TTransport instance){if (!instance.IsOpen){instance.Open();}}/// <summary>/// 歸還對象時校正對象/// </summary>private void ValidateOnReturn(TTransport instance){if (instance.IsOpen){instance.Close();}}/// <summary>/// 銷毀對象/// </summary>/// <param name="instance"></param>private void DestoryInstance(TTransport instance){instance.Flush();if (instance.IsOpen){instance.Close();}instance.Dispose();}/// <summary>/// 得到配置參數/// </summary>/// <returns></returns>private ThriftConfig GetConfig(){return Utility.GetConfig();}#endregion}
封裝了一個服務類來取Thrift連線物件,把對象池模式隱藏起來
要注意的是,這個對象必須實現IDisposable介面,因為要手動在串連完畢後歸還對象
internal class Service : IDisposable { ThriftPool pool; TTransport transport; MyThriftTest.Client client;//我寫的測試伺服器就叫MyThriftTest bool disposed; public ThriftConfig config { get; set; } public Service() { disposed = false; pool = new ThriftPool(); transport = pool.BorrowInstance();//從對象池取出一個對象 TProtocol protocol = new TBinaryProtocol(transport); client = new MyThriftTest.Client(protocol); } public string Invoke(string arg1, string arg2) {//我寫的測試方法就叫invoke return client.invoke(arg1, arg2); } ~Service() { Dispose(false); } protected void Dispose(bool disposing) { if (!disposed) { if (disposing) { pool.ReturnInstance(transport);//歸還當前對象到對象池 } // Release unmanaged resources disposed = true; } } public void Dispose() { Dispose(true); GC.SuppressFinalize(this); } }
測試:
static void Main(string[] args){for (int i = 0; i < 1000; i++){Thread t = new Thread(new ThreadStart(process));t.Name = "thread:" + i;t.Start();}}private static void process(){try{using(Service svr=new Service()){string o = svr.Invoke("hello", "world");log(o);}}catch (Exception ex){log(ex);}}static object o = new object();private static void log(string msg){lock (o){using (StreamWriter sm = new StreamWriter(@"D:\thrift.txt", true)){sm.WriteLine(msg);}}}
測試代碼中寫檔案而不是輸出到控制台,主要是因為控制台可顯示的條目過少,一旦出了錯就滾過去了
測試良好,有發現的問題再改吧,再次聲明,歡迎拍磚
參考連結:
C# http://www.cnblogs.com/RushSykes/archive/2012/04/16/2451880.html
Javahttp://www.cnblogs.com/51cto/archive/2010/08/18/Thrift_Connection_pool.html