談一談對像池SafeObjectPool能幹什麼
前言
首先從ado.net的連線池開始瞭解,資料庫操作通常是 new SqlConnection()、 Open()、 使用完後 Close(),整個過程相當耗時,特別是頻繁建議套字接連線的過程。ado.net 驅動已經現實了連線池管理,Open() 等於申請連線,Close() 即歸還資源。
Open() 的時候有幾種情況:
1、有資源直接返回;
2、無可用資源,且未超過池最大設定值時,建立資源並返回;
3、無可用資源,此時會等待設定秒數,若仍然未獲取資源則報錯;
連線的複用使效能成數倍提升,試想網站在某一時刻突然爆增10萬次,new 10萬個SqlConnection物件顯然會炸掉服務,建立,connect,disconnect,disponse,顯然開銷很大。
雖然ado.net自帶的連線池已經接近完美,但在某些場合還不夠用,先來一個壓力測試。
SafeObjectPool與dapper比武測試
[HttpGet("vs_gen")] async public Task<object> vs_gen() { var select = Tag.Select; var count = await select.CountAsync(); var items = await select.Page(page, limit).ToListAsync(); return new { count, items }; } [HttpGet("vs_dapper")] async public Task<object> vs_dapper() { var conn = new SqlConnection("Data Source=.;Integrated Security=True;Initial Catalog=cms;Pooling=true;Max Pool Size=11"); conn.Open(); var count = await conn.ExecuteScalarAsync<int>("SELECT count(1) FROM[dbo].[tag] a"); //conn.Close(); //conn = new SqlConnection("Data Source=.;Integrated Security=True;Initial Catalog=cms;Pooling=true;Max Pool Size=11"); //conn.Open(); var items = await conn.QueryAsync("SELECT TOP 20 a.[id], a.[parent_id], a.[name] FROM[dbo].[tag] a"); conn.Close(); return new { count, items }; }
連線池最大分別為:10,11
使用 apache ab 命令列測試上面兩個介面
ab -c 10 -n 1000 -s 6000 測試結果差不多。
-c 100 時,vs_dapper直接掛了,vs_gen沒影響(使用了SafeObjectPool)
實踐證明ado.net過於暴露,突然的高併發招架不住。
SafeObjectPool
它是一個物件池,可用於控制任何資源緊缺的物件,使用容器化管重複使用提升效能,有序的排隊獲取,使用完後歸還資源。
與ado.net連線池不同的地方,SafeObjectPool 解決池用盡後,再請求不報錯,進行排隊等待機制,並且適用任何物件不侷限於資料庫連線物件。
SafeObjectPool 提供可用性檢查方法,比如 redisClient 不可用時,所有Get/GetAsync都將報錯,直到後臺服務檢查並恢復狀態。
使用方法
Install-Package SafeObjectPool
var pool = new SafeObjectPool.ObjectPool<MemoryStream>(10, () => new MemoryStream(), obj => {
if (DateTime.Now.Subtract(obj.LastGetTime).TotalSeconds > 5) {
// 物件超過5秒未活動,進行操作
}
});
var obj = pool.Get(); //借
pool.Return(obj); //歸還
//或者 using 自動歸還
using (var obj = pool.Get()) {
}
異常
- 同步獲取資源時 pool.Get()
【連線池名稱】狀態不可用,等待後臺檢查程式恢復方可使用。
SafeObjectPool.Get 獲取超時(10秒),設定 Policy.IsThrowGetTimeoutException 可以避免該異常。
- 非同步獲取資源時 pool.GetAsync()
【連線池名稱】狀態不可用,等待後臺檢查程式恢復方可使用。
SafeObjectPool.GetAsync 無可用資源且佇列過長,Policy.AsyncGetCapacity =10000。
- 後臺服務檢查可用性時
CheckAvailable 無法獲得資源,Pool: 0/10, Get Wait: 0, GetAsync Wait: 0
擴充套件現實
SQLServer連線池
var pool = new System.Data.SqlClient.SqlConnectionPool("名稱", connectionString, 可用時觸發的委託, 不可用時觸發的委託);
var conn = pool.Get();
try {
// 使用 ...
pool.Return(conn); //正常歸還
} catch (Exception ex) {
pool.Return(conn, ex); //發生錯誤時歸還
}
MySQL連線池
var pool = new MySql.Data.MySqlClient.MySqlConnectionPool("名稱", connectionString, 可用時觸發的委託, 不可用時觸發的委託);
var conn = pool.Get();
try {
// 使用 ...
pool.Return(conn); //正常歸還
} catch (Exception ex) {
pool.Return(conn, ex); //發生錯誤時歸還
}
PostgreSQL連線池
var pool = new Npgsql.NpgsqlConnectionPool("名稱", connectionString, 可用時觸發的委託, 不可用時觸發的委託);
var conn = pool.Get();
try {
// 使用 ...
pool.Return(conn); //正常歸還
} catch (Exception ex) {
pool.Return(conn, ex); //發生錯誤時歸還
}
結語
本文由 ado.net 連線池,衍生到 SafeObjectPool,基於 SafeObjectPool 現實了 SQLServer連線池、MySQL連線池、PostgreSQL連線池。還有很多連線物件,比如redis client、rpc client、各大訊息佇列client,都可以封裝起來。
謝謝觀看,支援開源思想和奉獻。