(更新时间)2021年5月30日 商城高并发秒杀系统(.NET Core版) 07-负载均衡组件的封装

举报
愚公搬代码 发表于 2021/10/20 00:34:13 2021/10/20
【摘要】 一:负载均衡组件的封装 /// <summary> /// 负载均衡ServiceCollection扩展 /// </summary> public static class ...

一:负载均衡组件的封装

/// <summary>
/// 负载均衡ServiceCollection扩展
/// </summary>
public static class LoadBalanceServiceCollectionExtensions
{
    /// <summary>
    /// 注册负载均衡
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceCollection AddLoadBalance(this IServiceCollection services)
    {
        AddLoadBalance(services, options => { });
        return services;
    }

    /// <summary>
    /// 注册负载均衡
    /// </summary>
    /// <param name="services"></param>
    /// <returns></returns>
    public static IServiceCollection AddLoadBalance(this IServiceCollection services, Action<LoadBalanceOptions> options)
    {

        services.Configure<LoadBalanceOptions>(options);
        // 1、注册到IOC容器
        services.AddSingleton<ILoadBalance, RandomLoadBalance>();

        return services;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31

二:配置选项

/// <summary>
/// 负载均衡选项
/// </summary>
public class LoadBalanceOptions
{
    public LoadBalanceOptions()
    {
        this.Type = "Random";
    }

    /// <summary>
    /// 负载均衡类型
    /// </summary>
    public string Type { set; get; }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

三:相关依赖的类文件

/// <summary>
/// 负载均衡抽象实现
/// </summary>
public abstract class AbstractLoadBalance : ILoadBalance
{
    static int CalculateWarmupWeight(int uptime, int warmup, int weight)
    {
        int ww = (int)((float)uptime / ((float)warmup / (float)weight));
        return ww < 1 ? 1 : (ww > weight ? weight : ww);
    }

    public ServiceNode Select(IList<ServiceNode> serviceUrls)
    {
        if (serviceUrls == null || serviceUrls.Count == 0)
            return null;
        if (serviceUrls.Count == 1)
            return serviceUrls[0];
        return DoSelect(serviceUrls);
    }

    /// <summary>
    /// 子类去实现
    /// </summary>
    /// <param name="serviceUrls"></param>
    /// <returns></returns>
    public abstract ServiceNode DoSelect(IList<ServiceNode> serviceUrls);

    /// <summary>
    /// 获取权重
    /// </summary>
    /// <returns></returns>
    protected int GetWeight()
    {
        int weight = 100;
        if (weight > 0)
        {
            long timestamp = 0L;
            if (timestamp > 0L)
            {
                int uptime = (int)(DateTime.Now.ToFileTimeUtc() - timestamp);
                int warmup = 10 * 60 * 1000;
                if (uptime > 0 && uptime < warmup)
                {
                    weight = CalculateWarmupWeight(uptime, warmup, weight);
                }
            }
        }
        return weight;
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
/// <summary>
/// Hash一致性算法
/// </summary>
public class ConsistentHashLoadBalance : AbstractLoadBalance
{
    private ConcurrentDictionary<string, ConsistentHashSelector> selectors = new ConcurrentDictionary<string, ConsistentHashSelector>();

    public override ServiceNode DoSelect(IList<ServiceNode> serviceUrls)
    {
        string key = serviceUrls[0].Url;
        int identityHashCode = serviceUrls.GetHashCode();
        ConsistentHashSelector selector = (ConsistentHashSelector)selectors[key];
        if (selector == null || selector.GetHashCode() != identityHashCode)
        {
            selectors.TryAdd(key, new ConsistentHashSelector(serviceUrls, "", identityHashCode));
            selector = (ConsistentHashSelector)selectors[key];
        }
        return selector.Select(key);
    }

    private class ConsistentHashSelector
    {

        private SortedDictionary<long, ServiceNode> virtualServiceUrls;

        private int replicaNumber;

        private int identityHashCode;

        private int[] argumentIndex;
        private IList<ServiceNode> serviceUrls;
        private string v;

        public ConsistentHashSelector(IList<ServiceNode> serviceUrls, string methodName, int identityHashCode)
        {
            this.virtualServiceUrls = new SortedDictionary<long, ServiceNode>();
            this.identityHashCode = identityHashCode;
            string url = serviceUrls[0].Url;
            this.replicaNumber = 160;// 默认多少个虚拟节点
            string[] index = new string[] { };
            argumentIndex = new int[index.Length];
            for (int i = 0; i < index.Length; i++)
            {
                argumentIndex[i] = int.Parse(index[i]);
            }
            foreach (ServiceNode serviceUrl in serviceUrls)
            {
                string address = serviceUrl.Url;
                for (int i = 0; i < replicaNumber / 4; i++)
                {
                    byte[] digest = md5(address + i);
                    for (int h = 0; h < 4; h++)
                    {
                        long m = hash(digest, h);
                        virtualServiceUrls.Add(m, serviceUrl);
                    }
                }
            }
        }

        public ServiceNode Select(string url)
        {
            string key = url;
            byte[] digest = md5(key);
            return selectForKey(hash(digest, 0));
        }

        private string toKey(Object[] args)
        {
            StringBuilder buf = new StringBuilder();
            foreach (int i in argumentIndex)
            {
                if (i >= 0 && i < args.Length)
                {
                    buf.Append(args[i]);
                }
            }
            return buf.ToString();
        }

        private ServiceNode selectForKey(long hash)
        {
            KeyValuePair<long, ServiceNode> entry = virtualServiceUrls.GetEnumerator().Current;
            if ("null".Equals(entry) || "".Equals(entry))
            {
                entry = virtualServiceUrls.GetEnumerator().Current;
            }
            return entry.Value;
        }

        private long hash(byte[] digest, int number)
        {
            return (((long)(digest[3 + (number * 4)] & 0xFF) << 24)
                    | ((long)(digest[2 + number * 4] & 0xFF) << 16)
                    | ((long)(digest[1 + number * 4] & 0xFF) << 8)
                    | (digest[number * 4] & 0xFF))
                    & 0xFFFFFFFFL;
        }

        private byte[] md5(string value)
        {
            var hashed = EncryptProvider.Md5(value);
            byte[] bytes = Encoding.UTF8.GetBytes(hashed);
            return bytes;
        }

    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
/// <summary>
/// 服务负载均衡
/// </summary>
public interface ILoadBalance
{
    /// <summary>
    /// 服务选择
    /// </summary>
    /// <param name="serviceUrls"></param>
    /// <returns></returns>
    ServiceNode Select(IList<ServiceNode> serviceUrls);
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
/// <summary>
/// 最少活跃数算法
/// 核心在于记住每个url的状态
/// </summary>
class LeastActiveLoadBalance : AbstractLoadBalance
{
    private readonly Random random = new Random();

    public override ServiceUrl DoSelect(IList<ServiceUrl> serviceUrls)
    {
        int length = serviceUrls.Count; // Number of invokers
        int leastActive = -1; // The least active value of all invokers
        int leastCount = 0; // The number of invokers having the same least active value (leastActive)
        int[] leastIndexs = new int[length]; // The index of invokers having the same least active value (leastActive)
        int totalWeight = 0; // The sum of with warmup weights
        int firstWeight = 0; // Initial value, used for comparision
        bool sameWeight = true; // Every invoker has the same weight value?
        for (int i = 0; i < length; i++)
        {
            ServiceUrl serviceUrl = serviceUrls[i];
            int active = 10; // Active number(活跃数,是状态模式)
            int afterWarmup = GetWeight(); // Weight
            if (leastActive == -1 || active < leastActive)
            { // Restart, when find a invoker having smaller least active value.
                leastActive = active; // Record the current least active value
                leastCount = 1; // Reset leastCount, count again based on current leastCount
                leastIndexs[0] = i; // Reset
                totalWeight = afterWarmup; // Reset
                firstWeight = afterWarmup; // Record the weight the first invoker
                sameWeight = true; // Reset, every invoker has the same weight value?
            }
            else if (active == leastActive)
            { // If current invoker's active value equals with leaseActive, then accumulating.
                leastIndexs[leastCount++] = i; // Record index number of this invoker
                totalWeight += afterWarmup; // Add this invoker's weight to totalWeight.
                                            // If every invoker has the same weight?
                if (sameWeight && i > 0
                        && afterWarmup != firstWeight)
                {
                    sameWeight = false;
                }
            }
        }
        // assert(leastCount > 0)
        if (leastCount == 1)
        {
            // If we got exactly one invoker having the least active value, return this invoker directly.
            return serviceUrls[leastIndexs[0]];
        }
        if (!sameWeight && totalWeight > 0)
        {
            // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
            int offsetWeight = random.Next(totalWeight) + 1;
            // Return a invoker based on the random value.
            for (int i = 0; i < leastCount; i++)
            {
                int leastIndex = leastIndexs[i];
                offsetWeight -= GetWeight();
                if (offsetWeight <= 0)
                    return serviceUrls[leastIndex];
            }
        }
        // If all invokers have the same weight value or totalWeight=0, return evenly.
        return serviceUrls[leastIndexs[random.Next(leastCount)]];
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
/// <summary>
/// 加权随机算法
/// </summary>
public class RandomLoadBalance : AbstractLoadBalance
{
    private readonly Random random = new Random();

    public override ServiceNode DoSelect(IList<ServiceNode> serviceUrls)
    {
        int length = serviceUrls.Count; // Number of serviceUrls
        int totalWeight = 0; // The sum of weights
        bool sameWeight = true; // Every serviceUrls has the same weight?
        for (int i = 0; i < length; i++)
        {
            int weight = GetWeight();
            totalWeight += weight; // Sum
            if (sameWeight && i > 0
                    && weight != GetWeight())
            {
                sameWeight = false;
            }
        }
        if (totalWeight > 0 && !sameWeight)
        {
            // If (not every invoker has the same weight & at least one invoker's weight>0), select randomly based on totalWeight.
            int offset = random.Next(totalWeight);
            // Return a invoker based on the random value.
            for (int i = 0; i < length; i++)
            {
                offset -= GetWeight();
                if (offset < 0)
                {
                    return serviceUrls[i];
                }
            }
        }
        // If all invokers have the same weight value or totalWeight=0, return evenly.
        return serviceUrls[random.Next(length)];
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
/// <summary>
/// 轮询算法
/// </summary>
public class RoundRobinLoadBalance : AbstractLoadBalance
{
    private static int RECYCLE_PERIOD = 60000;

    public class WeightedRoundRobin
    {
        private static int weight;
        private static long current = 0;
        private static long lastUpdate;
        public static int GetWeight()
        {
            return weight;
        }
        public static void SetWeight(int weight)
        {
            WeightedRoundRobin.weight = weight;
            current = Interlocked.Add(ref current, 0);
        }
        public static long IncreaseCurrent()
        {
            return Interlocked.Add(ref current, weight);
        }
        public static void Sel(int total)
        {
            Interlocked.Add(ref current, -1 * total);
        }
        public static long GetLastUpdate()
        {
            return lastUpdate;
        }
        public static void setLastUpdate(long lastUpdate)
        {
            WeightedRoundRobin.lastUpdate = lastUpdate;
        }
    }

    private ConcurrentDictionary<string, ConcurrentDictionary<string, WeightedRoundRobin>> methodWeightMap = new ConcurrentDictionary<string, ConcurrentDictionary<string, WeightedRoundRobin>>();
    private AtomicBoolean updateLock = new AtomicBoolean();


    /**
     * get invoker addr list cached for specified invocation
     * <p>
     * <b>for unit test only</b>
     * 
     * @param invokers
     * @param invocation
     * @return
    protected ICollection<string> getInvokerAddrList(IList<ServiceNode> serviceUrls)
    {
        string key = serviceUrls[0].Url;
        ConcurrentDictionary<string, WeightedRoundRobin> map = methodWeightMap[key];
        if (map != null)
        {
            return map.Keys;
        }
        return null;
    }
    public override ServiceNode DoSelect(IList<ServiceNode> serviceUrls)
    {
        string key = serviceUrls[0].Url;
        ConcurrentDictionary<string, WeightedRoundRobin> map = methodWeightMap[key];
        if (map == null)
        {
            methodWeightMap.TryAdd(key, new ConcurrentDictionary<string, WeightedRoundRobin>());
            map = methodWeightMap[key];
        }
        int totalWeight = 0;
        long maxCurrent = long.MaxValue;
        long now = DateTime.Now.ToFileTimeUtc();
        ServiceNode serviceUrl = null;
        WeightedRoundRobin selectedWRR = null;
        foreach (ServiceNode url in serviceUrls)
        {
            string identifyString = url.Url;
            WeightedRoundRobin weightedRoundRobin = map[identifyString];
            int weight = GetWeight();
            if (weight < 0)
            {
                weight = 0;
            }
            if (weightedRoundRobin == null)
            {
                weightedRoundRobin = new WeightedRoundRobin();
                weightedRoundRobin.SetWeight(weight);
                map.TryAdd(identifyString, weightedRoundRobin);
                weightedRoundRobin = map[identifyString];
            }
            if (weight != weightedRoundRobin.GetWeight())
            {
                //weight changed
                weightedRoundRobin.SetWeight(weight);
            }
            long cur = weightedRoundRobin.IncreaseCurrent();
            weightedRoundRobin.SetLastUpdate(now);
            if (cur > maxCurrent)
            {
                maxCurrent = cur;
                serviceUrl = url;
                selectedWRR = weightedRoundRobin;
            }
            totalWeight += weight;
        }
        if (!updateLock.get() && serviceUrls.Count != map.Count)
        {
            if (updateLock.compareAndSet(false, true))
            {
                try
                {
                    // copy -> modify -> update reference
                    ConcurrentDictionary<String, WeightedRoundRobin> newMap = new ConcurrentDictionary<String, WeightedRoundRobin>();
                   // newMap.TryUpdate(map);
                    IEnumerator<string> it = newMap.Keys.GetEnumerator();
                    while (it.MoveNext())
                    {
                        
                        if (now - newMap[it.Current].GetLastUpdate() > RECYCLE_PERIOD)
                        {
                            it.Dispose();
                        }
                    }
                    methodWeightMap.TryAdd(key, newMap);
                }
                finally
                {
                    updateLock.set(false);
                }
            }
        }
        if (serviceUrl != null)
        {
            selectedWRR.Sel(totalWeight);
            return serviceUrl;
        }
        // should not happen here
        return serviceUrls[0];
    }
}

  
 
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52
  • 53
  • 54
  • 55
  • 56
  • 57
  • 58
  • 59
  • 60
  • 61
  • 62
  • 63
  • 64
  • 65
  • 66
  • 67
  • 68
  • 69
  • 70
  • 71
  • 72
  • 73
  • 74
  • 75
  • 76
  • 77
  • 78
  • 79
  • 80
  • 81
  • 82
  • 83
  • 84
  • 85
  • 86
  • 87
  • 88
  • 89
  • 90
  • 91
  • 92
  • 93
  • 94
  • 95
  • 96
  • 97
  • 98
  • 99
  • 100
  • 101
  • 102
  • 103
  • 104
  • 105
  • 106
  • 107
  • 108
  • 109
  • 110
  • 111
  • 112
  • 113
  • 114
  • 115
  • 116
  • 117
  • 118
  • 119
  • 120
  • 121
  • 122
  • 123
  • 124
  • 125
  • 126
  • 127
  • 128
  • 129
  • 130
  • 131
  • 132
  • 133
  • 134
  • 135
  • 136
  • 137
  • 138
  • 139
  • 140
  • 141

文章来源: codeboy.blog.csdn.net,作者:愚公搬代码,版权归原作者所有,如需转载,请联系作者。

原文链接:codeboy.blog.csdn.net/article/details/117396094

【版权声明】本文为华为云社区用户转载文章,如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。