Skip to content

默认值与回退

ABetterChoice SDK 的设计原则是:永不阻塞应用热路径。当平台不可达时,SDK 会按下面三层依次 回退:

  1. 上一次成功轮询写入的内存缓存;
  2. 客户端 SDK 在磁盘上保留的预计算包;
  3. 你在读取参数 / 开关时传入的默认值

本页描述每一层的行为,帮助你挑出安全的默认值,并清楚平台抖动时代码会发生什么。

下文 Go 与 HTTP 部分代表今天已正式可用的接入方式。Android、iOS、JavaScript、C++ 部分是 形态预览,这些 SDK 在 Settings → SDK&Key 下都是 Coming soon

读取 API 的形态

每个 SDK 都暴露带类型的读取 API,并接受默认值。当参数 / 开关缺失、网络断开、实验已归档时, 返回的就是这个默认值。

go
// Go
show := exp.GetBoolWithDefault("should_show_banner", false)
size := exp.GetInt64WithDefault("size", 16)
cpp
// C++
auto exp = AbcService::GetInstance()->GetExperiment("6666", "abc_layer_name", WithSetUnitID(uid));
std::string color = exp.GetString("color");        // 缺失时返回空串
java
// Android
AbcExpInfo info = mAbcExperiment.getExperiment("abc_layer_name");
boolean show = info.getBooleanValue("should_show_banner", true);
String  copy = info.getStringValue("copy", "Default copy");
javascript
// JavaScript
const exp = abc.getExperiment("abc_layer_name", true);
const show = exp.getBoolValue("should_show_banner", true);

默认值是契约的一等成员:哪怕 unit_id 永远没被分到任何实验,这个值也要让系统继续保持 正确。

回退链路

默认值回退链路:先查内存缓存,再查磁盘包,最后回退到调用方传入的默认值

实践中,你只会在以下场景看到默认值:

  • 初始化尚未完成的第一次调用(最好在 init 完成后再读);
  • 网络故障时长超过 SDK 容忍窗;
  • 该参数 / 开关从未返回过值(拼写错误、已归档、已删除)。

服务端 SDK 行为

服务端 SDK 每 3 秒轮询并把配置缓存在内存。一次轮询失败时,SDK 继续用上一次的缓存。没有磁盘 持久化 —— 进程重启时,初始化必须先成功,读取才能拿到真实值。

实操建议:

  • 让初始化以有限超时阻塞(默认 15 秒),避免空缓存就开始服务流量;
  • 为 "init succeeded" / "fell back to defaults" 各打一个指标,避免静默退化;
  • 默认值不要比对照组更激进 —— 平台静默时用户看到的就是它。

客户端 SDK 行为

客户端 SDK 把上一次成功的包持久化到本地(Android / iOS 用 MMKV,Web 用 localStorage)。下次 启动时:

  • 立刻返回持久化值,避免 UI 闪现默认值;
  • 后台请求刷新缓存;
  • 若刷新失败,读取继续返回持久化值;只有当本地文件缺失或损坏时,才落到调用方默认值。

实操建议:

  • UI 切换以参数为准,不要依赖"网络是否就绪"信号 —— SDK 已处理重试;
  • 默认值要匹配对照体验,让首次启动用户看到与回访用户一致的 UI;
  • 长 Web 会话上保持 enableAutomaticPoll: true,会话期内推送的更新也能在 10 分钟内生效。

HTTP API 行为

HTTP API 没有客户端缓存。每次调用都是新请求:

  • 传输失败时调用返回错误,由客户端选用默认值;
  • ret_code == 100exp_data 中缺该层时,说明该 unit_id 在这一层未分到任何实验, 使用默认值;
  • ret_code != 100 时按调用失败处理并使用默认值。完整含义见 HTTP API 端点

选一个安全的默认值

照下面三条做就不会后悔:

  1. 默认值始终选 control 分支,不要选 treatment。如果 treatment 会破坏用户流,默认值要 保证流不破。
  2. 使用静态默认值,不用"最常见的分组"。静态值可复现、可测试;动态值在故障时会自己漂移。
  3. 类型严格匹配。对 Boolean 参数传字符串默认值是未来的 bug —— 一直用上面的强类型 API 就 能避免。