swoole方案 灰度发布与 A/B 测试路由

发布时间:2026/7/26 5:22:50

swoole方案 灰度发布与 A/B 测试路由 大白话解释---灰度发布是什么 新版本写完了不敢直接全量上线万一有bug全站崩了。 灰度就是先让5%的用户用新版观察错误率、响应时间有没有异常没问题再慢慢加到10%、50%、100%。 改一行配置就能调比例v1[weight95],// 改成50v2[weight5],// 改成 50---A/B测试是什么 两个版本同时跑比如按钮颜色不一样看哪个版本用户点击率更高用数据决定用哪个。---为什么用取模不用随机数 随机数用户第1次请求落到v2第2次请求落到v1页面一会新版一会旧版体验很割裂。 取模用户ID%100同一个用户永远落到同一个桶体验一致。---三个策略优先级1.Header/Cookie 强制指定 → 开发自测用x-version:v2直接去v22.白名单→ 内部员工永远用新版提前发现问题3.按比例取模 → 普通用户按权重分流---跑起来测试 php8.4/mnt/d/ab/ab.php 强制走v2 curl-Hx-version: v2http://localhost:9800/看流量分布 curl http://localhost:9800/_stats?php/** * 灰度发布 A/B 测试路由 * * 大白话 * 新版本上线不敢全量发先让5%的用户用新版没问题再慢慢放量 * A/B测试同时跑两个版本看哪个转化率更高 * * 分流策略按优先级 * 1. 强制指定Header/Cookie 里带 x-versionv2直接去v2开发测试用 * 2. 白名单用户特定用户ID永远走新版 * 3. 按比例用用户ID取模稳定分流同一个用户每次都去同一个版本 * * 运行 * php8.4 ab.php */useSwoole\Http\Server;useSwoole\Http\Request;useSwoole\Http\Response;useSwoole\Coroutine\Http\Client;useSwoole\Table;// 版本配置每个版本对应一个后端服务constVERSIONS[v1[host127.0.0.1,port8081,weight95],// 老版本95%流量v2[host127.0.0.1,port8082,weight5],// 新版本5%流量];// 白名单这些用户ID永远走v2内部员工、测试账号constV2_WHITELIST[1,2,100,999];// ── 统计表记录每个版本处理了多少请求 ──────────────────────$statsnewTable(16);$stats-column(count,Table::TYPE_INT,8);$stats-create();foreach(array_keys(VERSIONS)as$v){$stats-set($v,[count0]);}$servernewServer(0.0.0.0,9800);$server-set([worker_numswoole_cpu_num(),enable_coroutinetrue,]);$server-on(request,function(Request$req,Response$res)use($stats){// 统计接口看当前流量分布if($req-server[request_uri]/_stats){$data[];foreach(array_keys(VERSIONS)as$v){$data[$v]$stats-get($v)[count];}$res-header(Content-Type,application/json);$res-end(json_encode($data));return;}// ── 策略1强制指定版本开发/测试用────────────────────// Header: x-version: v2 或 Cookie: x-versionv2$forced$req-header[x-version]??null;if(!$forced){// 从 Cookie 里找$cookies[];parse_str(str_replace(; ,,$req-header[cookie]??),$cookies);$forced$cookies[x-version]??null;}if($forcedisset(VERSIONS[$forced])){forward($req,$res,$forced,$stats);return;}// ── 策略2白名单用户走v2 ─────────────────────────────────// 用户ID从JWT解析出来的Header里读配合前面的auth网关$userId(int)($req-header[x-user-id]??0);if($userIdin_array($userId,V2_WHITELIST,true)){forward($req,$res,v2,$stats);return;}// ── 策略3按比例稳定分流 ─────────────────────────────────// 用用户ID取模没有用户ID就用IP// 取模保证同一个用户每次都落到同一个版本体验一致// 不能用随机数随机数会导致同一个用户一会v1一会v2很割裂$seed$userId?:ip2long($req-server[remote_addr]);$bucketabs($seed)%100;// 0~99$versionv1;$cursor0;foreach(VERSIONSas$v$cfg){$cursor$cfg[weight];if($bucket$cursor){$version$v;break;}}forward($req,$res,$version,$stats);});functionforward(Request$req,Response$res,string$version,Table$stats):void{$cfgVERSIONS[$version];$stats-incr($version,count);$cnewClient($cfg[host],$cfg[port]);$c-set([timeout10]);$headers$req-header??[];$headers[x-ab-version]$version;// 告诉后端当前是哪个版本方便日志$c-setHeaders($headers);$c-setMethod($req-server[request_method]);$body$req-rawContent();if($body!$body!false){$c-setData($body);}$qs$req-server[query_string]??;$uri$req-server[request_uri].($qs!??$qs:);$c-execute($uri);if($c-statusCode0){$res-status(502);$res-end(json_encode([errorbackend unavailable,version$version]));$c-close();return;}$res-status($c-statusCode);$res-header(X-AB-Version,$version);// 前端可以看到自己在哪个版本foreach($c-headers??[]as$k$v){$res-header($k,$v);}$res-end($c-body);$c-close();}$server-on(workerStart,function($s,$wid){if($wid0){echoA/B Router → http://0.0.0.0:9800\n;echov1(95%) → 8081 v2(5%) → 8082\n;echo统计: curl http://localhost:9800/_stats\n;}});$server-start();

相关新闻