(更新时间)2021年5月30日 商城高并发秒杀系统(.NET Core版) 07-负载均衡组件的封装
【摘要】
一:负载均衡组件的封装
/// <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)