Exasol Ado.Net 提供商的自定义连接池
Custom Connection pool for Exasol Ado.Net provider
我们正在使用内存数据库 Exasol
,它确实提供了 Ado.Net 提供程序,但它似乎缺少一些重要的功能,例如 ConnectionPooling
,因此每个连接都会创建并根据请求销毁,这会影响我们的性能,因为我们正在连接到 AWS
上的托管数据库。我创建了一个简单的 ConnectionPool
,具有 Resize
的能力,请建议这是否可以达到目的,或者我需要做更多的事情。
请注意,我不是在寻找代码审查,而是对我在当前实施中可能遗漏的内容进行批判性分析,如果有可用的实施(Nuget,Git),我可以重复使用。目前我正在根据大小调整大小,如何根据时间实现相同的大小,在空闲的一定持续时间内,应从队列中清除少量资源,从而减小大小。
重要细节:
- 在内部使用
ConcurrentQueue
从多个客户端安全地访问资源
- 使用
AutoResetEvent
等待,如果池为空则发出信号
使用 TPL
进行大小调整操作,无需停止调用代码,我的理解是即使在客户端调用 returns 时也能正常工作,因为它在 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);
}
}
}
});
}
}
您的池的实现很好。我不知道有任何 NuGet 实现这么小,而且对您的情况来说并不过分复杂。我只是想添加少量建议,您可以自行研究。
StartNew
is Dangerous Stephen Cleary 的文章非常好 post 关于您用于调整逻辑大小的方法。最重要的部分是:
Thread "A" will run on whatever TaskScheduler
is currently executing!
因此您的代码有时 可以使用UI
线程上下文并降低应用程序的性能。如果它适合您(例如,对于 ASP.NET 应用程序),那很好,但如果不适合,我建议您改用 Task.Run
方法。您还可以查看 Stephen 关于 TPL
最佳实践的博客。
一般来说,调整大小的逻辑是通过简单的方式完成的,将大小加倍,所以如果你达到了限制,大小就会变成它的两倍,反之亦然。我认为为用户提供管理此常量的能力可能会导致一些奇怪的错误,例如负池大小和类似错误。
所以你应该像 private
那样做你的 属性 设置器,并且对于我来说,删除有关调整大小的属性。也许将来您可以为您的应用程序平均收集池大小的统计信息,并将该参数用作默认值。
我们正在使用内存数据库 Exasol
,它确实提供了 Ado.Net 提供程序,但它似乎缺少一些重要的功能,例如 ConnectionPooling
,因此每个连接都会创建并根据请求销毁,这会影响我们的性能,因为我们正在连接到 AWS
上的托管数据库。我创建了一个简单的 ConnectionPool
,具有 Resize
的能力,请建议这是否可以达到目的,或者我需要做更多的事情。
请注意,我不是在寻找代码审查,而是对我在当前实施中可能遗漏的内容进行批判性分析,如果有可用的实施(Nuget,Git),我可以重复使用。目前我正在根据大小调整大小,如何根据时间实现相同的大小,在空闲的一定持续时间内,应从队列中清除少量资源,从而减小大小。
重要细节:
- 在内部使用
ConcurrentQueue
从多个客户端安全地访问资源 - 使用
AutoResetEvent
等待,如果池为空则发出信号 使用
TPL
进行大小调整操作,无需停止调用代码,我的理解是即使在客户端调用 returns 时也能正常工作,因为它在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); } } } }); } }
您的池的实现很好。我不知道有任何 NuGet 实现这么小,而且对您的情况来说并不过分复杂。我只是想添加少量建议,您可以自行研究。
StartNew
is Dangerous Stephen Cleary 的文章非常好 post 关于您用于调整逻辑大小的方法。最重要的部分是:Thread "A" will run on whatever
TaskScheduler
is currently executing!因此您的代码有时 可以使用
UI
线程上下文并降低应用程序的性能。如果它适合您(例如,对于 ASP.NET 应用程序),那很好,但如果不适合,我建议您改用Task.Run
方法。您还可以查看 Stephen 关于TPL
最佳实践的博客。一般来说,调整大小的逻辑是通过简单的方式完成的,将大小加倍,所以如果你达到了限制,大小就会变成它的两倍,反之亦然。我认为为用户提供管理此常量的能力可能会导致一些奇怪的错误,例如负池大小和类似错误。
所以你应该像
private
那样做你的 属性 设置器,并且对于我来说,删除有关调整大小的属性。也许将来您可以为您的应用程序平均收集池大小的统计信息,并将该参数用作默认值。