谈一谈对象池SafeObjectPool能干什么

前言

首先从ado.net的链接池开始了解,数据库操做一般是 new SqlConnection()、 Open()、 使用完后 Close(),整个过程至关耗时,特别是频繁建议套字接链接的过程。ado.net 驱动已经现实了链接池管理,Open() 等于申请链接,Close() 即归还资源。mysql

Open() 的时候有几种状况:git

一、有资源直接返回;github

二、无可用资源,且未超过池最大设置值时,建立资源并返回;redis

三、无可用资源,此时会等待设置秒数,若仍然未获取资源则报错;sql

链接的复用使性能成数倍提高,试想网站在某一时刻忽然爆增10万次,new 10万个SqlConnection对象显然会炸掉服务,建立,connect,disconnect,disponse,显然开销很大。数据库

虽然ado.net自带的链接池已经接近完美,但在某些场合还不够用,先来一个压力测试。apache

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 命令行测试上面两个接口app

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,均可以封装起来。

谢谢观看,支持开源思想和奉献。

SafeObjectPool github:https://github.com/2881099/SafeObjectPool

相关文章
相关标签/搜索