前言

Github:https://github.com/HealerJean

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

一、灰度枚举 GrayEnum

public interface GrayEnum {


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

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

        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"),
        ;

        /**
         * code
         */
        private final String code;

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


        /**
         * GrayBusinessEnum
         * @param code code
         * @return GrayBusinessEnum
         */
        public static GrayBusinessEnum toGrayBusinessEnum(String code) {
            return Arrays.stream(GrayBusinessEnum.values()).filter(item->item.getCode().equals(code)).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;



}

三、灰度工具

@Slf4j
public class GrayUtil {


    /**
     * 是否命中灰度
     * 一、灰度业务判断
     * 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) {

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


        // 二、灰度黑白名单判断
        // 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
        long grayPercent = grayBiz.getRate();
        long grayPercentAmount = grayBiz.getAmount();

        // 3.2、灰度比例计算,命中返回ture,不命中返回false
        long 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}
     */
    private static Long hashValue(GrayEnum.GrayBusinessEnum grayBusinessEnum, String grayValue) {
        return Long.valueOf(grayValue);
    }



}

ContactAuthor