简介
帮助中心可以从 KV 读取主题和欢迎横幅,这样编辑者无需部署新代码就能修改这些设置。更新后,不同位置的读请求可能会在短时间内返回不同版本。应用应在此过渡期间继续可用,而不是假定每次读取都能得到最新设置。
你将构建一个带安全默认值的版本化配置读取器,测试一组经过刻意模拟的旧值和新值,然后执行一次真实的云端更新。版本是与设置一起存储的标签,用于标识你收到的值。它不会让 KV 变成强一致数据库,也不能保证连续请求看到的版本号递增。
请先完成前面的 KV 实验。本独立 VM 已安装 Node.js 22.22.0,并在 /home/labex/project/delayed-config 中提供项目本地的 Wrangler 4.131.1。使用你自己的学习账户,并确保该账户具有相同的 account-read、Worker-write 和 KV-write 权限。本实验会创建一个可丢弃的 Worker 和命名空间,并且只公开合成的显示设置。对于这组小型数据集,不需要付费升级或购买域名。这些设置不会控制授权、支付或其他需要立即获得权威更新的决策。
连接独立的配置存储
在此步骤中,你将为显示配置连接一个新的命名空间。CONFIG 绑定会按照标准 Wrangler 配置保存资源引用。请使用新的实验资源,避免刻意写入的无效设置影响其他应用。
进入准备好的项目目录:
cd /home/labex/project/delayed-config
生成一个唯一名称,只需执行一次。openssl rand -hex 6 会输出随机后缀;$(...) 会将该后缀插入名称。Shell 变量会让后续命令在当前终端中继续使用这个名称。
WORKER_NAME="labex-config-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"
为此 VM 授权。除了读取账户身份之外,Workers Scripts Write 权限还允许部署和删除 Worker,Workers KV Write 权限允许管理本实验的命名空间和键。
npx wrangler login --device --browser=false --scopes account:read user:read workers_scripts:write workers_kv:write
在浏览器中打开显示的设备链接,输入当前代码,检查请求的权限和学习账户,然后授权 Wrangler。许可页面中也可能显示后台访问权限。返回终端并等待登录完成。
检查本课程前面介绍的相同 Worker 和 KV 写入权限,并确认学习账户。
npx wrangler whoami --json
确认输出包含 loggedIn: true 和学习账户的 name,即使只列出了一个账户也要确认。复制该账户的 id。在运行下面的命令前,将配置中的 YOUR_ACCOUNT_ID 替换为该 ID。这里的 cat heredoc 会把两行 JSON 之间的全部内容写入文件;> 会替换文件内容。未加引号的分隔符允许 Shell 插入 $WORKER_NAME。
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true
}
JSON
在该账户中创建一个命名空间。命名空间标题会包含 Worker 的唯一名称,便于你稍后识别这两个资源。--update-config=false 会让你手动看到并编辑绑定,而不是自动修改文件。
npx wrangler kv namespace create "$WORKER_NAME-config" --update-config=false
输出中会包含新命名空间的 ID。复制该 ID,然后在下面这份完整配置中替换 YOUR_ACCOUNT_ID 和 YOUR_NAMESPACE_ID。CONFIG 绑定名称由代码使用;ID 则用于标识真实的 Cloudflare 资源。
cat > wrangler.jsonc <<JSON
{
"name": "$WORKER_NAME",
"main": "src/index.js",
"compatibility_date": "2026-07-30",
"account_id": "YOUR_ACCOUNT_ID",
"workers_dev": true,
"kv_namespaces": [
{ "binding": "CONFIG", "id": "YOUR_NAMESPACE_ID" }
]
}
JSON
npx wrangler kv namespace list
找到本实验的命名空间标题,并将其 ID 与文件中的 ID 对比。其他命名空间可能也会存在,请不要修改它们。此配置记录后续命令应使用的账户和资源。绑定是对命名空间的引用,不是其中数据的副本。
使用安全默认值读取带版本的设置
在此步骤中,你将让两个有效版本都返回可用响应。如果显示设置缺失或损坏,应用会回退到普通的浅色主题,并且不显示横幅。这样,非必需的展示设置就不会导致帮助中心无法使用。
编写处理程序。固定的 defaults 对象不包含请求特定的状态,也不会被修改。每个请求都会读取各自的 KV 结果。
cat > src/index.js <<'JS'
const defaults = { version: 0, theme: "light", banner: "", source: "default" };
export default {
async fetch(request, env) {
const url = new URL(request.url);
if (url.pathname === "/health") return Response.json({ status: "ok" });
const key = url.searchParams.get("key") ?? "config:current";
if (url.pathname !== "/settings" || !/^config:[a-z0-9-]{1,20}$/.test(key)) {
return new Response("Not found", { status: 404 });
}
let value;
try {
value = await env.CONFIG.get(key, { type: "text", cacheTtl: 60 });
} catch {
return Response.json({ error: "Settings temporarily unavailable" }, { status: 503 });
}
if (value === null) return Response.json(defaults);
let settings;
try {
settings = JSON.parse(value);
} catch {
return Response.json(defaults);
}
if (!settings || !Number.isSafeInteger(settings.version) || settings.version < 1 ||
!["light", "dark"].includes(settings.theme) ||
typeof settings.banner !== "string" || settings.banner.length > 80) {
return Response.json(defaults);
}
return Response.json({
version: settings.version, theme: settings.theme,
banner: settings.banner, source: "stored"
});
}
};
JS
对 KV 的请求使用了 cacheTtl: 60,表示读取缓存持续时间,单位是秒。它不会让已存储的键过期,也不会要求所有位置都强制获取最新值。已有值和缺失键的结果都可能被缓存。请减少写入频率,并让应用能够容忍较旧但有效的配置。
处理程序会在使用设置前验证版本、支持的主题和横幅长度。/health 路由不会读取可选设置。如果存储操作失败,/settings 仍会明确返回 503;应用不会错误地报告自己已从存储中成功加载默认值。
创建两个小型版本文件。将每个预期值保存到普通文件中,便于写入前检查:
cat > config-v1.json <<'JSON'
{"version":1,"theme":"light","banner":"Welcome"}
JSON
cat > config-v2.json <<'JSON'
{"version":2,"theme":"dark","banner":"New help center"}
JSON
将它们写入两个独立的本地测试键,再写入一个格式错误的值。这些键可以让可能的输入保持可重复;它们不会模拟 Cloudflare 的网络时序。
npx wrangler kv key put config:v1 --path config-v1.json --binding CONFIG --local
npx wrangler kv key put config:v2 --path config-v2.json --binding CONFIG --local
npx wrangler kv key put config:broken broken-json --binding CONFIG --local
npx wrangler dev --local --ip 0.0.0.0 --port 8080 > local.log 2>&1 &
DEV_PID=$!
cat local.log
等待输出就绪消息。然后分别读取两个明确指定的键:
curl -i 'http://127.0.0.1:8080/settings?key=config:v1'
curl -i 'http://127.0.0.1:8080/settings?key=config:v2'
两次请求都应返回 HTTP 200,并包含 source: "stored"。版本 1 应为浅色主题,横幅为 Welcome;版本 2 应为深色主题,横幅为 New help center。两个版本都不依赖上一次请求的结果。
curl -i 'http://127.0.0.1:8080/settings?key=config:missing'
curl -i 'http://127.0.0.1:8080/settings?key=config:broken'
两次请求都应返回 {"version":0,"theme":"light","banner":"","source":"default"}。版本 0 是应用的默认标签,不是存储在 KV 中的修订版本。
curl -i http://127.0.0.1:8080/health
预期结果是 {"status":"ok"}。在完成清理前,请保持本地服务器运行。
练习受控的旧值读取序列
在此步骤中,你将让处理程序依次面对一组可预测的输入:旧值、新值、旧值、新值、缺失值和无效值。这是一个测试夹具,即刻意提供的输入,用于让原本不可预测的情况可以重复测试。它不能证明真实的 Cloudflare 请求曾返回旧值。
使用标准断言库创建一个小型 Node.js 测试。测试会导入你编写的处理程序,并提供相同的 CONFIG.get() 接口,只是返回值由测试控制:
cat > test-config.mjs <<'JS'
import assert from "node:assert/strict";
import worker from "./src/index.js";
const older = JSON.stringify({ version: 1, theme: "light", banner: "Welcome" });
const newer = JSON.stringify({ version: 2, theme: "dark", banner: "New help center" });
// A controlled fixture: these values simulate different reads, not a cloud outage.
const values = [older, newer, older, newer, null, "broken-json"];
const expectedVersions = [1, 2, 1, 2, 0, 0];
for (let i = 0; i < values.length; i += 1) {
const env = { CONFIG: { get: async () => values[i] } };
const response = await worker.fetch(new Request("https://example.test/settings"), env);
assert.equal(response.status, 200);
const body = await response.json();
assert.equal(body.version, expectedVersions[i]);
assert.ok(["light", "dark"].includes(body.theme));
assert.equal(typeof body.banner, "string");
}
console.log("Controlled old/new/missing/invalid reads stayed usable.");
JS
node test-config.mjs
预期输出为 Controlled old/new/missing/invalid reads stayed usable.。如果断言失败,命令会停止并显示错误。旧值重复出现是刻意安排的:不要添加进程全局的「最新版本」变量来掩盖它。Worker 可能运行在不同实例中,因此这样的变量无法建立账户范围内的最新版本。
配置更新期间,应继续让读取旧配置的请求正常工作。这种方式适用于横幅或主题,但不能让 KV 适合用于立即撤销某人的访问权限。下一步将进行真实的云端测试;第一次请求就可能返回新值,这同样是有效结果。
部署并建立第一个云端版本
在此步骤中,你将先建立一个真实的远程基线,再修改它。只有当前配置和格式错误的回退测试数据应属于本实验的云端命名空间。
npx wrangler kv key put config:current --path config-v1.json --binding CONFIG --remote
npx wrangler kv key put config:broken broken-json --binding CONFIG --remote
npx wrangler deploy
确认唯一的 Worker 名称和 CONFIG 绑定,然后复制实际的公开 URL:
WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/settings"
预期结果是版本 1、主题 light、横幅 Welcome,并且包含 source: "stored"。如有必要,请等待主机名就绪和 KV 完成可见;网络错误不是配置响应。在替换版本 1 之前,运行本步骤的独立检查。该检查会验证所选账户、已存储的值、已部署的绑定、默认值和健康响应。
观察真实更新,但不要求读取到旧值
在此步骤中,你将更新已存储的配置,同时保持 Worker 代码不变。先检查准备写入的新值,再将其写入同一个远程键:
cat config-v2.json
npx wrangler kv key put config:current --path config-v2.json --binding CONFIG --remote
npx wrangler kv key get config:current --binding CONFIG --remote --text
管理端读取结果应包含版本 2。现在检查应用实际看到的内容:
curl -i "$WORKER_URL/settings"
你可能会立即看到版本 2,也可能在读取逐步收敛期间看到仍然有效的旧版本。不要要求必须返回旧值来「证明」最终一致性,也不要为了诱发旧值而快速重复写入该键。如有需要,可以每隔 15 秒重复一次 HTTP 请求,最长观察 5 分钟。这只是本实验限定的观察窗口,并不承诺所有全球位置都会在 5 分钟内完成收敛。
当端点返回 {"version":2,"theme":"dark","banner":"New help center","source":"stored"} 后继续操作。如果在该观察窗口内仍未收敛,请检查账户和绑定,并报告结果无法确定。独立检查要求实际存储版本与实际响应一致;本地文件或测试夹具不能满足这一要求。
curl -i "$WORKER_URL/settings?key=config:broken"
curl -i "$WORKER_URL/health"
损坏的显示配置仍应使用受控默认值,健康状态仍应为 ok。在 Dashboard 中选择同一个账户,然后在 Storage & databases → Workers KV 下查看本实验的命名空间。将 config:current 与文件中的版本 2 进行对比。这个只读视图显示的是托管值;它无法证明每个远程位置当前缓存的内容。
受控测试验证了应用对旧值的容忍能力;这次真实更新则验证了部署,并观察了测试端点上的响应收敛。请分别看待这两项结论。有关 KV 一致性模型,请参阅 KV 的工作方式。
选择 KV Pairs,再点击 config:current 旁的 View。如果列表尚未反映更新,请点击 Refresh。下图显示版本 2、主题 dark 和横幅 New help center;你生成的命名空间名称会有所不同。此处只查看,不要修改值。

删除可丢弃的云端资源
在此步骤中,你将在 Wrangler 仍处于授权状态时删除两个资源。命名空间可以在 Worker 删除后继续存在,因此仅删除应用并不会清理其中的数据。
停止在当前终端中启动的本地开发进程:
kill "$DEV_PID"
删除前先检查保存的资源引用:
cat wrangler.jsonc
确认 labex-config-... Worker 名称和 CONFIG 命名空间 ID。删除此配置选中的 Worker:
npx wrangler delete
如果出现提示,请确认显示的名称与本实验一致,然后输入 y 确认。接着只删除 CONFIG 引用的命名空间:
npx wrangler kv namespace delete --binding CONFIG
接受确认提示前,请检查其中显示的命名空间。保留完整的 wrangler.jsonc,这样独立检查才能识别应当不存在的资源。
npx wrangler kv namespace list
本实验的命名空间应已不存在;不相关的命名空间应保留。刷新 Dashboard 中的列表,确认本实验的 Worker 和命名空间都已消失。请求失败或登录过期都不能证明资源已删除。请在退出登录前运行本步骤的检查,以便检查程序读取经过授权的资源清单。
结束 VM 授权
在此步骤中,你将在清理检查通过后断开 Wrangler。退出登录会结束此 VM 保存的 Wrangler 授权,但不会删除云端资源,也不会让你退出普通的 Dashboard 浏览器会话。
npx wrangler logout
npx wrangler whoami --json
确认结构化结果报告 "loggedIn": false。此时未认证的命令可能以非零退出状态结束,这是预期现象。如果只出现连接错误而没有明确的认证状态,请在连接恢复后重试。
剩余的本地文件和本地 KV 状态属于这个可丢弃的 VM,与已经删除的云端资源相互独立。现在可以结束实验。
总结
你构建了一个配置读取器,它可以接受旧版和新版的有效设置,对缺失值或无效值使用安全默认值,并让健康端点独立于可选的 KV 数据。你测试了一组受控的旧值读取序列,然后修改了一个真实的远程键,并观察已部署应用的响应完成收敛。
你了解到,版本标签用于描述返回的数据,而读取缓存持续时间、过期时间和强一致性属于不同的问题。最后,你删除了可丢弃的资源并退出登录。课程挑战将结合正确的命名空间绑定与安全的通知处理。



