LoadBalancedDemo

  1 package com.miaoshaproject;
  2 
  3 import java.util.ArrayList;
  4 import java.util.HashMap;
  5 import java.util.List;
  6 import java.util.Map;
  7 
  8 /**
  9  * @Author wangshuo
 10  * @Date 2022/6/16, 20:07
 11  * node参数:
 12  * weight: 配置文件中指定的该后端的权重,这个值是固定不变的。
 13  * effective_weight: 后端的有效权重,初始值为weight,程序报错后减一。
 14  * current_weight: 后端目前的权重,一开始为0,之后会动态调整
 15  */
 16 public class LoadBalancedDemo {
 17 
 18     //调用接口
 19     public interface Invoker {
 20         Boolean isAvailable();
 21         String id();
 22     }
 23     //约定的invoker和权重的键值对
 24     final private List<Node> nodes;
 25     private static class Node implements Comparable<Node> {
 26         final Invoker invoker;
 27         final Integer weight;
 28         Integer effectiveWeight;
 29         Integer currentWeight;
 30 
 31         Node(Invoker invoker, Integer weight) {
 32             this.invoker = invoker;
 33             this.weight = weight;
 34             this.effectiveWeight = weight;
 35             this.currentWeight = 0;
 36         }
 37 
 38         @Override
 39         public int compareTo(Node o) {
 40             return currentWeight > o.currentWeight ? 1 : (currentWeight.equals(o.currentWeight) ? 0 : -1);
 41         }
 42 
 43         public void onInvokeSuccess() {
 44             if (effectiveWeight < this.weight)
 45                 effectiveWeight++;
 46         }
 47 
 48         public void onInvokeFail() {
 49             effectiveWeight--;
 50         }
 51     }
 52 
 53     //构造
 54     public LoadBalancedDemo(Map<Invoker, Integer> invokersWeight) {
 55         if (invokersWeight != null && !invokersWeight.isEmpty()) {
 56             nodes = new ArrayList<>(invokersWeight.size());
 57             invokersWeight.forEach((invoker, weight) -> nodes.add(new Node(invoker, weight)));
 58         } else
 59             nodes = null;
 60     }
 61 
 62     /**
 63      * 算法逻辑:
 64      * 1. 对于每个请求,遍历集群中的所有可用后端,对于每个后端peer执行:
 65      *      peer->current_weight += peer->effective_weight。
 66      *      同时累加所有peer的effective_weight,保存为total。
 67      * 2. 从集群中选出current_weight最大的peer,作为本次选定的后端。
 68      * 3. 对于本次选定的后端,执行:peer->current_weight -= total。
 69      *
 70      */
 71     public Invoker select() {
 72         if (!checkNodes())
 73             return null;
 74         else if (nodes.size() == 1) {
 75             if (nodes.get(0).invoker.isAvailable())
 76                 return nodes.get(0).invoker;
 77             else
 78                 return null;
 79         }
 80         Integer total = 0;
 81         Node nodeOfMaxWeight = null;
 82         for (Node node : nodes) {
 83             total += node.effectiveWeight;
 84             node.currentWeight += node.effectiveWeight;
 85 
 86             if (nodeOfMaxWeight == null) {
 87                 nodeOfMaxWeight = node;
 88             } else {
 89                 nodeOfMaxWeight = nodeOfMaxWeight.compareTo(node) > 0 ? nodeOfMaxWeight : node;
 90             }
 91         }
 92 
 93         nodeOfMaxWeight.currentWeight -= total;
 94         return nodeOfMaxWeight.invoker;
 95     }
 96 
 97     public void onInvokeSuccess(Invoker invoker) {
 98         if (checkNodes()) {
 99             nodes.stream()
100                     .filter((Node node) -> invoker.id().equals(node.invoker.id()))
101                     .findFirst()
102                     .get()
103                     .onInvokeSuccess();
104         }
105     }
106 
107     public void onInvokeFail(Invoker invoker) {
108         if (checkNodes()) {
109             nodes.stream()
110                     .filter((Node node) -> invoker.id().equals(node.invoker.id()))
111                     .findFirst()
112                     .get()
113                     .onInvokeFail();
114         }
115     }
116 
117     private boolean checkNodes() {
118         return (nodes != null && nodes.size() > 0);
119     }
120 
121     public void printCurrentWeightBeforeSelect() {
122         if (checkNodes()) {
123             final StringBuffer out = new StringBuffer("{");
124             nodes.forEach(node -> out.append(node.invoker.id())
125                     .append("=")
126                     .append(node.currentWeight + node.effectiveWeight)
127                     .append(","));
128             out.append("}");
129             System.out.print(out);
130         }
131     }
132 
133     public void printCurrentWeight() {
134         if (checkNodes()) {
135             final StringBuffer out = new StringBuffer("{");
136             nodes.forEach(node -> out.append(node.invoker.id())
137                     .append("=")
138                     .append(node.currentWeight)
139                     .append(","));
140             out.append("}");
141             System.out.print(out);
142         }
143     }
144 
145     static Invoker initialize(String str){
146         return new Invoker() {
147             @Override
148             public Boolean isAvailable() {
149                 return true;
150             }
151             @Override
152             public String id() {
153                 return str;
154             }
155         };
156     }
157 
158     public static void main(String[] args) {
159         //new实验数据
160         Map<Invoker, Integer> invokersWeight = new HashMap<>(3);
161         invokersWeight.put(initialize("a"), 4);
162         invokersWeight.put(initialize("b"), 2);
163         invokersWeight.put(initialize("c"), 1);
164         int times = 7;
165         //test start
166         LoadBalancedDemo roundRobin = new LoadBalancedDemo(invokersWeight);
167         for (int i = 1; i <= times; i++) {
168             System.out.print("第" + i + "次请求    选中前数据");
169             roundRobin.printCurrentWeightBeforeSelect();
170             Invoker invoker = roundRobin.select();
171             System.out.print("    本次选中" + invoker.id() + "    选中后数据");
172             roundRobin.printCurrentWeight();
173             System.out.println();
174         }
175     }
176 }