一、负载均衡维度
从负载均衡设备的角度来看,分为硬件负载均衡和软件负载均衡
- 硬件负载均衡 - 比如最常见的F5,还有array等,这些负载均衡是商业的负载均衡器,性能比较好,背后有专业的团队维护,可提供各种解决方案,但是价格昂贵。
- 软件负载均衡** - **nginx、lvs、tengine(阿里对nginx进行的改造)。
从负载均衡的技术角度来看,分为服务端负载均衡和客户端负载均衡
- 服务端负载均衡 - 当我们访问一个服务,请求会先到另外一台服务器,然后这台服务器,会把请求分发到提供这个服务的服务器,如果有多台服务器,就需要根据一定的算法去选择其中的一台。
- 客户端负载均衡 - 客户端服务负载均衡的概念貌似是有了服务治理才产生的,简单的来说,就是在一台服务器上维护着所有服务的ip,名称等信息,当我们在代码中访问一个服务,是通过一个组件访问的,这个组件会从那台服务器上渠道所有提供这个服务的服务器信息,然后通过一定的算法,选择一台服务器进行请求。
二、负载均衡算法
2.1、随机
2.1.1、普通随机
public class Demo {
public static List<String> serverList = new ArrayList<>();
static {
serverList.add("192.168.198.1");
serverList.add("192.168.198.2");
serverList.add("192.168.198.3");
}
public static String selectServer(){
Random random = new Random();
int index = random.nextInt(serverList.size());
return serverList.get(index);
}
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
System.out.println(selectServer());
}
}
}
2.2.2、加权随机
加权随机,虽然采用的还是随机算法,但是每台机械设置了权重,权重大的服务器应获得的概率大,权重小的获得概率小。通常有以下两种实现方式
实现方式一
public static List<String> serverList = new ArrayList<>();
//192.168.198.1权重是2
//192.168.198.2权重是1
//192.168.198.2权重是3
static {
//权重是2添加两次
serverList.add("192.168.198.1");
serverList.add("192.168.198.1");
serverList.add("192.168.198.2");
//权重是三添加三次
serverList.add("192.168.198.3");
serverList.add("192.168.198.3");
serverList.add("192.168.198.3");
//在初始化数据之后,可以进行一次shuffle
}
public static String selectServer(){
Random random = new Random();
int index = random.nextInt(serverList.size());
return serverList.get(index);
}
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
System.out.println(selectServer());
}
}
此种实现方式,如果权重很大,会占用内存,所以可以用以下方法优化
实现方式二
假如服务器A权重是2,服务器B权重是7,服务器C权重是1
public class Demo {
public static List<String> serverList = new ArrayList<>();
static int weight_A = 2;
static int weight_B = 7;
static int weight_C = 1;
static {
serverList.add("192.168.198.1");
serverList.add("192.168.198.2");
serverList.add("192.168.198.3");
}
public static String selectServer(){
int totalWeight = weight_A + weight_B + weight_C;
Random random = new Random();
int index = random.nextInt(totalWeight);
String ip = null;
if(index <= weight_A){
ip = serverList.get(0);
}else if(weight_A < index && index <= (weight_A + weight_B)){
ip = serverList.get(1);
}else {
ip = serverList.get(2);
}
return ip;
}
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
System.out.println(selectServer());
}
}
}
注意:这种算法有严重的缺陷,会导致某些服务空闲,某些服务压力过大,没有均衡分配请求。
2.2、轮询
2.2.1、完全轮询
public class Demo {
public static List<String> serverList = new ArrayList<>();
static int index = 0;
static {
serverList.add("192.168.198.1");
serverList.add("192.168.198.2");
serverList.add("192.168.198.3");
}
public static String selectServer(){
if(index == serverList.size()){
index = 0;
}
return serverList.get(index++);
}
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
System.out.println(selectServer());
}
}
}
2.2.2、加权轮询
public class Demo {
public static List<String> serverList = new ArrayList<>();
static int index = 0;
static int weight_a = 2;
static int weight_b = 7;
static int weight_c = 1;
static int weight_index = 1;
static {
serverList.add("192.168.198.1");
serverList.add("192.168.198.2");
serverList.add("192.168.198.3");
}
public static String selectServer(){
if(index == serverList.size()){
index = 0;
}
String ip = null;
if(weight_index <= weight_a){
ip = serverList.get(0);
}
if(weight_index > weight_a && weight_index <= weight_a + weight_b){
ip = serverList.get(1);
}
if(weight_index > weight_a + weight_b && weight_index <= weight_a + weight_b + weight_c){
ip = serverList.get(2);
}
weight_index++;
return ip;
}
public static void main(String[] args){
for (int i = 0; i < 10; i++) {
System.out.println(selectServer());
}
}
}
2.2.3、平滑加权轮询
定义:A服务器的权重是5,B服务器的权重是1,C服务器的权重是1,请求会落在权重最大的机械上
固定权重为:5 :1:1,总权重 7 非固定权重:根据每次请求按照一定规则变动 第一次请求:固定权重5:1:1,则请求到A服务器,请求之后,非固定权重为 5-7 :1:1,及 -2:1:1 第二次请求:固定权重为3:2:2, 则请求到A服务器,请求之后,非固定权重为 3-7 :1:1,及 -4:1:1 第二次请求:固定权重为1:3:3, 则请求到A服务器,请求之后,非固定权重为 3-7 :1:1,及 -4:1:1 以此类推注意:平滑加权轮询,巧妙的利用了巧妙算法,既有轮询的效果,又避免了某台服务器压力突然升高
public class Demo {
public static List<String> serverList = new ArrayList<>();
static int index = 0;
static int weight_a = 5;
static int weight_b = 1;
static int weight_c = 1;
static int weight_total = weight_a + weight_b + weight_c;
static List<Integer> fixWeight = new ArrayList<>();
static List<Integer> noFixWeight = new ArrayList<>();
static {
serverList.add("192.168.198.1");
serverList.add("192.168.198.2");
serverList.add("192.168.198.3");
fixWeight.add(weight_a);
fixWeight.add(weight_b);
fixWeight.add(weight_c);
noFixWeight.add(weight_a);
noFixWeight.add(weight_b);
noFixWeight.add(weight_c);
}
static int getMaxWeightIndex(List<Integer> weight ){
int index = 0;
int value = 0;
for (int i = 0; i < weight.size(); i++) {
if(weight.get(i) > value){
value = weight.get(i);
index = i;
}
}
return index;
}
public static String selectServer(){
int index = getMaxWeightIndex(fixWeight);
int maxWeight = fixWeight.get(index);
int currentWeight = maxWeight - weight_total;
fixWeight.set(index, currentWeight);
for (int i = 0; i < fixWeight.size(); i++) {
fixWeight.set(i, noFixWeight.get(i) + fixWeight.get(i));
}
//System.out.println("固定权重:" + fixWeight.toString());
return serverList.get(index);
}
public static void main(String[] args){
Map<String, Integer> map = new HashMap<>();
for (int i = 0; i < 70; i++) {
String ip = selectServer();
System.out.println(ip);
if(map.get(ip) == null){
map.put(ip, 1);
}else {
map.put(ip, map.get(ip) + 1);
}
}
System.out.println(map.toString());
}
}
2.3、哈希
就是根据某个值生成一个哈希值,然后对应到某台服务器上去,如果用用户哈希的话,可以解决session共享问题,因为某个用户哈希后永远都在那台机械上(前提:不发生扩容,缩容的情况)
常用算法:哈希环(可用treemap实现),ABC为真实的节点,其他为虚拟节点。
private static String go(String client) {
int nodeCount = 20;
TreeMap<Integer, String> treeMap = new TreeMap();
for (String s : new Servers().list) {
for (int i = 0; i < nodeCount; i++)
treeMap.put((s + "--服务器---" + i).hashCode(), s);
}
int clientHash = client.hashCode();
SortedMap<Integer, String> subMap = treeMap.tailMap(clientHash);
Integer firstHash;
if (subMap.size() > 0) {
firstHash = subMap.firstKey();
} else {
firstHash = treeMap.firstKey();
}
String s = treeMap.get(firstHash);
return s;
}
2.4、最小压力
最小压力算法是指:选择一台当前最悠闲的服务器,这种算法是比较科学的,但是该如何判断服务器最悠闲呢。