实现
软件:nginx、haproxy
硬件:f5服务器
硬件
第一台服务 权重3
第二台服务 权重2
第三台服务 权重1
随机算法
普通方式
那么就维护一个list 放入3个第一台服务、2个第二台服务、1个第一台服务,然后根据list的大小生成随机数取出服务器地址访问,这就是普通的轮询随机算法
优化后
把所有的轮询权重加在一起,3+2+1然后再次范围内从0开始生成随机数,然后判断它属于哪个范围内<3则是第一台服务器,else<5则是第二台服务,否则是第三台服务
注意:如果权重相同则直接按照服务器个数随机即可
轮询算法
普通方式
那么就维护一个list 放入3个第一台服务、2个第二台服务、1个第一台服务,然后按照顺序依次取地址,和随机差不多只是不产生随机数直接一个数++方式取服务地址
平滑加权轮询算法
一台服务器会连续获得请求,应该分散开来,例如 111223不如改为123121这样所以就需要实现动态权重
- weight为服务器权重 3:2:1
- currentWeight动态权重 初始为0:0:0
- 每一次新请求 currentWeight+=weight
currentWeight += weight | max | return | max所在位置=max-权重总和(1+2+3) | |
---|---|---|---|---|
第一次 | 3:2:1 | 3 | 第一台服务器地址 | -3:2:1 |
第二次 | 0:4:2 | 4 | 第二台服务器地址 | 0:-2:2 |
第三次 | 3:0:3 | 3 | 第一台服务器地址 | -3:-2:3 |
基于哈希环的一致性哈希算法
注:这种算法可以使某个请求一直访问一个url地址,如果hash散列算法越均匀则容错负载能力越强
假设上图中4节点挂掉,那么根据圆环的特性还有一致性的要求,那么3到1这部分的请求都需要由1承担,这样并不能做到负载均衡
如果增加虚拟节点并且互相分散开,那么这样带有虚拟节点的hash环当某一个节点挂掉了那么只会影响到前一个服务负担两份请求的任务(上图有问题不应该有顺序规律,hash算法进行散列后是无序的,也许节点一顺时针下一个是节点3-2不一定非要是4-1)
/**
* HashMath
*
* @author hanzhao
* @date 2020/11/19
*/
public class HashMath {
//存储hash的对应关系,key为hash值value对应实际的ip地址
private static TreeMap<Integer,String> hashApplication = new TreeMap<>();
//每个固定ip有多少个虚拟节点
private static final int nodes = 100;
//默认初始化的ip节点
private static List<String> ips;
static {
ips = new ArrayList<>();
ips.add("192.168.1.1");
ips.add("192.168.1.2");
ips.add("192.168.1.3");
ips.add("192.168.1.4");
//经过hash算法进行散列添加虚拟节点
for (String ip:ips) {
for (int i = 0; i < nodes; i++) {
Integer hashNum = hash(ip+i);
//hashApplication就相当于hash环将所有的节点分布到一个环上,很具hash散列算法平均的划分节点使其对应到指定的ip地址上及逆行负载均衡
hashApplication.put(hashNum,ip);
}
}
}
/**
* 将请求及逆行hash然后定位到环上的节点,取出节点对应的ip地址
* @param request
* @return
*/
public static String getServer(String request) {
//用相同的hash算法对请求进行hash,得出当前请求的hash值
Integer hashNumber = hash(request);
//找到大于请求hash值的子树然后找到第一个节点
SortedMap<Integer,String> subMap = hashApplication.tailMap(hashNumber);
//因为是个圆环所以大于最后一个节点的请求hash值由第一个节点对应的请求处理
if(subMap.size()<1){
return hashApplication.get(hashApplication.firstKey());
}
Integer index = subMap.firstKey();
//然后取出这个hash的值对应的ip地址则完成了此次负载均衡
return hashApplication.get(index);
}
/**
* 主程序入口
* @param args
*/
public static void main(String[] args) {
Integer firstCount = 0;
Integer secondCount = 0;
Integer thredCount = 0;
Integer fourThCount = 0;
for (int i = 0; i < 6000; i++) {
String ip = getServer("request"+i);
switch (ip){
case "192.168.1.1":
firstCount++;
break;
case "192.168.1.2":
secondCount++;
break;
case "192.168.1.3":
thredCount++;
break;
case "192.168.1.4":
fourThCount++;
break;
}
}
System.out.println("第一台服务器命中:"+firstCount+"次");
System.out.println("第二台服务器命中:"+secondCount+"次");
System.out.println("第三台服务器命中:"+thredCount+"次");
System.out.println("第四台服务器命中:"+fourThCount+"次");
}
/**
* hash算法
* @param key
* @return
*/
public static Integer hash(String key) {
final int p = 16777619;
int hash = (int)2166136261L;
for (int i = 0; i < key.length(); i++)
hash = (hash ^ key.charAt(i)) * p;
hash += hash << 13;
hash ^= hash >> 7;
hash += hash << 3;
hash ^= hash >> 17;
hash += hash << 5;
// 如果算出来的值为负数则取其绝对值
if (hash < 0)
hash = Math.abs(hash);
return hash;
}
}
最小活跃数算法
最小活跃数算法一般为三台机器,每台机器接收了多少个连接或请求然后累计+1,处理完或者意外出错累计-1然后根据值大小分配请求和连接
参考文章和视频
https://cloud.tencent.com/developer/article/1648654
https://www.bilibili.com/video/BV1Xi4y177nq?p=1
https://cloud.tencent.com/developer/article/1084801 https://www.codeproject.com/Articles/56138/Consistent-hashing http://sundoctor.iteye.com/blog/2104321
https://www.dazhuanlan.com/2019/12/26/5e0405834b16e/