通过权重值得出唯一数据,权重越大的数据,被选举出来的概率越大

介绍

  • 业务场景: 项目的一个功能需要用到第三方接口文档,而该功能的第三方接口文档有多个,项目都希望能配置上,但是每次使用该功能只会用这些第三方接口的其中一个去调用
  • 解决思路: 新建一个权重表,给每个第三方接口都去配置权重,但权重最大的不一定就会用到该第三方接口调用,只是概率会更大
  • 算法讲解: 假设A第三方接口权重为3,B第三方接口权重为7, 那么总范围就是10, A接口所属范围就是(0-3],B接口所属范围就是(3-10], 算法将从0-10生成随机数,该数是在哪个范围内,则选举出哪个范围对应的第三方接口数据出来,并去使用该第三方接口

实体类准备

CfgInterfaceWeight

  • 权重表对应实体类
/**
 * api接口配置实体
 */
 @Data
public class CfgInterfaceWeight {

   private Integer id;
   private String interfaceName;//接口名
   private Integer weight;//权重
   private Integer status;//状态(0:正常,1:停用)

  • 接口配置表对应实体类
/**
 * api接口配置实体
 */
 @Data
public class CfgInterfaceConfig {

    private Integer id;
    private String interfaceName; //接口名称
    private String interfaceNote; //接口注释
    private Integer interfaceType;//接口类型 (0:携号转网,1:空号查询)
    private String cfgKey;  //配置项的建key
    private String cfgValue; //配置项的值value
    private String note;  //配置项注释
    private String protocol; //简易协议标识
    private Integer sortNum;  //key属性的排序
    private Integer status; //状态(0:开启,1:停用)
   

RangeStateDTO

  • 自定义封装类,存储数据对应权重瓜分的范围
@Data
public class RangeStateDTO {

    private Double min;

    private Double max;


}

业务部分

  • 1.先查出所有数据,按权重从小到大排序(细节)
  • 2.根据情况做不同调整,有多个数据的情况下才会按照权重随机数方式选举出唯一的一个数据
/**
     * 根据权重选举出 空号查询接口中的 数据
     * @return
     */
    private List<CfgInterfaceConfig> selectWeightInterFace() {
    // 先查出所有数据,按权重从`小到大排序`(细节)
        List<CfgInterfaceWeight> weights = cfgInterfaceWeightMapper.findAllStateWeight();
        if (CollectionUtils.isEmpty(weights)){
        // 没有数据就返回空
            return null;
        }else if (CollectionUtils.isNotEmpty(weights) && weights.size() == 1){
        // 只有一个数据 就只返回该数据
            List<CfgInterfaceConfig> cfgInterfaceConfigByInterfaceName = cfgInterfaceConfigMapper.findCfgInterfaceConfigByInterfaceName(weights.get(0).getInterfaceName());
            return cfgInterfaceConfigByInterfaceName;
        }else {
        // 有多个数据的情况下,就要权重选举方式 得出唯一的数据,权重越大 被获取的概率越大
            return  randomInterFaceWeigth(weights);
        }
    }
  • SQL:先查出所有数据,按权重从小到大排序(细节)
<select id="findAllStateWeight" resultType="com.xyc.sms.common.entity.cfg.CfgInterfaceWeight">
		SELECT INTERFACE_NAME as interfaceName,WEIGHT as weight
		FROM
		CFG_INTERFACE_WEIGHT
		WHERE STATUS =0 and INTERFACE_TYPE =1
		ORDER BY WEIGHT ASC
	</select>

权重-随机数-选举算法(重点)

  • 以上业务部分的randomInterFaceWeigth(weights);方法
  • 生成随机数,对应不同数据的权重值按比例瓜分,随机数是在哪个数据的权重瓜分范围,则选举出哪个数据出来
private List<CfgInterfaceConfig> randomInterFaceWeigth(List<CfgInterfaceWeight> weights) {
        // map<接口名称, 对应的权重范围>
        HashMap<String, RangeStateDTO> map = new HashMap<>();
//        权重总和
        double sumWeight = weights.stream().mapToDouble(CfgInterfaceWeight::getWeight).sum();
        // 获取随机值,随机数字(0-1) * 所有权重
        double random = Math.random() * sumWeight;
        // 元素头部索引
        int headIndex = 0;
        // 元素尾部索引
        int tailIndex = weights.size() - 1;
//        范围起始值
        Double prevRate = 0d;
        // 计算每个接口,对应的权重范围
        for (int i = 0; i < weights.size(); i++) {
            CfgInterfaceWeight cfgInterfaceWeight = weights.get(i);
            if (i == headIndex) {
                RangeStateDTO range = new RangeStateDTO(0d, Double.valueOf(cfgInterfaceWeight.getWeight()));
                map.put(cfgInterfaceWeight.getInterfaceName(), range);
                prevRate = Double.valueOf(cfgInterfaceWeight.getWeight());
            } else if (i != tailIndex) {
                RangeStateDTO range = new RangeStateDTO(Double.valueOf(prevRate), Double.valueOf(prevRate + cfgInterfaceWeight.getWeight()));
                map.put(cfgInterfaceWeight.getInterfaceName(), range);
                prevRate = range.getMax();
            } else if (i == tailIndex) {
                RangeStateDTO range = new RangeStateDTO(prevRate, sumWeight);
                map.put(cfgInterfaceWeight.getInterfaceName(), range);
            }

        }
//      选举出唯一接口
        String selectInterfaceName = null;
        Set<Map.Entry<String, RangeStateDTO>> entries = map.entrySet();
        for (Map.Entry<String, RangeStateDTO> entry : entries) {
            RangeStateDTO range = entry.getValue();
            if (random > range.getMin() && random <= range.getMax()) {
                selectInterfaceName = entry.getKey();
            }
        }
        if (Objects.isNull(selectInterfaceName)) {
            return new ArrayList<>();
        }

        logger.info("接口总权重为[{}],随机数为[{}],各接口对应范围数据[{}],选举出使用的携号转网查询接口为[{}]", sumWeight, random, map.toString(), selectInterfaceName);
        return cfgInterfaceConfigMapper.findCfgInterfaceConfigByInterfaceName(selectInterfaceName);
    }

算法测试demo类

public class randomNum {

    public static void main(String[] args) {
        getRange();

    }

    public    static void getRange(){
        // map<接口名称, 对应的权重范围>
        HashMap<String, Range> map = new HashMap<>();
        // 生产随机数字
        double randomNum = Math.random() ;
        System.out.println(randomNum);
        // 获取数据库数据
        List<InterfaceRate> interfaceRateList = data();
        // 获取最大随机值
        double maxRate =  maxRate(interfaceRateList);
        // 获取随机值,随机数字(0-1) * 所有权重
        double  actuallRate = randomNum * maxRate;
        // 元素头部索引
        int  headIndex = 0;
        // 元素尾部索引
        int  tailIndex = interfaceRateList.size() - 1;
        Double prevRate = 0d;
        // 计算每个接口,对应的权重范围
        for (int i = 0; i < interfaceRateList.size(); i++) {
            InterfaceRate interfaceRate = interfaceRateList.get(i);
            if (i == headIndex){
                Range range = new Range(0d, Double.valueOf(interfaceRate.getRate()));
                map.put(interfaceRate.getInterfaceName(), range);
                prevRate  = Double.valueOf(interfaceRate.getRate());
            }else  if (i != tailIndex){
                Range range = new Range(Double.valueOf(prevRate) , Double.valueOf(prevRate + interfaceRate.getRate()));
                map.put(interfaceRate.getInterfaceName(), range);
                prevRate = range.getMax();
            }else if ( i == tailIndex){
                Range range = new Range(prevRate, maxRate);
                map.put(interfaceRate.getInterfaceName(), range);
            }

        }

        System.out.println("所有权重和: " + maxRate);
        System.out.println("实际随机权重值: " + actuallRate);
        System.out.println("初始化map: "+ map);


        // 选举
        String selectInteferName = null;
        Set<Map.Entry<String, Range>> entries = map.entrySet();
        for (Map.Entry<String, Range> entry : entries) {
            Range range = entry.getValue();
            if (actuallRate > range.getMin() && actuallRate <= range.getMax()){
                selectInteferName = entry.getKey();
            }
        }
        System.out.println("调用接口为: " + selectInteferName);


    }


    /**
     * 模拟数据库数据   order by 权重  asc(升序)
     * @return
     */
    public static List<InterfaceRate>  data(){
        ArrayList<InterfaceRate> list = new ArrayList<>();
        list.add(new InterfaceRate("A",5));
        list.add(new InterfaceRate("B",5));
        list.add(new InterfaceRate("C",5));
//        list.add(new InterfaceRate("D", 25d));
        return  list;
    }


    public  static   double  maxRate(List<InterfaceRate> interfaceRateList){
        return    interfaceRateList.stream().mapToDouble(InterfaceRate::getRate).sum();
    }
}
  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值