详解.NET中负载均衡的使用

2022-11-13 10:11:05 负载均衡 详解 net

一、简介

负载均衡(Load Balance),简称 LB,就是将并发的用户请求通过规则后平衡、分摊到多台服务器上进行执行,以此达到压力分摊、数据并行的效果。常见的算法也有许多随机、轮询、加权等,今天我们就使用 C# 来实现这几种算法,并讲解在实际项目中的使用。

二、应用场景

负载均衡算法在开发层面,使用场景其实并不多。通常在项目重构、转型、上线大版本新功能等,为了避免上线出现 Bug 应用功能 100% 的挂掉。可以在程序中使用负载均衡,将部分 Http 流量,打入项目中新的功能模块,然后进行监控,出现问题可以及时进行调整。

这样 AB 测试的场景,也可以在运维或者网关等其他层面实现流量分配。但现实是大多数公司项目因为一些原因没有这样的支持,这时开发就可以在项目中使用代码进行实现。

三、实际案例

有这样一个需求,电商系统中,有一个预估运费的微服务(ShippinGCharge )。此时上面领导来了需求,预估运费要改版,开发预估了一下改动不小。经过两周的奋斗 ShippingCharge 需求终于开发测试好了,此时要上线,但是掐指一算,万一有问题不就死翘翘了,而且还和钱相关。

此时负载均衡算法就派上用场了,我们可以让 10% 的流量打入这次的改动,可以先进行监控,可以再全部切过来。实际项目中,使用的肯定是权重的,后面随机、轮询也简单进行介绍一下其实现。

假设在改动 ShippingCharge 时,没有修改旧的功能,是在 controller 下面,对 call business 层换成了这次需求的,这样我们就可以使用负载均衡,让 10% 的流量打入新的 business,其余的依然走老的 business。

四、算法实现

这里不会说的太精细,会将核心实现代码做介绍,实际项目中使用需要自己进行一下结合,举一反三哈

下面定义了一个 ServiceCenterModel 主要用作承载需要负载均衡的对象信息,可以是 call 下游的 url,也可以是程序内的某一算法标

4.1 随机

随机算法的先对来讲,较为简单一些,主要根据 Random 与 ServiceList 的数量结合实现。

如下:

/// <summary>
/// 随机
/// </summary>
public class RandomAlGorithm
{
    /// <summary>
    /// Random Function
    /// </summary>
    private static readonly Random random = new Random();

    /// <summary>
    /// serviceList
    /// </summary>
    /// <param name="serviceList">service url set</param>
    /// <returns></returns>
    public static string Get(List<ServiceCenterModel> serviceList)
    {
        if (serviceList == null)
            return null;

        if (serviceList.Count == 1)
            return serviceList[0].Service;

        // 返回一个小于所指定最大值的非负随机数
        int index = random.Next(serviceList.Count);

        string url = serviceList[index].Service;

        return url;
    }
}

模拟 10 次 http request,可以看到对OldBusiness、NewBusiness进行了随机的返回

public static void Main(string[] args)
{
    // 模拟从配置中心读取 Service 
    var serviceList = new List<ServiceCenterModel>()
    {
        new ServiceCenterModel { Service ="OldBusiness"},
        new ServiceCenterModel { Service ="NewBusiness"},
    };

    // 模拟 Http 请求次数
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(RandomAlgorithm.Get(serviceList));
    }
}

4.2 轮询

轮询的实现思路,将每次读取 ServiceList 的 Index 放到静态全局变量中,当到 ServiceList 最后一个时从0开始读取。

如下:

/// <summary>
/// 轮询
/// </summary>
public class PollingAlgorithm
{
    private static Dictionary<string, int> _serviceDic = new Dictionary<string, int>();
    private static SpinLock _spinLock = new SpinLock();

    /// <summary>
    /// Get URL From Service List
    /// </summary>
    /// <param name="serviceList">Service URL Set</param>
    /// <param name="serviceName">Service Name</param>
    /// <returns></returns>
    public static string Get(List<ServiceCenterModel> serviceList, string serviceName)
    {
        if (serviceList == null || string.IsNullOrEmpty(serviceName))
            return null;

        if (serviceList.Count == 1)
            return serviceList[0].Service;

        bool locked = false;
        _spinLock.Enter(ref locked);//获取
        int index = -1;
        if (!_serviceDic.ContainsKey(serviceName)) // Not Exist
            _serviceDic.TryAdd(serviceName, index);
        else
            _serviceDic.TryGetValue(serviceName, out index);
        string url = string.Empty;
        ++index;
        if (index > serviceList.Count - 1) //当前索引 > 最新服务最大索引
        {
            index = 0;
            url = serviceList[0].Service;
        }
        else
        {
            url = serviceList[index].Service;
        }
        _serviceDic[serviceName] = index;

        if (locked) //释放锁
            _spinLock.Exit();

        return url;
    }
}

模拟 10 次 http request,可以看到对OldBusiness、NewBusiness进行了轮询返回

public static void Main(string[] args)
{
    // 模拟从配置中心读取 Service 
    var serviceList = new List<ServiceCenterModel>()
    {
        new ServiceCenterModel { Service ="OldBusiness"},
        new ServiceCenterModel { Service ="NewBusiness"},
    };

    // 模拟 Http 请求次数
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(PollingAlgorithm.Get(serviceList, "ShippingChargeBusiness"));
    }
}

4.3 权重

权重的实现思路,将配置权重的 Service 按照数量放置在一个集合中,然后按照轮询的方式进行读取,需要注意的是这的 weight 只能配置大于 0 的整数。

如下:

/// <summary>
/// 权重
/// </summary>
public class WeightAlgorithm
{
    private static ConcurrentDictionary<string, WeightAlgorithmItem> _serviceDic = new ConcurrentDictionary<string, WeightAlgorithmItem>();
    private static SpinLock _spinLock = new SpinLock();

    public static string Get(List<ServiceCenterModel> serviceList, string serviceName)
    {
        if (serviceList == null)
            return null;

        if (serviceList.Count == 1)
            return serviceList[0].Service;

        bool locked = false;
        _spinLock.Enter(ref locked);//获取锁

        WeightAlgorithmItem weightAlgorithmItem = null;
        if (!_serviceDic.ContainsKey(serviceName))
        {
            weightAlgorithmItem = new WeightAlgorithmItem()
            {
                Index = -1,
                Urls = new List<string>()
            };
            BuildWeightAlgorithmItem(weightAlgorithmItem, serviceList);
            _serviceDic.TryAdd(serviceName, weightAlgorithmItem);
        }
        else
        {
            _serviceDic.TryGetValue(serviceName, out weightAlgorithmItem);
            weightAlgorithmItem.Urls.Clear();
            BuildWeightAlgorithmItem(weightAlgorithmItem, serviceList);
        }

        string url = string.Empty;
        ++weightAlgorithmItem.Index;
        if (weightAlgorithmItem.Index > weightAlgorithmItem.Urls.Count - 1) //当前索引 > 最新服务最大索引
        {
            weightAlgorithmItem.Index = 0;
            url = serviceList[0].Service;
        }
        else
        {
            url = weightAlgorithmItem.Urls[weightAlgorithmItem.Index];
        }
        _serviceDic[serviceName] = weightAlgorithmItem;

        if (locked) //释放锁
            _spinLock.Exit();

        return url;
    }

    private static void BuildWeightAlgorithmItem(WeightAlgorithmItem weightAlgorithmItem, List<ServiceCenterModel> serviceList)
    {
        serviceList.ForEach(service => //有几个权重就加几个实例
        {
            for (int i = 0; i < service.Weight; i++)
            {
                weightAlgorithmItem.Urls.Add(service.Service);
            }
        });
    }
}
public class WeightAlgorithmItem
{
    public List<string> Urls { get; set; }
    public int Index { get; set; }
}

模拟 10 次 http request,可以看到对 OldBusiness 返回了 9 次,NewBusiness 返回了一次

public static void Main(string[] args)
{
    // 模拟从配置中心读取 Service 
    var serviceList = new List<ServiceCenterModel>()
    {
        new ServiceCenterModel { Service ="OldBusiness",Weight = 9 },
        new ServiceCenterModel { Service ="NewBusiness",Weight = 1 },
    };

    // 模拟 Http 请求次数
    for (int i = 0; i < 10; i++)
    {
        Console.WriteLine(WeightAlgorithm.Get(serviceList, "ShippingChargeBusiness"));
    }
}

到此这篇关于详解.net中负载均衡的使用的文章就介绍到这了,更多相关 .NET负载均衡 内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!

相关文章