C#实现平滑加权轮询WeightedRoundRobin

C#实现平滑加权轮询

作者:NewcatsHuang
时间:2022-01-22
完整代码:Github传送门

代码

WeightedRoundRobinHelper.cs

/// <summary>
/// 平滑加权轮询(需要实例化为单例)
/// </summary>
/// <typeparam name="T">节点值类型</typeparam>
public class WeightedRoundRobinHelper<T>
{
    #region 字段
    /// <summary>
    /// 最大公约数
    /// </summary>
    private readonly int _gcd = 0;

    /// <summary>
    /// 最大权重值
    /// </summary>
    private readonly int _maxWeight = 0;

    /// <summary>
    /// 节点数
    /// </summary>
    private readonly int _nodesCount = 0;

    /// <summary>
    /// 当前权重
    /// </summary>
    private int _currentWeight = 0;

    /// <summary>
    /// 上次选中的节点
    /// </summary>
    private int _lastChosenNode = -1;

    /// <summary>
    /// 锁
    /// </summary>
    private SpinLock _sLock = new SpinLock(true);

    /// <summary>
    /// 节点
    /// </summary>
    private readonly List<WeightedNode<T>> _nodes;
    #endregion

    /// <summary>
    /// 当前所有节点按权重正序排列之后序列化的json字符串的md5值(System.Text.Json的默认配置)
    /// </summary>
    public string Md5Value { get; set; }

    /// <summary>
    /// 构造函数
    /// </summary>
    /// <param name="nodes">节点</param>
    public WeightedRoundRobinHelper(List<WeightedNode<T>> nodes)
    {
        _nodes = nodes.OrderBy(q => q.Weight).ToList();
        _gcd = GetGcd(_nodes);
        _maxWeight = GetMaxWeight(_nodes);
        _nodesCount = _nodes.Count;
        Md5Value = GetMd5(_nodes);
    }

    /// <summary>
    /// 获取此次选择结果
    /// </summary>
    public WeightedNode<T> GetResult()
    {
        var isLocked = false;
        _sLock.Enter(ref isLocked);

        do
        {
            _lastChosenNode = (_lastChosenNode + 1) % _nodesCount;
            if (_lastChosenNode == 0)
            {
                _currentWeight -= _gcd;
                if (_currentWeight <= 0)
                {
                    _currentWeight = _maxWeight;
                }
            }
        } while (_nodes[_lastChosenNode].Weight < _currentWeight);

        if (isLocked)
        {
            _sLock.Exit(true);
        }

        return _nodes[_lastChosenNode];
    }

    /// <summary>
    /// 取权重的最大公约数(GreatestCommonDivisor)
    /// </summary>
    private int GetGcd(List<WeightedNode<T>> nodes)
    {
        int index = _lastChosenNode;
        if (index < 0)
            index = 0;

        int a = nodes[index].Weight;

        if (index >= _nodesCount - 1)
            index = -1;

        int b = nodes[index + 1].Weight;
        while (b != 0)
        {
            var t = b;
            b = a % b;
            a = t;
        }
        return a;
    }

    /// <summary>
    /// 取最大权重值
    /// </summary>
    private int GetMaxWeight(List<WeightedNode<T>> nodes)
    {
        int max = nodes.Max(n => n.Weight);
        return max;
    }

    /// <summary>
    /// 取所有节点的json字符串的md5值(System.Text.Json的默认配置)
    /// </summary>
    private string GetMd5(List<WeightedNode<T>> nodes)
    {
        string json = System.Text.Json.JsonSerializer.Serialize(nodes);
        return EncryptHelper.MD5By32(json);
    }
}

/// <summary>
/// 权重节点
/// </summary>
public class WeightedNode<T>
{
    /// <summary>
    /// 节点值
    /// </summary>
    public T Value { get; set; }

    /// <summary>
    /// 初始权重
    /// </summary>
    public int Weight { get; set; }
}

测试

class Program
{
    static void Main(string[] args)
    {
        var dic = new ConcurrentDictionary<string, int>();

        var selector = new WeightedRoundRobinHelper<string>(new List<WeightedNode<string>>
        {
            new WeightedNode<string>(){ Value="1111111111111111", Weight=7},
            new WeightedNode<string>(){ Value="2222222222222222", Weight=4},
            new WeightedNode<string>(){ Value="3333333333333333", Weight=3},
            new WeightedNode<string>(){ Value="4444444444444444", Weight=2},
            new WeightedNode<string>(){ Value="5555555555555555", Weight=1},
        });

        Parallel.For(1, 100, (n) =>
        {
            var s = selector.GetResult();
            var key = $"节点:{s.Value}, 权重:{s.Weight}";
            Console.WriteLine(key);
            dic.AddOrUpdate(key, 1, (k, v) => v + 1);
        });

        Console.WriteLine("\r\n");
        foreach (var kvp in dic)
        {
            Console.WriteLine($"{kvp.Key}  总计命中 {kvp.Value} 次");
        }
    }
}

结果

节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:3333333333333333, 权重:3
节点:1111111111111111, 权重:7
节点:4444444444444444, 权重:2
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:5555555555555555, 权重:1
节点:4444444444444444, 权重:2
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:5555555555555555, 权重:1
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:3333333333333333, 权重:3
节点:1111111111111111, 权重:7
节点:4444444444444444, 权重:2
节点:2222222222222222, 权重:4
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:4444444444444444, 权重:2
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:4444444444444444, 权重:2
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:5555555555555555, 权重:1
节点:4444444444444444, 权重:2
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:4444444444444444, 权重:2
节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:5555555555555555, 权重:1
节点:4444444444444444, 权重:2
节点:3333333333333333, 权重:3
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:4444444444444444, 权重:2
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:5555555555555555, 权重:1
节点:4444444444444444, 权重:2
节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:2222222222222222, 权重:4
节点:4444444444444444, 权重:2
节点:3333333333333333, 权重:3
节点:2222222222222222, 权重:4
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:1111111111111111, 权重:7
节点:5555555555555555, 权重:1
节点:4444444444444444, 权重:2


节点:1111111111111111, 权重:7  总计命中 41 次
节点:3333333333333333, 权重:3  总计命中 17 次
节点:5555555555555555, 权重:1  总计命中 6 次
节点:2222222222222222, 权重:4  总计命中 23 次
节点:4444444444444444, 权重:2  总计命中 12 次

转载请注明出处,谢谢O(∩_∩)O

posted @ 2022-01-22 23:24  Newcats  阅读(167)  评论(0编辑  收藏  举报