常见的负载均衡算法列举与实现。
随机
随机从集群中选择一个服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 public class RandomSlb { private static final List<Server> servers = new ArrayList <>(); private static final ThreadLocal<Server> currentServer = new ThreadLocal <>(); public static Server getNextServer () throws NoSuchAlgorithmException { int nextIndex = SecureRandom.getInstanceStrong().nextInt(servers.size()); Server server = servers.get(nextIndex); currentServer.set(server); return server; } public static void main (String[] args) throws NoSuchAlgorithmException { servers.add(new Server ("127.0.0.1" , "111" , 1 )); servers.add(new Server ("127.0.0.2" , "222" , 2 )); servers.add(new Server ("127.0.0.3" , "333" , 3 )); servers.add(new Server ("127.0.0.4" , "444" , 4 )); System.out.println(getNextServer()); System.out.println(getNextServer()); } }
随机加权
根据权重值,随机获取集群服务器。权值越大获取的概率越大
TreeMap排序
通过树结构实现:找到大于该权重值中的最小权重的服务。如: random = 4, 则找出的第一个为
结果近似于方法一,key为最大权重值,取最接近的一个
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 public class WeightRandom { private final TreeMap<Double, Server> weightMap = new TreeMap <>(); public WeightRandom (final List<Server> list) { for (Server server : list) { double lastWeight = this .weightMap.size() == 0 ? 0 : this .weightMap.lastKey(); this .weightMap.put(server.getWeight() + lastWeight, server); } } public Server randomServer () { double randomWeight = this .weightMap.lastKey() * Math.random(); SortedMap<Double, Server> tailMap = this .weightMap.tailMap(randomWeight, false ); return this .weightMap.get(tailMap.firstKey()); } public static void main (String[] args) { List<Server> servers = new ArrayList <>(); servers.add(new Server ("127.0.0.1" , "111" , 1 )); servers.add(new Server ("127.0.0.2" , "222" , 22 )); servers.add(new Server ("127.0.0.3" , "333" , 3 )); servers.add(new Server ("127.0.0.4" , "444" , 4 )); WeightRandom weightRandom = new WeightRandom (servers); int s1 = 0 , s2 = 0 , s3 = 0 , s4 = 0 ; float maxCount = 10000 ; for (int i = 0 ; i < maxCount; i++) { Server server = weightRandom.randomServer(); switch (server.getPort()) { case "111" : s1++; break ; case "222" : s2++; break ; case "333" : s3++; break ; case "444" : s4++; break ; } } System.out.printf("s1 = %d 出现概率 %.2f \n" , s1, (s1 / maxCount) * 100 ); System.out.printf("s2 = %d 出现概率 %.2f \n" , s2, (s2 / maxCount) * 100 ); System.out.printf("s3 = %d 出现概率 %.2f \n" , s3, (s3 / maxCount) * 100 ); System.out.printf("s4 = %d 出现概率 %.2f \n" , s4, (s4 / maxCount) * 100 ); } }
区间法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 public class WeightSlb { private static final List<Server> servers = new ArrayList <>(); private static volatile int totalWeights; private static final Map<Server, Set<Integer>> maps = new HashMap <>(); private static final ThreadLocal<Server> currentServer = new ThreadLocal <>(); private static void weightExtent () { totalWeights = servers.stream().mapToInt(Server::getWeight).sum(); int startSum = 0 ; for (Server s : servers) { Set<Integer> list = maps.computeIfAbsent(s, n -> new HashSet <>()); for (int i = startSum; i < startSum + s.getWeight(); i++) { list.add(i); } startSum += s.getWeight(); } } public static Server getNextServer () throws NoSuchAlgorithmException { int nextInt = SecureRandom.getInstanceStrong().nextInt(totalWeights); for (Server server : maps.keySet()) { if (maps.get(server).contains(nextInt)) { currentServer.set(server); break ; } } return currentServer.get(); } public static void main (String[] args) throws NoSuchAlgorithmException { servers.add(new Server ("127.0.0.1" , "111" , 1 )); servers.add(new Server ("127.0.0.2" , "222" , 12 )); servers.add(new Server ("127.0.0.3" , "333" , 3 )); servers.add(new Server ("127.0.0.4" , "444" , 4 )); weightExtent(); int s1 = 0 , s2 = 0 , s3 = 0 , s4 = 0 ; float maxCount = 10000 ; for (int i = 0 ; i < maxCount; i++) { Server server = getNextServer(); switch (server.getPort()) { case "111" : s1++; break ; case "222" : s2++; break ; case "333" : s3++; break ; case "444" : s4++; break ; } } System.out.printf("s1 = %d 出现概率 %.2f \n" , s1, (s1 / maxCount) * 100 ); System.out.printf("s2 = %d 出现概率 %.2f \n" , s2, (s2 / maxCount) * 100 ); System.out.printf("s3 = %d 出现概率 %.2f \n" , s3, (s3 / maxCount) * 100 ); System.out.printf("s4 = %d 出现概率 %.2f \n" , s4, (s4 / maxCount) * 100 ); } }
B+树结构
利用B+树的原理。叶子结点存放元素,非叶子结点用于索引。非叶子结点有两个属性,分别保存左右子树的累加权重。如下图:
更改一个元素,只须修改该元素到根结点那半部分的权值即可
轮询
把来自用户的请求轮流分配给集群中的服务器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 public class PollingSlb { private static final List<Server> servers = new ArrayList <>(); private static final ThreadLocal<Server> currentServer = new ThreadLocal <>(); private static volatile int currentServerIndex = 0 ; public static Server getNextServer () { Server server = currentServer.get(); if (server != null ) currentServerIndex = (currentServerIndex + 1 ) % servers.size(); server = servers.get(currentServerIndex); currentServer.set(server); return server; } public static void main (String[] args) { servers.add(new Server ("127.0.0.1" , "111" , 1 )); servers.add(new Server ("127.0.0.2" , "222" , 1 )); servers.add(new Server ("127.0.0.3" , "333" , 1 )); System.out.println(PollingSlb.getNextServer()); System.out.println(PollingSlb.getNextServer()); System.out.println(PollingSlb.getNextServer()); System.out.println(PollingSlb.getNextServer()); System.out.println(PollingSlb.getNextServer()); } }