What is gray scale

Sometimes, the product says, when we launch this new version, cut 20% of the traffic to see the conversion rate. Sometimes, the leader said, we still need to validate the reconfigured system in production. Sometimes, the product says, the conversion rate is as expected, and the traffic is up to 80%. Sometimes, the leader said, production fried, fast, fast all cut back to the old system. I believe that many people have encountered the above scenario, so what is the common way to solve these problems? Use configuration file, disadvantage, change configuration need to publish. Using DB storage, disadvantages, every request to access DB, not suitable to use the configuration center, disadvantages, can not do complex rules, such as 10% traffic, exclude certain users.

What needs to be considered to implement a general grayscale framework

1, rules can be configured after 2, rule configuration is of immediate effect, support the thermal load of 3 and different team has different technology stacks, needs to support the market mainstream configuration center 4, complex rules can custom extensions, and data support for custom extensions, rules of execution is traceable, tracking data can be see, The storage of data is customizable.

Design – Build the skeleton first

1. A Rule is composed of many small expressions, for example, between 0 and 20, in [1,2,3,4,6,7], equal to “Jack”. First design a class to describe such a Rule. The execution result of the rule is equal to the execution result of all features)

public class GrayRuleConfig
{
    private List<Feature> features;
    public static class  Feature{
        private String key;
        private boolean enable;
        private boolean unionAll;
        privateMap<String,String> metaData; }}Copy the code

2. Rule behavior definition and Feature behavior

public interface IRule {
    String getKey(a);
    boolean enable(a);
    boolean check(Object value);
}
Copy the code
public interface IFeature<F> {
    F getFeature(a);
    boolean check(Object targetValue);
}
Copy the code

3. The public behavior of rules and features is abstracted and the execution skeleton is defined

public abstract class AbstractRule implements IRule {
    private final ITraceRepository traceRepository;

    public AbstractRule(ITraceRepository traceRepository) {
        this.traceRepository = traceRepository;
    }

    public boolean check(Object value) {
        boolean result = doCheck(value);
        beforeGrayCheck(value);
        try {
            if (result) {
                execResultPass();
            } else {
                execResultNoPass();
            }
            afterGrayCheck(value, result);
        } catch (Exception ex) {
            execGrayErrorAfter(ex, value);
        }

        return result;
    }

    protected void beforeGrayCheck(Object value) {
        System.out.println(this.getClass() + "; beforeGrayCheck:" + value);
    }

    protected void afterGrayCheck(Object value, boolean result) {
        System.out.println(this.getClass() + "; afterGrayCheck:" + value);

    }

    protected void execGrayErrorAfter(Exception ex, Object value) {
        //todo log
        System.out.println(this.getClass() + "; execGrayErrorAfter:" + value + "ex:" + ex);

    }

    private void execResultPass(a) {
        traceRepository.pass(this.getKey());
    }

    private void execResultNoPass(a) {
        traceRepository.noPass(this.getKey());
    }
    public abstract boolean doCheck(Object targetValue);
Copy the code
public abstract class AbstractFeature<F> implements IFeature<F>  {
    private ITraceRepository traceRepository;
    private F feature;

    public void setTraceRepository(ITraceRepository traceRepository){
        this.traceRepository=traceRepository;
    }
    public ITraceRepository getTraceRepository(a){
        return this.traceRepository;
    }
    protected IFeature buildFeature(String featureDesc){
        return buildFeature(featureDesc,null);
    }
    protected IFeature buildFeature(String featureDesc,ITraceRepository traceRepository){
        this.feature=parseFeature(featureDesc);
        this.traceRepository=traceRepository;
        return this;
    }
    public F getFeature(a) {
        return this.feature;
    }

    public boolean check(Object targetValue) {
       return doCheck(targetValue);
    }
    private F parseFeature(String featureDesc){
        if (StringUtils.isEmpty(featureDesc)){
            throw new GrayException("featureDesc can not be null");
        }
       return doParseFeature(featureDesc);
    }

    protected abstract F doParseFeature(String featureDesc);
    protected abstract boolean doCheck(Object value);
Copy the code

Rule implementation supported by default

public class DefaultRule extends AbstractRule {
    private String key;
    private boolean enable;

    public boolean isUnionAll(a) {
        return unionAll;
    }

    public void setUnionAll(boolean unionAll) {
        this.unionAll = unionAll;
    }

    private boolean unionAll;

    public DefaultRule(ITraceRepository traceRepository) {
        super(traceRepository);
    }
    public void setKey(String key){
        this.key=key;
    }
    public void setEnable(boolean enable){
        this.enable=enable;
    }
    public boolean enable(a) {
        return this.enable;
    }
    public String getKey(a) {
        return this.key;
    }

    private List<IFeature> features;
    public void setFeatures(List<IFeature> features){
        this.features=features;
    }
    public List<IFeature> getFeatures(a) {
        return this.features;
    }

    public boolean doCheck(Object targetValue) {
        boolean result=false;
        for (IFeature feature:this.features){
            result=feature.check(targetValue);
            if(result&&! unionAll){break;
            }
            if(! result&&unionAll){break; }}return result;
    }
Copy the code

Rules part, first write here, Github has the source code