一、负载均衡维度
从负载均衡设备的角度来看,分为硬件负载均衡和软件负载均衡
- 硬件负载均衡 - 比如最常见的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权重是3static {//权重是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、最小压力
最小压力算法是指:选择一台当前最悠闲的服务器,这种算法是比较科学的,但是该如何判断服务器最悠闲呢。
