同城生活小程序后台把跑腿业务关了,用户端首页仍显示跑腿图标——多半是端缓存了旧配置,或配置中心变更没有触发客户端失效。云上部署时,配置发布与缓存策略要和业务开关同一套口径,否则运营改十次开关,用户看到的仍是上线第一天的菜单。
本文谈配置中心写入、版本号、端侧缓存失效与验收。不绑定具体面板菜单路径。
一、配置最少要有什么字段
{
"config_version": "20260925.4",
"biz_switches": {
"waimai": true,
"paotui": false,
"tuangou": false
},
"home_entries": [
{
"code": "waimai", "route": "/pages/waimai/index" },
{
"code": "paotui", "route": "/pages/paotui/index" }
],
"updated_at": "2026-09-25T11:30:00+08:00"
}
缺少 config_version 时,端无法判断本地缓存是否过期,只能等 TTL 自然失效——开城当天不可接受。
二、配置中心写入:原子 bump 版本
后台改开关时,宜事务内完成:
def publish_biz_switch(biz: str, enabled: bool):
with db.transaction():
cfg = load_config_for_update()
cfg["biz_switches"][biz] = enabled
cfg["config_version"] = next_version(cfg["config_version"])
cfg["updated_at"] = now_iso()
save_config(cfg)
write_audit(biz, enabled, cfg["config_version"])
purge_cdn_config_cache(cfg["config_version"]) # 若走 CDN 缓存
禁止只改布尔值不 bump 版本;也禁止多实例各写各的,导致读到旧值。
三、端缓存策略:版本优先于 TTL
小程序本地可缓存整包配置,但拉取逻辑建议:
const CACHE_KEY = "app_config";
async function loadConfig(): Promise<AppConfig> {
const local = wx.getStorageSync(CACHE_KEY) as AppConfig | "";
const localVer = local?.config_version ?? "0";
const remote = await request<{
config: AppConfig; not_modified?: boolean }>({
url: "/api/app/config",
header: {
"If-None-Match": localVer },
});
if (remote.not_modified && local) return local;
wx.setStorageSync(CACHE_KEY, remote.config);
return remote.config;
}
服务端对 If-None-Match 返回 304 或 { not_modified: true },减少流量;版本变更时必须返回新包。
TTL 只作兜底(如 24h 强制刷新),不能替代版本对比。
四、CDN 与 API 网关层的缓存
若配置走 CDN 或网关缓存:
/api/app/config
Cache-Control: private, max-age=60
ETag: "20260925.4"
后台 bump 版本后:
- 源站 ETag 变
- 调用 CDN 刷新该 URL(或带 version query)
- 端
If-None-Match命中新 ETag
禁止 CDN 长缓存无 purge,导致全国用户几天看见旧菜单。
五、失效验收:关开关后多久端上消失
开城前演练:
# 1. 记录当前 version
curl -s /api/app/config | jq .config_version
# 2. 后台关 paotui
# 3. 再次 curl,version 应变化
curl -s /api/app/config | jq .biz_switches.paotui
# 4. 真机 kill 小程序进程,重进,首页无跑腿
通过标准:
- 版本 bump 后 5 分钟内,新安装/冷启动用户必见新菜单
- 已打开用户:onShow 拉配置或 TTL 兜底内更新,不宜超过约定 SLA(如 15 分钟)
- 深链未开通路由仍被拦截
六、与业务开关、商家端一致
配置中心宜单源:biz_switches 同时驱动用户端 home、商家端 Tab、可选的后台菜单。商家端若独立缓存,须同一 config_version 机制,避免用户端已隐藏、商家端仍见跑腿 Tab。
七、日志与排障
配置发布宜记结构化日志:
{
"event": "config_publish",
"config_version": "20260925.4",
"changed": ["biz_switches.paotui"],
"operator": "ops_01",
"ts": "2026-09-25T11:30:01+08:00"
}
用户投诉「菜单不对」时,五分钟内应能答:当前线上 version、该用户本地 version(若上报)、CDN 是否已 purge。
八、光合同城边界
光合同城同城生活小程序为成品系统,配置与业务开关可在私有化环境部署;多业务扩展同一后台。支持源码交付;业务规则由客户确定;系统侧不抽成客户平台订单。
九、常见踩坑
坑 1:只依赖 wx.setStorage 永不过期
运营改开关,老用户永远看见旧菜单。
坑 2:CDN 缓存 24h 未 purge
源站已关模块,边缘节点仍返回旧 JSON。
坑 3:多实例配置不一致
负载均衡打到不同版本,用户时有时无。
坑 4:无 ETag
端每次全量拉包,浪费流量;或从不拉包,永远旧数据。
坑 5:开关变更无审计
出问题无法追溯谁把 paotui 打开又关上。
十、多实例与数据库读一致
配置存库时,多应用实例可能读到不同快照。宜:
应用实例 A/B/C
-> 读 config 时带 config_version
-> 本地短缓存 30s + version 校验
-> 写 config 仅经单点 API,事务 bump version
def get_config_cached():
ver, body = cache.get("app_config")
remote_ver = db.scalar("SELECT config_version FROM app_config LIMIT 1")
if ver != remote_ver:
body = load_config_from_db()
cache.set("app_config", (remote_ver, body), ttl=30)
return body
禁止各实例各自改 JSON 文件;否则用户刷新一次换一个菜单。
十一、与用户投诉联动的埋点
端上宜上报:local_config_version、remote_config_version、last_fetch_ts。客服台查询单用户时,能判断是「未拉到新配置」还是「后台未改」。开城第一周,配置拉取失败率应单独告警,与支付回调失败同级对待。
十二、冷启动与热启动差异
冷启动必须拉配置后再渲染 home,避免先闪全菜单再隐藏。热启动 onShow 时对比 version,不一致则静默刷新菜单。真机验收时两种路径都要测:杀进程重进、后台切前台,均不应出现未开通入口闪现超过 1 秒。
十三、小结
同城生活小程序配置中心与端缓存失效,靠 config_version、ETag/304、CDN purge 与 onShow 刷新组合,而不是等 TTL 碰运气。后台关了业务,端上仍显示入口——先查版本有没有 bump、CDN 有没有刷新、本地缓存有没有对比版本。首期只开外卖,把「关开关后 5 分钟真机验收」写进上线清单,比事后解释「用户清缓存就好」更靠谱。