概述
公司新提的一个需求,需要用到灰度测试,30%的用户看到的新逻辑,70%用户还是旧的,刚开始想到ip取余,用户id取余,发现取余误差会很大,用于灰度测试是不可取的。而且怎么才算100%?因为一直有新用户,还有未登录的用户,而且老用户也不一定会登录,所以100%只是一个范围,没有具体数值的。
实现方式
在网上看了一下灰度测试常用的手段,发现一致性Hash环
挺符合的。正常都是用于负载服务器的,由于需求急,就不配置负载了,直接1套代码2个逻辑(很不规范哈),30%的用户看到新逻辑,剩下70%还是旧的。
class GrayScaleTest {
// 10台服务器(模拟10个服务器)
private $servers = [
'server1',
'server2',
'server3',
'server4',
'server5',
'server6',
'server7',
'server8',
'server9',
'server10',
];
private $hashRing = [];
public function __construct()
{
$this->hashRing = $this->buildHashRing();
}
// 构建一致性哈希环
private function buildHashRing()
{
// 虚拟节点个数,可以根据实际情况调整(虚拟节点的数量越多,哈希环的分布就越均匀,负载也可以更加平衡,虚拟节点的增加会增加计算和存储的开销。)
$replicas = 500;
// 生成虚拟节点
foreach ($this->servers as $server)
{
for ($i = 0; $i < $replicas; $i++)
{
$hash = $this->hash($server . ':' . $i);
$this->hashRing[$hash] = $server;
}
}
return $this->hashRing;
}
// 生成hash值
private function hash($str)
{
//return sprintf("%u", crc32(mhash(MHASH_MURMURHASH3, $str)));
return crc32($str);
}
// 获取用户id在那个服务器上
public function getServer($userid)
{
// 计算用户ID的哈希值
$hash = $this->hash($userid);
// 查找虚拟节点
$virtualNodes = array_keys($this->hashRing);
// hash值排序
sort($virtualNodes);
// 循环比较,看在哪个区间
foreach ($virtualNodes as $virtualNode)
{
if ($hash <= $virtualNode)
{
return $this->hashRing[$virtualNode];
}
}
return end($this->servers);
}
// 30%的灰度
public function isInGrayScale($userid)
{
$server = $this->getServer($userid);
// 前3台服务器进行灰度测试
if (in_array($server, array_slice($this->servers, 0, 3)))
{
return true;
}
return false;
}
}
随机生成1000个id,进行测试,发现都在30%左右(存在误差的),还是可以接受的。
500虚拟节点
第一次:276/1000
第二次:269/1000
第三次:280/1000
第四次:277/1000
第五次:264/1000
1000虚拟节点
第一次:278/1000
第二次:282/1000
第三次:269/1000
第四次:270/1000
第五次:302/1000