Skip to content

Latest commit

 

History

History
56 lines (45 loc) · 2.41 KB

algorithm.md

File metadata and controls

56 lines (45 loc) · 2.41 KB

Feature Flag灰度算法

如何判断算法好不好

  • 保证离散:确保灰度的范围是离散的,例如如果灰度对象是用户,那么每个年份的用户都可以灰度到最好;针对不同的flag,每次灰度的范围也应该离散,例如flag A和flag B的灰度范围应该不一样;
  • 保证一致:在灰度信息不变的情况下,针对同样的业务id每次的灰度结果都能保持一致;

场景分析

灰度的场景有以下两种:

  • 场景一:从整体上保障既定比例的流量灰度即可,针对同个业务id的请求(例如同个userId),不保证一定都是灰度或者都是不灰度;
    • 后端存储优化,需要控制1%的流量先使用新的接口;
    • ……
  • 场景二:需要保障同一个业务id每次的灰度结果是一致的,即针对同样的业务id,要么全灰度、要么全不灰度
    • A/B测试;
    • 用户端新增功能,需要小部分用户先体验,不可能有时候出现新功能,有时候不出现。应该确保灰度的用户都出现新功能;
    • ……

算法实现

基于以上场景,算法如下:

  • 随机算法(针对场景一)

    • 生成100以内的随机数;
    ThreadLocalRandom.current().nextInt(100)
    • 根据生成的随机数进行判断,小于灰度比例则开启灰度,否则不开启。
  • 一致性算法(针对场景二)

    • 根据flagName计算种子(确保每次的灰度,可以灰度到不同的群体);
    • 根据bizId+flagName计算hash(小于100),保证同个业务id每次的执行结果都一致;
    • 根据生成的随机数进行判断,小于灰度比例则开启灰度,否则不开启。
    /**
     * 根据业务id和flagName计算hash值
     *
     * @param bizId 业务id
     * @return 返回计算后小于等于100的哈希值
     */
    protected long seededHash(String bizId) {
        // FNV算法,根据flagName生成种子,确保不同的flag可以灰度到不同范围的群体
        long seed = FNV.fnv1a_32(getFlagName());
        long h = seed % 100L;
    
        byte[] bytes = bizId.getBytes();
        for (int i = 0; i < bytes.length; i++) {
        int t = 0xFF & bytes[i];
        // 为什么乘以31?因此31在JVM内部做过优化,通过位异5位-1完成,比较高效
        h = (h * 31L + t) % 100L;
        }
    
        return h;
    }