如何根据配置的百分比让请求按比例打散(java 版)
一.背景
现有三个集群,公司内部有一个服务由于某些原因需要根据ip直连的方式根据每个集群配置的百分比回推数据,以此来达到流量的负载均衡,不至于让请求打到单独的一个集群,听起来其实是一个很简单的功能,无非就是key-value形式,可以代表集群名,value代表ip的集合,但是由于某些特殊原因数据格式并不是这种,而是如下格式
123456-xy-30,第一位是ip,第二位是集群名,第三位是百分比,配置中存放的则是123456-xy-30,12456-xy-30,1122-ai-35,1232-ai-35,1123-rb-35,2321-rb-35。现在需求转换成从这个数据中按百分比获取ip,集群名称相同的则归属于一个集群下的,之前写过python版本的 https://blog.csdn.net/qq_45136592/article/details/121622036?spm=1001.2014.3001.5502,现由于要把python的转换成java再记录一下为什么这么干
二.思考
问题转换:现在我们需要做的就是将数据进行分隔,然后将集群名相同的ip放到一个list中,然后和集群名绑定到一起。表面上看按百分比获取ip,实际是可以转换成按百分比获取随机获取集群名,然后随机获取集群名下的ip。
现在要做的就是将存储集群名和百分比的数据转换成按百分比排序的集群名集合,然后对百分比也按相对应的顺序排序来形成与集群名的映射。假设现在有数据 123456-xy-30,12456-xy-30,1122-ai-35,1232-ai-35,1123-rb-35,2321-rb-35 ,对数据进行切割并排序我们得到一个集群名的list[xy,ai,rb],对百分比排序我们得到一个百分比的list[30,35,35],因为都是按指定顺序排序的所以每个集群的百分比都能与自己的名称相映射。对百分比中的值,按顺序相加得到[30,75,100],然后调用随机函数区间在 (0-100)之间,判断落入的区间,若值为20,可以看出落在30前面且离20最近的元素是30那就说明此时随机到了xy这个集群,若值为60则说明落到了ai集群里面,以此类推.
三. 编码
public String getHostByWeight(List<String> hosts) {
Map<String, List<String>> providerIpListMap = new HashMap<>(8);
Map<String, Integer> weightMap = new HashMap<>(8);
for (String host : hosts) {
String[] split = host.split("-");
String ip = split[0];
String provider = split[1];
String weight = split[2];
providerIpListMap.computeIfAbsent(provider, k -> new ArrayList<>()).add(ip);
weightMap.put(provider, Integer.parseInt(weight));
}
// 获取按权重排序好的服务提供商名称
List<String> providerSortByWeightList = weightMap.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getKey).collect(Collectors.toList());
//获取排序好的权重,providerSortByWeightList中的顺序和afterSortWeightList保持一致
List<Integer> afterSortWeightList = weightMap.entrySet().stream().sorted(Map.Entry.comparingByValue()).map(Map.Entry::getValue).collect(Collectors.toList());
// 对权重值按序累加 [1,2,3] 得到的结果为[1,3,6]
List<Integer> result = IntStream.range(0, afterSortWeightList.size())
.mapToObj(i -> afterSortWeightList.subList(0, i + 1)
.stream()
.reduce(0, Integer::sum))
.collect(Collectors.toList());
// 在累加后集合中的最大值中获取随机数
Integer random = RandomUtil.getInteger(0, result.get(result.size() - 1));
//利用二分法查找,返回值为负的插入点值,所谓插入点值就是第一个比关键字大的元素在数组中的位置索引,这个位置索引从1开始
int index = Collections.binarySearch(result, random);
int realIndex;
//超出最大值的索引位置则直接取排序后的最大值
if (Math.abs(index) > result.size()) {
realIndex = result.size() - 1;
} else {
realIndex = index - 1;
}
// 根据服务商的名称获取ip
List<String> providerIp = providerIpListMap.get(providerSortByWeightList.get(realIndex));
Integer integer = RandomUtil.getInteger(0, providerIp.size());
return providerIp.get(integer);
}