消息队列10:为RabbitMq添加连接池

环境:

  • win11
  • rabbitmq-3.8.17
  • .net 6.0
  • RabbitMQ.Client 6.8.1
  • vs2022

安装RabbitMq环境参照:

问题:rabbitmq的c#客户端没有自带连接池,所以需要手动实现。

简易实现如下:

using RabbitMQ.Client;
using System.Collections.Concurrent;
using System.Text;

//测试调用
var channel = await ChannelPool.Default.GetChannelAsync("guid", () =>
{
    var factory = new ConnectionFactory()
    {
        HostName = "localhost",
        Port = 5672,
        UserName = "test",
        Password = "123456",
        VirtualHost = "/",
    };
    return Task.FromResult(factory.CreateConnection());
});
try
{
    var body = Encoding.UTF8.GetBytes("{\"Name\":\"tom\"}");
    channel.RawChannel.BasicPublish(exchange: "", routingKey: "test-queue", body: body);
}
finally
{
    channel.Return();
}


#region 连接池
/// <summary>
/// rabbitmq 本身没有提供链接池, 且 IModel 的建立和释放也需要发送请求, 所以建立 connection 轮训机制和 IModel 的缓冲池机制<br/>
/// 参考: <seealso href="https://www.rabbitmq.com/client-libraries/dotnet-api-guide#connection-and-channel-lifespan"/>
/// </summary>
public class ChannelPool
{
    public static ChannelPool Default = new(8, 50);
    private int connectionCount;
    private int channelCountPerConnection;
    public ChannelPool(int connectionCount = 1, int channelCountPerConnection = 5)
    {
        if (connectionCount > 0) this.connectionCount = connectionCount;
        if (channelCountPerConnection > 0) this.channelCountPerConnection = channelCountPerConnection;
    }
    public class ChannelItem
    {
        public int ConnectionIndex { get; set; }
        public HostItem CacheHost { get; set; }
        public IModel RawChannel { get; set; }
        public void Return() => CacheHost.ChannelPools[ConnectionIndex].Return(this);
    }
    public class HostItem
    {
        public SemaphoreSlim HostLocker { get; set; }
        public List<IConnection> Connections { get; set; }
        public int CurrentConnectionIndex { get; set; }
        public List<SemaphoreSlim> ConnectionLockers { get; set; }
        public List<EasyPool<ChannelItem>> ChannelPools { get; set; }
    }
    #region EasyPool
    public sealed class EasyPool<T> : IDisposable where T : class
    {
        private readonly ConcurrentBag<T> _pool;
        private readonly Func<T> _factory;
        private readonly int _maxCount;
        public EasyPool(Func<T> factory, int maxCount)
        {
            _factory = factory;
            _maxCount = maxCount;
            _pool = new ConcurrentBag<T>();
        }
        public T Get()
        {
            if (!_pool.TryTake(out var result)) return _factory();
            return result;
        }
        public bool Return(T item)
        {
            if (_pool.Count >= _maxCount)
            {
                if (item is IDisposable disposable) try { disposable.Dispose(); } catch { }
                return false;
            }
            _pool.Add(item);
            return true;
        }
        public void Dispose()
        {
            T result;
            while (_pool.TryTake(out result))
            {
                if (result is IDisposable disposable)
                {
                    try { disposable.Dispose(); } catch { }
                }
            }
        }
    }
    #endregion
    private readonly Dictionary<string, HostItem> _cacheHosts = new();
    public async Task<ChannelItem> GetChannelAsync(string key, Func<Task<IConnection>> connectionFactoty)
    {
        var connectionCount = this.connectionCount;
        var maxChannelCountPerConnection = this.channelCountPerConnection;
        //获取 HostItem
        if (!_cacheHosts.TryGetValue(key, out var cacheHost))
        {
            lock (_cacheHosts)
            {
                if (!_cacheHosts.TryGetValue(key, out cacheHost))
                {
                    cacheHost = new HostItem
                    {
                        HostLocker = new(1, 1),
                        CurrentConnectionIndex = -1,
                        Connections = new List<IConnection>(connectionCount),
                        ConnectionLockers = new List<SemaphoreSlim>(connectionCount),
                        ChannelPools = new List<EasyPool<ChannelItem>>(connectionCount),
                    };
                    for (int i = 0; i < connectionCount; i++)
                    {
                        cacheHost.Connections.Add(null);
                        cacheHost.ConnectionLockers.Add(new(1, 1));
                        var idx = i;
                        cacheHost.ChannelPools.Add(new EasyPool<ChannelItem>(() => new ChannelItem
                        {
                            ConnectionIndex = idx,
                            RawChannel = cacheHost.Connections[idx].CreateModel(),
                            CacheHost = cacheHost
                        }, maxChannelCountPerConnection));
                    }
                    _cacheHosts.Add(key, cacheHost);
                }
            }
        }
        //轮训得到连接索引
        await cacheHost.HostLocker.WaitAsync();
        int connectionIdx;
        try
        {
            connectionIdx = ++cacheHost.CurrentConnectionIndex;
            if (connectionIdx >= connectionCount) cacheHost.CurrentConnectionIndex = connectionIdx = connectionIdx % connectionCount;
        }
        finally
        {
            try { cacheHost.HostLocker.Release(); } catch { }
        }
        //检查是否初始化链接
        var conn = cacheHost.Connections[connectionIdx];
        if (conn == null)
        {
            var connectionLocker = cacheHost.ConnectionLockers[connectionIdx];
            await connectionLocker.WaitAsync();
            try
            {
                conn = cacheHost.Connections[connectionIdx];
                if (conn == null)
                {
                    conn = await connectionFactoty();
                    cacheHost.Connections[connectionIdx] = conn;
                }
            }
            finally
            {
                try { connectionLocker.Release(); } catch { }
            }
        }
        //得到 Channel
        return cacheHost.ChannelPools[connectionIdx].Get();
    }
}
#endregion
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

jackletter

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值