对于做开发的同学来说,负载均衡算法已经不陌生了,今天一起来盘点一下分布式系统中都是有哪些负载均衡算法以及它的优缺点;
1.轮询法(Round Robin)
思想: 将请求按顺序轮流地分配到后端服务器上,它均衡地对待后端每一台服务器,而不关心服务实际的连接数和当前系统的负载;
代码实现:
private List list = new CopyOnWriteArrayList();
private volatile Integer pos = 0;
{
list.add("127.0.0.1");
list.add("127.0.0.2");
list.add("127.0.0.3");
}
public String getServer() {
if (null == list || list.size() <= 0) {
return null;
}
String server = null;
synchronized (pos) {
if (pos >= list.size()) {
pos = 0;
}
server = list.get(pos++);
}
return server;
}
总结:
这种算法简单,依次转发,每个服务器的请求数量平均;
缺点:当集群中的服务器的性能有差别时,无法区别对待的情况下会造成资源浪费;
2.随机法(Random)
思想: 通过系统随机函数,根据后端服务器列表大小值来随机选取其中一台进行访问;随着调用量的增大,效果接近轮询算法;
代码实现:
private List list = new CopyOnWriteArrayList();
{
list.add("127.0.0.1");
list.add("127.0.0.2");
list.add("127.0.0.3");
list.add("127.0.0.4");
}
public String getServerRandom() {
if (null == list || list.size() <= 0) {
return null;
}
Random random = new Random();
String server = list.get(random.nextInt(list.size()));
return server;
}
总结:
算法虽然简单,但是在大请求量的情况下才能保证均衡
3.源地址哈希法(Hash)
思想: 源地址哈希的思想是获取客户端访问的ip地址,通过hash函数计算得到一个值,用该值从服务器列表中进行取模运算;当服务器列表不变时,同一ip总是请求到同一台服务器中;
代码实现:
private List list = new CopyOnWriteArrayList();
{
list.add("127.0.0.1");
list.add("127.0.0.2");
list.add("127.0.0.3");
list.add("127.0.0.4");
}
public String getServerHash(String hostIp) {
if (null == list || list.size() <= 0 || null == hostIp) {
return null;
}
int code = hostIp.hashCode();
int serverPos = list.size() % code;
return list.get(serverPos);
}
4.加权轮询法(Weight Round Robin)
思想: 与轮询算法相比,它加了权重,权重超高的服务,接收到有请求越多;
代码实现:
private ConcurrentMap hosts = new ConcurrentHashMap<>();
private volatile Integer pos = 0;
{
hosts.put("127.0.0.1", 1);
hosts.put("127.0.0.2", 2);
hosts.put("127.0.0.3", 2);
hosts.put("127.0.0.4", 1);
}
public String getServerRoundRobin() {
List list = new ArrayList<>();
for (Map.Entry entry : hosts.entrySet()) {
Integer value = entry.getValue();
for (int i = 0; i < value; i++) {
list.add(entry.getKey());
}
}
String server = null;
synchronized (pos) {
if (pos >= list.size()) {
pos = 0;
}
server = list.get(pos++);
}
return server;
}
5.加权轮随机(Weight Random)
思想: 是在随机的基础上,加上权值;
代码实现:
private ConcurrentMap hosts = new ConcurrentHashMap<>();
private volatile Integer pos = 0;
{
hosts.put("127.0.0.1", 1);
hosts.put("127.0.0.2", 2);
hosts.put("127.0.0.3", 2);
hosts.put("127.0.0.4", 1);
}
public String getServerRandomWeight() {
List list = new ArrayList<>();
for (Map.Entry entry : hosts.entrySet()) {
Integer value = entry.getValue();
for (int i = 0; i < value; i++) {
list.add(entry.getKey());
}
}
Random random = new Random();
String server = list.get(random.nextInt(list.size()));
return server;
}
上面两种实现加权的方式都是权重为几,就往list里面add几次,如果服务器数量之庞大,会导致list列表过大;有另外一种实现加权的方式,把每台服务和权重划分为一段,权重越大,占的段长越长:
代码实现:
private Map hosts = new ConcurrentHashMap<>();
private volatile Integer pos = 0;
{
hosts.put("127.0.0.1", 1);
hosts.put("127.0.0.2", 2);
hosts.put("127.0.0.3", 2);
hosts.put("127.0.0.4", 1);
}
public String getServerRandomWeight2() {
// 累加所有权重
int sum = 0;
for (Map.Entry entry : hosts.entrySet()) {
sum += entry.getValue();
}
Random random = new Random();
int index = random.nextInt(sum);
for (Map.Entry entry : hosts.entrySet()) {
Integer value = entry.getValue();
if(value >= index){
return entry.getKey();
}
index -= entry.getValue();
}
return null;
}
6.最小连接数(Least Connections)
思想: 最小连接数法是根据服务器当前的连接情况进行负载均衡的,它会选择一台连接数最少的机器来提供服务;
代码实现:
// key:机器ip value:当前访问量
private Map hosts = new ConcurrentHashMap<>();
private volatile Integer pos = 0;
{
hosts.put("127.0.0.1", 6);
hosts.put("127.0.0.2", 2);
hosts.put("127.0.0.3", 3);
hosts.put("127.0.0.4", 8);
}
public String getServerLeastConnection() {
// 寻找最小连接
int min = 0;
String key = null;
for (Map.Entry entry : hosts.entrySet()) {
if (entry.getValue() < min) {
min = entry.getValue();
key = entry.getKey();
}
}
hosts.put(key, min + 1);
return key;
}
注:文中代码实现不适合真实场景,只是为了简单易懂理解算法思想;