考虑一种情况,一组机器来提供一个服务,客户端要以相同的机会访问各台机器,而且其中一台机器负载过高的时候,要减少对这台服务器的访问,直到它的负载降低下来,而且如果我们添加了一台新的服务器,要把客户端的请求也均衡到这台新机器上。
摘要:
考虑一种情况,一组机器来提供一个服务,客户端要以相同的机会访问各台机器,而且其中一台机器负载过高的时候,要减少对这台服务器的访问,直到它的负载降低下来,而且如果我们添加了一台新的服务器,要把客户端的请求也均衡到这台新机器上。
思路及分析:
说到负载均衡,多半会用到哈希算法,比如说我们有a,b,c三台机器,我们会用一个很大的盒子去放这3台机器,比如这个盒子有10个格子,那我们这三台机器要均匀的放到各个格子里,如下:
1-a,2-b,3-c,4-a,5-b,6-c,7-a,8-b,9-c,10-a
我们要实现一个相对均匀分布的随机算法,每次产生一个1-10的随机数,而且产生了100个随机数的话,选中a和b和c的几率接近1:1:1。
如果a机器宕机了,我们要告诉负载均衡服务器,当随机数落在放a的格子上的时候,我们要用一种过载处理机制选择b和c里面的一台没有过载的机器。
最后,如果我们新添加一台服务器,要重新把所有机器均匀的放在大盒子里,然后重新启用分配策略。
实现:
我们写一个抽象类HashBase,它包含两个抽象方法,GetRandom用来产生一个随机数,可以在非抽象类里实现自己的随机数产生算法,SelectOtherUnLoadWarningItem用来在某机器负载过高或者宕机的时候把请求到该机的请求路由到别的负载低的机器。
有四个公开方法,作为对外公开的接口GetItem用来获取一台机器的服务地址,SetLoadWarning用来告诉负载均衡服务器某台服务器过载了,UnsetLoadWarning用来通知负载均衡服务器某台服务器不过载了,ReConfig用来在添加或者摘除一台服务器的时候重新配置哈希策略。
最后我们写了个SimpleHashImp类来继承HashBase,我们用Random类来产生随机数,并且在某台机器负载高的时候把请求随机路由到另外一台机器上。
代码实现:
HashBase
using System;
using System.Collections.Generic;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace HashTest
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
public abstract class HashBase<T>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
protected fields#region protected fields
protected int _maxHashItemCount = 0;
protected object _syncRoot = new object();
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
protected Dictionary<int, T> _dict = null;
protected Dictionary<T, bool> _dictLoadWarning = null;
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
protected abstract methods#region protected abstract methods
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
protected abstract int GetRandom();
protected abstract T SelectOtherUnLoadWarningItem(T ret);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
public contructs#region public contructs
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public HashBase(int maxHashItemCount, IList<T> items)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
init(maxHashItemCount, items);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
public virtual methods#region public virtual methods
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public virtual void ReConfig(int maxHashItemCount, IList<T> items)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
init(maxHashItemCount, items);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
protected methods#region protected methods
protected void init(int maxHashItemCount, IList<T> items)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
lock (_syncRoot)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_maxHashItemCount = maxHashItemCount;
_dict = new Dictionary<int, T>(maxHashItemCount);
for (int i = 0; i < _maxHashItemCount; i++)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
T item = default(T);
for (int j = items.Count; j >= 0; j--)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (i % items.Count == j)
item = items[j];
}
_dict.Add(i, item);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
_dictLoadWarning = new Dictionary<T, bool>(items.Count);
foreach (T item in items)
_dictLoadWarning.Add(item, false);
}
}
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
public methods#region public methods
public T GetItem()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
int rdn = GetRandom();
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
lock (_syncRoot)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
T ret = _dict[rdn];
if (_dictLoadWarning[ret])
ret = SelectOtherUnLoadWarningItem(ret);
return ret;
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void SetLoadWarning(T t)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
lock(_syncRoot) _dictLoadWarning[t] = true;
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public void UnsetLoadWarning(T t)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
lock (_syncRoot) _dictLoadWarning[t] = false;
}
#endregion
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
SimpleHashImp
using System;
using System.Collections.Generic;
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
namespace HashTest
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
class SimpleHashImp : HashBase<string>
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
private fields#region private fields
Random _random = null;
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
contructs#region contructs
public SimpleHashImp(int seed, int maxHashItemCount, IList<string> items)
: base(maxHashItemCount, items)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
_random = new Random(seed);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
#endregion
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedSubBlock.gif)
overrid methods#region overrid methods
protected override int GetRandom()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
return _random.Next(_maxHashItemCount);
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
protected override string SelectOtherUnLoadWarningItem(string ret)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
foreach (KeyValuePair<string, bool> item in _dictLoadWarning)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (!item.Value)
return item.Key;
}
throw new ApplicationException("503");
}
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
public override void ReConfig(int maxHashItemCount, IList<string> items)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
base.init(maxHashItemCount, items);
}
#endregion
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
测试代码
using System;
using System.Collections.Generic;
namespace HashTest
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedBlockStart.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/ContractedBlock.gif)
{
static class Program
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
/**//// <summary>
/// The main entry point for the application.
/// </summary>
[STAThread]
static void Main()
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
List<string> items = new List<string>();
items.Add("a");
items.Add("b");
items.Add("c");
HashBase<string> hash = new SimpleHashImp(10, 20, items);
![](https://www.cnblogs.com/Images/OutliningIndicators/InBlock.gif)
for (int i = 0; i < 100; i++)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
if (i == 10)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
hash.SetLoadWarning("a");
Console.WriteLine("机器a宕机了。。。");
}
if (i == 20)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
hash.UnsetLoadWarning("a");
Console.WriteLine("机器a已经恢复运行");
}
Console.WriteLine(hash.GetItem());
if (i == 30)
![](https://www.cnblogs.com/Images/OutliningIndicators/ExpandedSubBlockStart.gif)
{
items.Add("d");
hash.ReConfig(100, items);
Console.WriteLine("添加了一台新机器d,并扩大了散列集合");
}
System.Threading.Thread.Sleep(1000);
}
}
}
}
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
![](https://www.cnblogs.com/Images/OutliningIndicators/None.gif)
其它分析:
1、负载均衡服务器本身是个单点,如果它本身也要支持流量负载的话,要把它本身的状态放到数据库里。
2、在某台机器宕机的时候把请求路由到其它机器的算法可以写的更智能一些,目前的时候只是在其它机器里找一台没有过载的机器,如果都过载就给客户端返回503应答,就是所有服务器都忙,不至于让更多的请求把服务器压跨,只是让新的请求进不来。
3、产生随机数的算法一定要选个比较均匀分布的。
4、由于考虑了可能动态扩容机器,所以代码里好多地方加了lock语句,不知道.net里的lock语句到底支持多大吞吐量,可以换成其它方式来处理扩容,比如用新旧两套处理策略,旧的一直保持到没有新的请求过来,新来的请求访问新的策略,这时候可能短时间内不均衡,但每套策略都运行的很快,没有锁。