为什么你的A/B测试可以不用动后端代码?
故障定位思路
如果你正在处理CDN A,先别急着照搬网上的参数。当你需要验证一个新功能、一个新UI或者一套新算法时,通常的做法是:在后端写一个分流逻辑,根据用户ID或IP分配版本,然后发布上线。这个过程至少有三个痛点:
- 开发与运维协同成本高——改一行代码可能需要走完整的CI/CD流程。
- 后端性能被分流逻辑拖累——每次请求都要判断一次版本归属。
- 缓存失效影响实验结果——CDN缓存一旦命中,所有用户看到同一个版本,分流失效。
这些问题有一个共同的解决方案:把分流逻辑放到CDN边缘节点上。通过边缘脚本(EdgeScript / EdgeWorker / Cloudflare Workers 等),你可以在用户请求到达源站之前,就决定让它访问A版还是B版。整个过程不需要修改源站代码,也不需要重新部署应用。
先搞清两个基础概念
什么是CDN边缘脚本?
CDN的边缘节点不再是只做缓存和转发,现在很多CDN提供商允许你在节点上运行轻量级的JavaScript、Lua或Python脚本。这些脚本在用户请求到达节点时立即执行,可以改写请求、响应头、URL,甚至直接返回内容而不回源。这就是“边缘计算”的一种典型形态。
边缘脚本的优势是部署快(秒级生效)、延迟极低(节点离用户近)、不占用源站资源。对于A/B测试这种需要实时决策且流量规模大的场景,边缘脚本是非常合适的执行载体。
Cookie如何帮助路由?
HTTP Cookie是服务端存储在客户端(浏览器)中的小段文本,每次请求时都会自动携带。在A/B测试中,我们可以把用户被分配的“版本号”存在Cookie里。这样,无论用户刷新多少次页面,边缘脚本都能从Cookie中读出版本号,把它引导到对应的后端或资源上。这就实现了用户级的一致分流——同一个用户在测试期间始终看到同一个版本。
想继续深入:此处可内链到“CDN A优化清单”文章。
边缘脚本实现A/B测试的核心流程
故障定位思路
整个逻辑可以概括为三个步骤:
- 接收请求时判断Cookie —— 边缘脚本检查请求头中的Cookie字段是否包含“ab_test”键。
- 按规则分配版本 —— 如果没有Cookie,则根据随机算法(或哈希取模)分配A或B,并将结果写入Set-Cookie头。
- 根据版本改写请求或响应 —— 将版本信息传递给源站(如改URL、加请求头),或者直接返回不同版本的静态资源。
进阶阅读:此处可内链到“CDN A性能优化”指南。
CDN A:手把手:一个简单的边缘脚本示例(伪代码+说明)
故障定位思路
下面以类JavaScript的边缘脚本(多数CDN支持的语法)为例,展示核心逻辑。请注意,不同CDN平台API名称略有差异,但思想一致。
// 边缘脚本处理函数
function handleRequest(request) {
// 1. 获取Cookie中的版本号
let version = getCookieValue(request.headers.get('cookie'), 'ab_test');
// 2. 如果没有版本号,则随机分配并设置Cookie
if (!version) {
version = Math.random() < 0.5 ? 'A' : 'B';
// 设置Cookie,有效期1天
responseHeaders.set('Set-Cookie', `ab_test=${version}; Path=/; Max-Age=86400`);
}
// 3. 根据版本修改请求路径
let newUrl = request.url;
if (version === 'B') {
newUrl = request.url.replace('/page', '/page-b');
}
// 4. 转发修改后的请求到源站
return fetch(newUrl, {headers: request.headers});
}
这段代码做了三件事:
- 读Cookie:从请求中提取“ab_test”的值。如果不存在,则进入分配逻辑。
- 分配并固化:使用Math.random()以50%概率分配A/B,然后通过Set-Cookie让浏览器记住这个分配结果。
- 路由改写:如果是B版本,就把URL中的“/page”替换成“/page-b”,这个新URL可能对应B版本的后端服务或静态文件。
这样,后续所有来自同一用户的请求都会带上ab_test Cookie,边缘脚本直接读取版本号并转发到正确的后端。源站不需要任何分流逻辑。
相关阅读:此处可内链到“CDN A常见问题”专题。
更高级的场景:动态路由到不同后端集群与CDN A
实际操作要点
有些团队希望A/B测试不只是换页面,而是切换整个后端API集群(比如A版用旧推荐算法,B版用新推荐算法)。这时,边缘脚本可以改写Host头或URL前缀,让请求路由到不同的源站组。例如:
if (version === 'B') {
// 替换Host为B版集群入口
request.headers.set('Host', 'b-cluster.example.com');
}
或者配合CDN的“回源规则”(源站池),边缘脚本动态选择回源组。这种方式可以在不触及DNS的前提下,实现微服务级别的灰度路由。
你需要特别注意的几个坑
缓存污染问题
如果CDN节点对同一URL缓存了A版本的内容,而后续另一个用户的Cookie是B版本,但缓存命中后直接返回了A版内容,这就破坏了实验的准确性。解决方案有两种:
- 不缓存实验页面:设置Cache-Control: no-cache或private,适用于动态页面。
- 在边缘脚本中修改URL:把版本号(如?ab=A或?ab=B)拼入缓存键,使A/B分别缓存。但要注意URL对SEO的影响。
Cookie过期与用户一致性
如果Cookie有效期太短,用户在下一次请求时可能被重新分配版本,导致同一个用户在测试期内看到多个版本,影响数据准确性。建议设置一个足够长的过期时间(如7天),或者在Cookie中同时记录实验ID和版本号。
灰度比例与流量平滑
不要直接用50%硬编码。实际生产中,你可能想先放1%的流量到B版本,观察无异常后再逐步提升比例。可以用一个可配置的变量(如“trafficPercent”)来控制。
验证与回滚
边缘脚本部署后,通过curl带不同Cookie测试能否路由到正确版本。同时,脚本应包含“降级逻辑”:如果出现异常(如无法获取Cookie),默认走A版本,保证用户体验不中断。
边缘脚本 vs 传统A/B测试工具
故障定位思路
市面上有很多A/B测试SaaS(如Google Optimize、Optimizely)本质是通过客户端JavaScript或服务端SDK实现的。边缘脚本的优势在于:
- 对源站零侵入——不需要引入第三方JavaScript,也不修改后端代码。
- 性能更好——决策在CDN边完成,不增加源站延迟。
- 更易规模化——CDN节点分布全球,分流逻辑全局统一。
- 成本更低——不需要为A/B测试额外购买服务(多数CDN的边缘计算包含在套餐内或按请求量计费)。
但缺点同样存在:边缘脚本的调试、日志和监控能力相对较弱,复杂的分流逻辑(如基于用户标签的多维组合)实现起来不如服务端SDK灵活。因此,边缘脚本最适合“简单、高频、对延迟敏感”的A/B测试场景。
补充参考:此处可内链到“CDN A故障排查实例”。
落地步骤建议
先看关键判断
- 选CDN平台:确认你的CDN是否支持边缘脚本(EdgeScript、EdgeWorker、Workers等)。阿里云CDN、腾讯云CDN、Cloudflare、Akamai等主流产品均支持。
- 编写测试脚本:按上面示例实现最简版本,用curl测试Cookie读写是否正确。
- 小流量灰度:先只对1%的请求启用脚本,观察是否有异常。
- 收集数据:在源站(或通过CDN日志)记录每个请求携带的版本号,与业务指标关联分析。
- 迭代优化:根据实验结论决定是否全量切换B版本,并清理边缘脚本或配置。
总结
我的处理经验
利用CDN边缘脚本做A/B测试,本质是把分流决策权从源站上移到边缘,通过Cookie保持用户一致性。它让前端团队可以独立做灰度实验,后端只需部署不同的版本,两者通过边缘脚本解耦。对于中小团队来说,这是一种零成本、高灵活性的实验方式。理解了这个思路后,你还可以扩展到“基于地理位置的动态内容分发”、“不同设备类型返回不同资源”等更多边缘计算场景。后续只要定期检查关键指标,CDN A就不会变成维护负担。
延伸阅读
