C#实现Thrift连接池[新]

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

posted @ 2012-10-08 20:15  $walker  阅读(2564)  评论(0编辑  收藏  举报