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;
}

参考资料


文章作者: zzq0324
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 zzq0324 !
  目录