前言

Github:https://github.com/HealerJean

博客:http://blog.healerjean.com

一、灰度枚举 GrayEnum

package com.hlj.util.z028_灰度工具;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.Arrays;

/**
 * GrayEnum
 * @author zhangyujin1
 * @date 2021/4/14  2:24 下午.
 */
public interface GrayEnum {


    /**
     * 灰度进度
     */
    @Getter
    @AllArgsConstructor
    enum GrayResEnum implements GrayEnum {

        /**
         * GRAY_CLOSE
         */
        GRAY_WHITE_TRUE( "whiteHit", true,"命中灰度白名单"),
        GRAY_TRUE("hit" ,true,"命中灰度"),

        GRAY_CLOSE( "close", false,"灰度关闭"),
        GRAY_NOT_EXIST( "notExist", false,"灰度不存在"),
        GRAY_BLACK_TRUE( "blackHit", false,"命中灰度白名单"),
        GRAY_FALSE("unHit", false, "未命中或者灰度不存在"),
        ;


        /**
         * 灰度详细结果
         */
        private final String code;

        /**
         * 灰度结果
         */
        private final Boolean flag;

        /**
         * desc
         */
        private final String desc;

    }



    /**
     * 灰度业务
     */
    @Getter
    @AllArgsConstructor
    enum GrayBusinessEnum implements GrayEnum {
        /**
         * INSURANCE_6067
         */
        BUSINESS_OOO1("businessDemo", "灰度场景1"),
        ;

        /**
         * insuranceId
         */
        private final String bizType;

        /**
         * desc
         */
        private final String bizDesc;


        /**
         * GrayBusinessEnum
         * @param bizType insuranceId
         * @return GrayBusinessEnum
         */
        public static GrayBusinessEnum toGrayBusinessEnum(String bizType) {
            return Arrays.stream(GrayBusinessEnum.values()).filter(item->item.getBizType().equals(bizType)).findAny().orElse(null);
        }
    }

}

二、灰度配置

依靠配置中心进行配置

package com.hlj.util.z028_灰度工具;

import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Configuration;

import java.util.Map;

/**
 * DuccBypassInsuranceConfiguration
 *
 * @author zhangyujin
 * @date 2022/9/21  21:21.
 */
@Slf4j
@Data
@Configuration
public class GrayConfiguration {

    /**
     * 灰度对象
     */
    private Map<String, GrayBizBO> grayBizMap;


}

package com.hlj.util.z028_灰度工具;

import lombok.Data;
import lombok.experimental.Accessors;

import java.io.Serializable;
import java.util.Set;

/**
 * GrayInsuranceBusinessDto
 * @author zhangyujin
 * @date 2022/9/22  13:48.
 */
@Accessors(chain = true)
@Data
public class GrayBizBO implements Serializable {

    /**
     * serialVersionUID
     */
    private static final long serialVersionUID = 5110796299306482078L;

    /**
     * 灰度开关
     */
    private  Boolean flag;

    /**
     * 灰度白名单
     */
    private Set<String> whiteInfos;

    /**
     * 灰度黑名单
     */
    private Set<String> blackInfos;

    /**
     * 灰度比例
     */
    private Integer rate;

    /**
     * 灰度总额
     */
    private Integer amount;



}

三、灰度工具

package com.hlj.util.z028_灰度工具;

import com.google.common.collect.Maps;
import com.google.common.collect.Sets;
import lombok.extern.slf4j.Slf4j;
import org.junit.Test;
import org.springframework.stereotype.Service;

import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;


/**
 * 自制工具类
 *
 * @author healerjean
 * @date 2022-09-21 22:27
 */
@Service
@Slf4j
public class GrayUtil {

    /**
     * grayConfiguration
     */
    private static final GrayConfiguration grayConfiguration;

    static {
        grayConfiguration = new GrayConfiguration();
        Map<String, GrayBizBO> grayBizMap = Maps.newHashMap();
        grayBizMap.put(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1.getBizType(),
                     new GrayBizBO()
                        .setFlag(true)
                        .setRate(2)
                        .setAmount(10)
                        .setWhiteInfos(Sets.newHashSet("4", "5", "6"))
                        .setBlackInfos(Sets.newHashSet("7", "8", "9")));
        grayConfiguration.setGrayBizMap(grayBizMap);

    }


    /**
     * 是否命中灰度
     * 一、灰度业务判断
     * 1、灰度业务不存在 返回:GrayEnum.GrayResEnum.GRAY_NOT_EXIST
     * 2、判断是否灰度关闭,是返回 GrayEnum.GrayResEnum.GRAY_CLOSE;
     * 二、灰度黑白名单判断
     *
     * @param grayBusinessEnum 灰度业务枚举
     * @param grayValue        灰度值
     * @return 灰度开关是否打开
     */
    public static GrayEnum.GrayResEnum hitGray(GrayEnum.GrayBusinessEnum grayBusinessEnum, String grayValue) {

        // 一、灰度业务判断
        // 1、灰度业务不存在 返回:GrayEnum.GrayResEnum.GRAY_NOT_EXIST
        Map<String, GrayBizBO> grayBizMap = Optional.ofNullable(grayConfiguration.getGrayBizMap()).orElse(Maps.newHashMap());
        GrayBizBO grayBiz = grayBizMap.get(grayBusinessEnum.getBizType());
        if (Objects.isNull(grayBiz)) {
            log.info("[GrayUtil#hitGray] grayBusinessEnum:{}, grayValue:{} 灰度结果: false(未配置灰度)", grayBusinessEnum, grayValue);
            return GrayEnum.GrayResEnum.GRAY_NOT_EXIST;
        }

        // 2、判断是否灰度关闭,是返回 GrayEnum.GrayResEnum.GRAY_CLOSE;
        if (Boolean.FALSE.equals(grayBiz.getFlag())) {
            log.info("[GrayUtil#hitGray] grayBusinessEnum:{}, grayValue:{} 灰度结果: false(灰度关闭)", grayBusinessEnum, grayValue);
            return GrayEnum.GrayResEnum.GRAY_CLOSE;
        }


        // 二、灰度黑白名单判断
        // 1、白名单判断,如果在白名单,返回:GrayEnum.GrayResEnum.GRAY_WHITE_TRUE;
        Set<String> whiteUsers = Optional.ofNullable(grayBiz.getWhiteInfos()).orElse(Sets.newHashSet());
        if (whiteUsers.contains(grayValue)) {
            log.info("[GrayUtil#hitGray] grayBusinessEnum:{}, grayValue:{} 灰度结果:true(白名单命中)", grayBusinessEnum, grayValue);
            return GrayEnum.GrayResEnum.GRAY_WHITE_TRUE;
        }

        // 2、黑白名单判断,如果在白名单,返回:GrayEnum.GrayResEnum.GRAY_BLACK_TRUE;
        Set<String> blackInfos = Optional.ofNullable(grayBiz.getBlackInfos()).orElse(Sets.newHashSet());
        if (blackInfos.contains(grayValue)) {
            log.info("[GrayUtil#hitGray] grayBusinessEnum:{}, grayValue:{} 灰度结果:false(黑名单命中)", grayBusinessEnum, grayValue);
            return GrayEnum.GrayResEnum.GRAY_BLACK_TRUE;
        }

        // 三、灰度比例判断
        // 3.1、灰度比例不存在,则返回false
        Integer grayPercent = grayBiz.getRate();
        Integer grayPercentAmount = grayBiz.getAmount();

        // 3.2、灰度比例计算,命中返回ture,不命中返回false
        int rate = Math.abs(hashValue(grayBusinessEnum, grayValue)) % grayPercentAmount;
        if (rate <= grayPercent) {
            log.info("[GrayUtil#hitGray] grayBusinessEnum:{}, grayValue:{} 灰度结果:true(灰度命中)", grayBusinessEnum, grayValue);
            return GrayEnum.GrayResEnum.GRAY_TRUE;
        }
        log.info("[GrayUtil#hitGray] grayBusinessEnum:{}, grayValue:{} 灰度结果:false(灰度未命中)", grayBusinessEnum, grayValue);
        return GrayEnum.GrayResEnum.GRAY_FALSE;
    }


    /**
     * hashValue
     *
     * @param grayBusinessEnum grayBusinessEnum
     * @param grayValue        grayValue
     * @return {@link Integer}
     */
    public static Integer hashValue(GrayEnum.GrayBusinessEnum grayBusinessEnum, String grayValue) {
        return Integer.valueOf(grayValue);
    }


     @Test
     public void test(){
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "1"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "2"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "3"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "4"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "5"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "6"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "7"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "8"));
         System.out.println(hitGray(GrayEnum.GrayBusinessEnum.BUSINESS_OOO1, "9"));
     }

}

ContactAuthor