.Net 下的数据库主从分离以及简单的几种负载均衡策略代码实现 (下)

前言:

延续上一篇博客写的 数据库主从分离配置,本篇简单介绍一下代码层面的负载均衡策略。

在上篇文章中,已经实现了两个数据库间的实时同步,那么对于代码层面,就需要根据T-SQL语句的操作是读还是写,来链接不同的数据库。

1、创建一个枚举 LibraDbBehaviorEnum

/// <summary>
/// 数据库操作的行为
/// </summary>
public enum LibraDbBehaviorEnum
{
    Read,
    Write
}

2、创建模型 LibraConnectionStringModel

用来存储每个链接对象配置

/// <summary>
/// 每个数据库链接模型
/// </summary>
public class LibraConnectionStringModel
{
    /// <summary>
    /// 当前连接字符串
    /// </summary>
    public string ConnectionString { get; set; }

    /// <summary>
    /// 权重数值 Weighing模式可用
    /// </summary>
    public int WeighingNumber { get; set; }

    /// <summary>
    /// 请求次数
    /// </summary>
    internal int AskCount { get; set; } = 0;

    /// <summary>
    /// 最近10次耗时 供压力策略使用的
    /// </summary>
    internal double[] TimeConsume { get; set; } = new double[10];
}

3、创建枚举 LibraStrategyReaderEnum

用来标识负载均衡的读策略,提供四种策略供参考。

public enum LibraStrategyReaderEnum
{
    /*
        读取负载均衡实现方式:
        1、轮询策略
        2、随机策略
        3、权重策略
        4、压力策略
            a) 可根据近百次响应时间的平均来分配
            b) 根据服务器硬件实时监控来分配
            c) 根据当前每台服务器的数据库链接数来分配
        5 ...
    */
    /// <summary>
    /// 轮询平均策略
    /// </summary>
    [Description("轮询策略")]
    RoundRobin,
    /// <summary>
    /// 随机平均策略
    /// </summary>
    [Description("随机策略")]
    Random,
    /// <summary>
    /// 服务器权重策略
    /// </summary>
    [Description("权重策略")]
    Weighing,
    /// <summary>
    /// 数据库压力策略
    /// 使用响应平均分配策略
    /// </summary>
    [Description("压力策略")]
    Pressure
}

4、创建数据库链接的管理类LibraConnectionStringPool

该类供操作读写数据库时,返回数据库的链接

internal class LibraConnectionStringPool
{
    internal static LibraConnectionStringModel WriteConnection;
    internal static List<LibraConnectionStringModel> ReadConnections;
    internal static LibraStrategyReaderEnum Strategy;

    /// <summary>
    /// 初始化连接字符串
    /// </summary>
    /// <param name="writeConnection">写链接的实体</param>
    /// <param name="strategy">读取策略</param>
    /// <param name="readConnections">读链接的实体集</param>
    internal static void PoolInitialization(LibraConnectionStringModel writeConnection, LibraStrategyReaderEnum strategy, LibraConnectionStringModel[] readConnections)
    {
        WriteConnection = writeConnection ?? throw new ArgumentNullException(nameof(writeConnection));
        ReadConnections = new List<LibraConnectionStringModel>();
        // 如果有配置读链接
        if (readConnections != null && readConnections.Length > 0)
        {
            // 设置负载均衡策略
            Strategy = strategy;
            // 如果是权重策略,需要根据权重创建对应权重值的链接。
            if (Strategy == LibraStrategyReaderEnum.Weighing)
            {
                foreach (var item in readConnections)
                {
                    for (int i = 0; i < item.WeighingNumber; i++)
                    {
                        ReadConnections.Add(item);
                    }
                }
            }
            else
            {
                ReadConnections = readConnections.ToList();
            }
        }
    }

    /// <summary>
    /// 根据操作数据库的目的,返回链接字符串
    /// </summary>
    /// <param name="behavior"></param>
    /// <param name="realtime">是否实时查询</param>
    /// <returns></returns>
    internal static LibraConnectionStringModel GetConnection(LibraDbBehaviorEnum behavior, bool realtime = false)
    {
        return behavior switch
        {
            LibraDbBehaviorEnum.Read => Dispatcher(realtime),
            LibraDbBehaviorEnum.Write => WriteConnection,
            _ => throw new Exception("错误的数据操作目的."),
        };
    }

    private static int _nIndex = 0;

    internal static LibraConnectionStringModel Dispatcher(bool realtime)
    {
        // 未配置读写分离,则自动返回主库.
        // 或者需要实时查询,则返回主库
        if (ReadConnections == null || ReadConnections.Count == 0 || realtime) return WriteConnection;
        switch (Strategy)
        {
            case LibraStrategyReaderEnum.RoundRobin:
                {
                    var readModel = ReadConnections[_nIndex++ % ReadConnections.Count];
                    if (_nIndex == ReadConnections.Count) _nIndex = 0;
                    return readModel;
                }
            case LibraStrategyReaderEnum.Random:
            case LibraStrategyReaderEnum.Weighing:
                {
                    var readModel = ReadConnections[new Random(_nIndex++).Next(0, ReadConnections.Count)];
                    if (_nIndex == ReadConnections.Count) _nIndex = 0;
                    return readModel;
                }
            case LibraStrategyReaderEnum.Pressure:
                {
                    // 压力策略下,自动获取最小耗时的连接
                    var sumMaxTimeConsume = ReadConnections.Select(r => r.TimeConsume.Sum()).Min();
                    return ReadConnections.Where(r => r.TimeConsume.Sum() == sumMaxTimeConsume).First();
                }
            // 未配置策略则自动返回主库
            default: return WriteConnection;
        }
    }
}

即使已经配置读写分离,但是也会出现需要查询实时状态下的数据,这时候提供了realtime用来返回写链接。

在上方代码中,权重策略与随机策略的逻辑是相同的。不同的是,在初始化配置中,权重策略下,会根据权重值新增了对应多个相同链接。目的在于随机Random下出现的比值加大。

压力策略,需要根据每次使用链接操作查询的前后进行耗时记录。这里提供简单的实现:

/// <summary>
/// 执行SQL语句的操作
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="sql"></param>
/// <param name="func"></param>
/// <param name="parameter"></param>
/// <returns></returns>
public static T Execute<T>(string sql, LibraDbBehaviorEnum behavior, Func<SqlCommand, T> func, bool realtime, params SqlParameter[] parameter)
{
	// 去获取对应的链接字符串对象
    var connectionModel = LibraConnectionStringPool.GetConnection(behavior, realtime);
    using SqlConnection connection = new SqlConnection(connectionModel.ConnectionString);
    SqlCommand command = new SqlCommand(sql, connection);
    if (parameter != null && parameter.Length > 0)
        command.Parameters.AddRange(parameter);
    connection.Open();
    // 执行前开启计时器
    _watch.Restart();
    var result = func.Invoke(command);
    // 执行完结束计时器
    _watch.Stop();
    // 将计时器耗时结果记录到对应链接字符串对象中
    connectionModel.TimeConsume[connectionModel.AskCount++ % connectionModel.TimeConsume.Length] = _watch.Elapsed.TotalMilliseconds;
    connection.Close();
    return result;
}

至此,数据库的读写分离负载均衡已完成。测试结果就不放上来了。

另外,根据所学知识以及网上教程,写了一个小小的ORM实现框架。 github地址:Libra.orm ,欢迎各位大佬多多指教。

最后,我尽可能详细的去解释介绍如何实现,如果在本文中有错漏之处,请各位大佬们多多指教。如果我的文章能帮到你,也请各位不吝点个赞点个收藏,如果对文中代码有疑问,也请下方评论。谢谢各位看官。

  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值