A/B测试的核心诉求很简单:让一部分用户看到方案A,另一部分用户看到方案B,再通过数据对比判断哪个方案效果更好。听起来容易,但真正落地时,分流逻辑放在哪里往往成为第一个难题。放在客户端,意味着每次实验都要发版;放在源站服务器,分流压力会集中到后端;自建分流网关,又要多维护一套基础设施。腾讯云CDN提供的Edge Functions(边缘函数)功能,基于SCF无服务器架构运行在遍布各地的边缘节点上,恰好能把分流这件事前置到离用户最近的地方,本文就来详细拆解这套方案的落地过程。

边缘函数做A/B测试的工作原理
要理解这套方案,先要弄清楚请求在接入边缘函数后的完整链路。用户发起HTTP请求后,请求首先到达距离最近的CDN边缘节点。边缘节点判断该加速域名是否配置了触发规则,如果命中规则,就会执行对应的边缘函数代码。函数代码可以读取请求头、Cookie、URL参数,也可以修改响应内容、注入响应头,甚至直接在边缘生成响应,不再回源。
用在A/B测试场景时,典型的流程是这样的:边缘函数首先检查请求中是否携带实验分组Cookie,如果没有,则按预设比例(比如50%对50%)随机分配一个分组,并通过Set-Cookie响应头把分组结果写入浏览器;如果已经有分组Cookie,就直接读取分组结果保持稳定。接着函数根据分组决定回源路径,比如A组请求原始页面,B组请求实验页面,或者干脆在边缘对HTML内容做字符串替换。整个过程对源站透明,用户端也只看到一个域名。
相比传统方案,这种模式的最大优势在于变更成本低。实验配置全部集中在函数代码里,调整分流比例、增加实验组、下线实验,都只需要更新边缘函数,不需要动源站,更不需要客户端发版。同时函数运行在边缘节点,分流计算几乎不增加额外延迟,这个特点是中心化分流服务很难做到的。
编写边缘函数的分流逻辑代码
腾讯云边缘函数采用JavaScript语法,入口函数接收一个event对象,包含请求信息,函数返回值就是响应内容。下面是一段可以直接使用的分流代码示例,实现了Cookie分组、比例控制和路径改写三个核心逻辑。
// 实验配置
const EXPERIMENT = {
name: "exp_btn_color", // 实验名称
ratioB: 50, // B组流量百分比
pathA: "/index.html", // A组路径
pathB: "/index_v2.html" // B组路径
};
async function handle(event, context) {
const request = event.request;
const cookieHeader = request.headers["cookie"] || "";
const cookieKey = "ab_" + EXPERIMENT.name;
// 从Cookie中解析已有分组
const match = cookieHeader.match(
new RegExp(cookieKey + "=(A|B)")
);
let group;
if (match) {
group = match[1]; // 已分组,保持稳定
} else {
// 按比例随机分配
group = Math.random() * 100 < EXPERIMENT.ratioB ? "B" : "A";
}
// 根据分组改写回源路径
let uri = request.uri;
if (uri === EXPERIMENT.pathA) {
uri = group === "B" ? EXPERIMENT.pathB : EXPERIMENT.pathA;
}
// 通过自定义回源头传递分组信息,便于源站或日志识别
request.headers["x-ab-group"] = group;
// 携带Set-Cookie下发分组结果
const response = await fetch({
url: "http://origin-source" + uri,
headers: request.headers,
method: request.method,
body: request.body
});
response.headers["set-cookie"] =
cookieKey + "=" + group + "; Path=/; Max-Age=2592000";
return response;
}
exports.main = handle;这段代码有几个细节值得注意。第一,分组必须依赖Cookie而不是随机数,否则同一个用户刷新页面可能在A、B两组之间反复横跳,实验数据就全废了。Cookie有效期建议设置为30天左右,覆盖一个完整的实验周期。第二,通过x-ab-group自定义回源头把分组信息传给源站,源站的访问日志就能直接统计各分组的转化数据,不需要额外的埋点系统。第三,路径改写只针对实验页面本身,其他请求原样透传,避免影响无关资源。
如果实验对象只是页面上的小改动,比如按钮文案或配色,还可以采用另一种更轻量的做法:不做路径改写,而是在边缘函数拿到响应后,对HTML内容做字符串替换,直接把立即购买替换成马上抢购。这种方式连实验页面都不用准备,但要注意替换逻辑对响应体大小的限制,边缘函数处理大文件时性能会下降。
在CDN控制台完成配置联动
代码写好后,接入步骤并不复杂。先在腾讯云SCF控制台的左侧导航中找到边缘函数入口,创建函数并粘贴代码,保存后系统会在几分钟内把代码分发到全国边缘节点。接下来进入CDN控制台,在目标域名的边缘模块中配置触发规则,支持按路径、文件类型、请求头等条件匹配,A/B测试一般配置为命中实验页面的具体路径。
规则生效后建议先做一轮验证:用curl命令带上和不带分组Cookie分别请求,观察响应头中的set-cookie字段和回源日志中的分组标记是否符合预期。确认无误后再逐步放量,可以先设置ratioB为5%观察一段时间,没有异常再调整到目标比例。
灰度期间要重点关注错误监控。边缘函数执行失败时CDN会执行降级逻辑,直接回源返回原始内容,这在业务上是安全的,但意味着部分用户实际没被分组。如果发现降级次数异常升高,通常是代码里存在未捕获的异常,比如请求头字段缺失导致的类型错误,需要在代码里对 headers 的取值做充分的空值保护。
缓存一致性与数据统计两个关键坑
第一个坑是缓存。CDN默认会缓存静态页面,如果A、B两个分组的响应内容不同,但缓存键完全相同,就会出现B组用户刷出A组页面的问题。解决办法是在分流时给不同分组打上不同的缓存标识,比如函数中主动设置vary响应头为x-ab-group,或者在回源路径上附加查询参数?ab=A,确保两个版本的内容在边缘节点分别缓存。同时实验页面在源站的缓存配置要与函数逻辑对齐,建议实验期间对相关路径设置较短的缓存时间,保证实验版本更新能快速生效。
第二个坑是数据统计口径。A/B测试最终比的不是访问量而是转化率,如果只靠CDN侧的分组日志,只能知道每组有多少请求,无法知道后续的转化行为。完整方案需要把分组Cookie同步到业务侧:用户进入页面后,前端JS读取分组Cookie并通过埋点上报,后续的下单、注册等转化事件都携带分组标识,这样分析系统才能算出每组的转化率并做显著性检验。分组标识本身也要纳入埋点体系的公共属性,避免每个实验都重复上报。
还有一个统计上的常见误区要提醒:分流比例是按请求随机分配时,同一个用户多次访问可能被归入不同请求,虽然Cookie机制保证了用户维度的一致性,但如果统计时按请求数而不是按独立用户数计算,结果会出现偏差。实验设计阶段就应该明确统计单元是用户而不是请求,这直接决定了样本量的计算和实验周期的预估。
与其他分流方案的对比
最后横向对比一下主流方案。客户端分流(前端JS判断分组再渲染不同版本)实现最简单,但分流逻辑暴露在代码里,用户可以感知甚至干预,且每次实验调整都要发版,适合纯前端UI实验。网关分流(在Nginx或API网关层做流量切分)功能强大,控制粒度细,但需要维护中心化网关资源,分流请求要额外经过一层转发,架构复杂度上升。边缘函数分流则在两者之间取了平衡:配置即代码,变更灵活;运行在边缘,无中心化瓶颈;配合CDN的缓存能力,性能表现也最好。
当然它也有局限。边缘函数适合逻辑相对简单的分流场景,如果实验需要复杂的用户画像判断,比如只对某地区、某会员等级的用户开启实验,就需要函数内调用额外的数据服务,这会增加回源延迟。这种情况下更稳妥的做法是边缘只负责打分组标记,复杂判断放到源站完成。总体而言,对于中轻度的A/B测试需求,腾讯云CDN加SCF边缘函数的组合是一套上手快、成本低、可观测性也够用的方案,值得在实际项目中尝试。