1

我们使用的是内存数据库Exasol,它确实提供了Ado.Net提供商,但它似乎缺少像ConnectionPooling一些重要的功能,因此,每一个连接创建和销毁每请求,这会影响我们的性能,因为我们正在连接到AWS上的托管数据库。我已经创建了一个简单的ConnectionPool,有能力Resize,请提出建议,如果这可以达到目的,或者我需要做更多的事情。自定义连接池

请注意我并不是在寻找代码审查,而是对我在当前实现中可能缺少的重要分析,以及是否有可用的实现(Nuget,Git),我可以重复使用。目前,我正在根据规模调整大小,如何根据时间达到相同的目标,在某些闲置时间内,应从队列中清除少量资源,从而减小规模。

重要细节:

  1. 使用ConcurrentQueue内部的线程安全的访问资源,从多个客户端
  2. 使用AutoResetEvent的等待和信号,如果池是空的
  3. 使用TPL的尺寸调整操作,没有停止呼叫代码,我的理解是这项工作,即使当客户来电返回,因为它的一个Threadpool thread

    class ExasolConnectionPool 
    { 
        /// <summary> 
        /// Thread safe queue for storing the connection objects 
        /// </summary> 
        private ConcurrentQueue<EXAConnection> ExasolConnectionQueue { get; set; } 
    
        /// <summary> 
        /// Number of connections on the Connection pool 
        /// </summary> 
        private int _connectionCount; 
    
        /// <summary> 
        /// Max Pool Size 
        /// </summary> 
        private int MaxPoolSize { get; set; } 
    
        /// <summary> 
        /// Min Pool Size 
        /// </summary> 
        private int MinPoolSize { get; set; } 
    
        /// <summary> 
        /// Increase in Pool Size 
        /// </summary> 
        private int IncreasePoolSize { get; set; } 
    
        /// <summary> 
        /// Decrease in Pool Size 
        /// </summary> 
        private int DecreasePoolSize { get; set; } 
    
        /// <summary> 
        /// Connection string for the Connection pool connections 
        /// </summary> 
        private string ConnectionString { get; set; } 
    
        /// <summary> 
        /// Auto Reset event for the connection pool 
        /// </summary> 
        private AutoResetEvent ExasolConnectionPoolAre { get; set; } 
    
        /// <summary> 
        /// Connection pool specific Lock object 
        /// </summary> 
        private readonly object lockObject; 
    
        /// <summary> 
        /// Connection pool constructor 
        /// </summary> 
        /// <param name="connectionString"></param> 
        /// <param name="poolSize"></param> 
        public ExasolConnectionPool(string connectionString, int poolSize = 10) 
        { 
         // Set the Connection String 
         ConnectionString = connectionString; 
    
         // Intialize the Connection Queue 
         ExasolConnectionQueue = new ConcurrentQueue<EXAConnection>(); 
    
         // Enqueue initial set of connections 
         for (int counter = 0; counter < poolSize; counter++) 
         { 
          var exaConnection = new EXAConnection {ConnectionString = ConnectionString}; 
    
          ExasolConnectionQueue.Enqueue(exaConnection); 
         } 
    
         // Initialize Lock object 
         lockObject = new object(); 
    
         // Set the Connection queue count 
         _connectionCount = poolSize; 
    
         // Max pool size 
         MaxPoolSize = poolSize; 
    
         // Min Pool Size 
         MinPoolSize = 2; 
    
         IncreasePoolSize = 5; 
    
         DecreasePoolSize = 3; 
    
         ExasolConnectionPoolAre = new AutoResetEvent(false); 
        } 
    
        /// <summary> 
        /// 
        /// </summary> 
        /// <returns></returns> 
        public EXAConnection GetConnection() 
        { 
         // Return ExaConnection object 
         EXAConnection returnConnection; 
    
         // Try Dequeue the connection object from the Concurrent Queue 
         var validExasolConnection = ExasolConnectionQueue.TryDequeue(out returnConnection); 
    
         // If No Valid connection is available, then wait using AutoReset signaling mechanism 
         while (!validExasolConnection) 
         { 
          ExasolConnectionPoolAre.WaitOne(); 
    
          validExasolConnection = ExasolConnectionQueue.TryDequeue(out returnConnection); 
         } 
    
         // Thread safe connection count update 
         Interlocked.Decrement(ref _connectionCount); 
    
         Task.Factory.StartNew(() => 
         { 
          lock (lockObject) 
          { 
           if (_connectionCount > MinPoolSize) return; 
    
           for (var counter = 0; counter < IncreasePoolSize; counter++) 
           { 
            var exaConnection = new EXAConnection {ConnectionString = ConnectionString}; 
    
            ExasolConnectionQueue.Enqueue(exaConnection); 
    
            Interlocked.Increment(ref _connectionCount); 
           } 
          } 
         }); 
    
         return (returnConnection); 
        } 
    
        /// <summary> 
        /// 
        /// </summary> 
        /// <param name="returnedConnection"></param> 
        public void ReturnConnection(EXAConnection returnedConnection) 
        { 
         ExasolConnectionQueue.Enqueue(returnedConnection); 
    
         Interlocked.Increment(ref _connectionCount); 
    
         ExasolConnectionPoolAre.Set(); 
    
         Task.Factory.StartNew(() => 
         { 
          lock (lockObject) 
          { 
           if (_connectionCount < MaxPoolSize * 1.5) return; 
    
           for (var counter = 0; counter < DecreasePoolSize; counter++) 
           { 
            EXAConnection exaConnection; 
    
            if (ExasolConnectionQueue.TryDequeue(out exaConnection)) 
            { 
             exaConnection.Dispose(); 
    
             exaConnection = null; 
    
             Interlocked.Decrement(ref _connectionCount); 
            } 
           } 
          } 
         }); 
        } 
    } 
    

回答

1

您游泳池的实施情况良好。我不知道有哪些NuGet实现这么小,并且不会因为你的情况而过于复杂。我只是想添加一些您可以自行调查的建议。

  1. StartNew is Dangerous文章斯蒂芬·克利里是关于你正在使用的调整逻辑的方法伟大的职位。最重要的部分是这样的:

    线程“A”将在任何TaskScheduler运行是目前执行!

    所以,你的代码有时可以使用UI线程上下文,并降低应用程序的性能。如果你没有问题(例如,ASP.NET应用程序),那么很好,但如果不是,我建议你改用Task.Run方法。您还可以查看Stephen的博客,了解TPL最佳做法。

  2. 一般来说,调整大小的逻辑是以一种简单的方式完成的,其大小加倍,所以如果达到了极限,大小会变成两倍,反之亦然。我认为向用户提供管理这些常量的能力可能会导致一些奇怪的错误,例如负池大小和类似。

    所以你应该做你的财产设置者为private,并且,对于我来说,删除有关调整大小的属性。也许将来您可以为应用程序平均收集池大小的统计数据,并将该参数用作默认值。

+0

感谢您的评论和重要细节。对于调整大小,由于连接对象是昂贵的资源,因此不像标准资源池,更喜欢受控制的调整大小机制,有时超过特定持续时间的空闲开放连接是纯粹的资源浪费,可以重新创建。在其他资源池中,他们通过加倍来增加收集的大小,而不是实际的对象,这些对象仍然需要根据需要创建/添加。 –

+0

对于各种属性,正如您可能已经注意到的那样,它们是私有对象,但是与任何标准连接池用户都能够调整它一样,通过某些检查来避免异常 –

+0

是的,那只是一个观察。我想你知道你在做什么,所以你的项目祝你好运:) – VMAtm