简介
维护横幅应在公告窗口结束时消失。如果继续显示,访问者可能会误以为旧故障仍在发生。你将构建一个 Worker,从 KV 读取通知,并判断是否仍应显示该通知。
这里有两个独立的截止时间。应用截止时间告诉代码何时停止显示消息;KV 过期时间告诉存储服务何时删除条目。你会在存储中保留一条有意设置为过期的参考记录,以证明即使数据仍然存在,应用也能隐藏已过期的内容。然后,你会观察云端 KV 中的第二条记录自动过期。
请先完成「提供账户偏好设置」。这台独立 VM 已安装 Node.js 22.22.0 和项目本地的 Wrangler 4.131.1,项目位于 /home/labex/project/temporary-notices。使用你自己的学习账户,并保留相同的账户读取、Worker 写入和 KV 写入权限。你将创建一个临时 Worker 和命名空间,只使用模拟消息,并在退出登录前完成清理。这个小练习不需要购买域名或升级付费计划。除了编写和测试处理程序外,还需要为定时观察预留大约五分钟。
连接通知命名空间
在本步骤中,你将为临时通知连接一个全新的命名空间。使用独立的命名空间和唯一的 Worker 名称,避免测试过期机制时删除其他应用的数据。NOTICES 绑定会将处理程序连接到这个资源。
进入准备好的项目目录:
cd /home/labex/project/temporary-notices
生成一次唯一名称。openssl rand -hex 6 会输出随机后缀;$(...) 会将该后缀插入名称中。Shell 变量会让后续命令在当前终端中继续使用这个名称。
WORKER_NAME="labex-notices-$(openssl rand -hex 6)"
printf '%s\n' "$WORKER_NAME"
为这台 VM 授权。除了读取账户身份之外,Workers Scripts Write 权限允许部署和删除资源,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 代码块将两个 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-notices" --update-config=false
输出中会包含新命名空间的 ID。复制该 ID,然后在下面这份完整配置中替换 YOUR_ACCOUNT_ID 和 YOUR_NAMESPACE_ID。NOTICES 是代码使用的绑定名称;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": "NOTICES", "id": "YOUR_NAMESPACE_ID" }
]
}
JSON
npx wrangler kv namespace list
找到本实验的命名空间标题,并将其 ID 与配置文件中的 ID 进行比较。列表中可能还有其他命名空间;不要修改它们。这个配置记录了后续命令应使用的账户和资源。绑定是对命名空间的引用,不是命名空间数据的副本。
在删除旧通知数据前隐藏它
在本步骤中,你将把显示行为与存储清理分开。时间戳是表示某个时间点的数字。这里的 displayUntil 使用 Unix 秒,即从 1970 年开始按 UTC 计算的秒数。Date.now() 返回毫秒,因此处理程序会先除以 1000 再进行比较。使用相同的时间单位可以避免常见的截止时间错误。
使用下面带引号的 here-document 编写处理程序:
cat > src/index.js <<'JS'
export default {
async fetch(request, env) {
const url = new URL(request.url);
const key = url.searchParams.get("key") ?? "notice:maintenance";
if (url.pathname !== "/notice" || !/^notice:[a-z]{1,20}$/.test(key)) {
return new Response("Not found", { status: 404 });
}
let entry;
try {
entry = await env.NOTICES.getWithMetadata(key, "text");
} catch {
return Response.json({ error: "Notice storage unavailable" }, { status: 503 });
}
if (entry.value === null) {
return Response.json({ visible: false, reason: "missing" });
}
let notice;
try {
notice = JSON.parse(entry.value);
} catch {
return Response.json({ visible: false, reason: "invalid" });
}
if (!notice || typeof notice.message !== "string" || !notice.message.trim() ||
!Number.isSafeInteger(notice.displayUntil) || notice.displayUntil <= 0) {
return Response.json({ visible: false, reason: "invalid" });
}
if (Math.floor(Date.now() / 1000) >= notice.displayUntil) {
return Response.json({ visible: false, reason: "expired" });
}
return Response.json({
visible: true, message: notice.message,
kind: entry.metadata?.kind === "maintenance" ? "maintenance" : "general"
});
}
};
JS
key 查询参数用于选择模拟通知;如果不提供该参数,处理程序会使用 notice:maintenance。应用会通过带有明确原因的 JSON 响应隐藏缺失、格式错误和已过期的通知。KV 读取失败时会返回 503,而不是假装通知不存在。元数据字段 kind 用于标记通知类型;如果元数据缺失或值不符合预期,则回退为 general。
截止时间比较使用 >=:通知会在截止时间到达时隐藏,而不是再延后一秒。这项检查会在每次请求时执行。已经显示横幅的网页还需要使用自己的计时器刷新或移除横幅;Worker 响应本身无法修改已经渲染的页面。
在本地保存一条有意设置为旧的参考通知。截止时间 1 表示 1970 年的一个确定时刻,因此从应用角度看,这条记录已经过期。我们故意不设置 KV 过期时间,使这条记录仍可供检查。
npx wrangler kv key put notice:reference '{"message":"Old maintenance notice","displayUntil":1}' --binding NOTICES --local --metadata '{"kind":"maintenance"}'
npx wrangler dev --local --ip 0.0.0.0 --port 8080 > local.log 2>&1 &
DEV_PID=$!
cat local.log
后台服务器会将输出写入 local.log。重复运行日志命令,直到日志报告服务器已在端口 8080 就绪。现在请求旧的参考通知:
curl -i 'http://127.0.0.1:8080/notice?key=notice:reference'
预期返回 HTTP 200 和 {"visible":false,"reason":"expired"}。引号可以避免 URL 中的问号被 Shell 当作文件名语法处理。证明该条目仍然存在:
npx wrangler kv key get notice:reference --binding NOTICES --local --text
JSON 数据仍然存在。是你的代码,而不是自动删除机制,阻止了旧通知显示。缺失的键也应安全处理:
curl -i 'http://127.0.0.1:8080/notice?key=notice:missing'
预期返回 {"visible":false,"reason":"missing"}。在清理前保持本地服务器运行。
使用两个截止时间发布通知
在本步骤中,你将先发布 Worker,然后开启一个较短的云端通知窗口。在启动计时前准备好端点,可以留出时间检查实时结果。
在远程命名空间中创建相同的、不会过期的参考通知:
npx wrangler kv key put notice:reference '{"message":"Old maintenance notice","displayUntil":1}' --binding NOTICES --remote --metadata '{"kind":"maintenance"}'
npx wrangler deploy
确认生成的 Worker 名称和 NOTICES 绑定,然后从输出中保存实际的公共 URL:
WORKER_URL="https://YOUR_WORKER.YOUR_SUBDOMAIN.workers.dev"
curl -i "$WORKER_URL/notice?key=notice:reference"
预期旧参考通知会以 expired 原因隐藏。如果主机名尚未就绪,请等待并重试,然后再开始定时部分。暂时不要请求默认的维护通知键:缺失 KV 键的读取也可能被缓存。
运行下一组命令前,先阅读剩余说明。date +%s 会返回 VM 当前的 Unix 时间;$((...)) 会执行 Shell 算术。我们会在三分钟后停止显示通知,再让 KV 在一分钟后删除它。
DISPLAY_UNTIL=$(($(date +%s) + 180))
KV_EXPIRES=$((DISPLAY_UNTIL + 60))
写入实际的应用数据。未加引号的 JSON 分隔符会将数字形式的截止时间插入文件:
cat > notice.json <<JSON
{"message":"Maintenance starts soon","displayUntil":$DISPLAY_UNTIL}
JSON
--path 会从该文件读取值。--expiration 使用 Unix 秒设置绝对 KV 过期时间;--metadata 会在值旁边添加通知类别。
npx wrangler kv key put notice:maintenance --path notice.json --binding NOTICES --remote --expiration "$KV_EXPIRES" --metadata '{"kind":"maintenance"}'
KV 也支持相对 TTL(存活时间),以写入时刻之后的秒数表示。Wrangler 将该选项称为 --ttl;绑定 API 将其称为 expirationTtl。相对过期和绝对过期都必须至少比当前时间晚 60 秒。这里使用绝对过期时间,方便你直接比较两个截止时间。请参阅 KV 过期选项。
npx wrangler kv key list --binding NOTICES --remote
找到 notice:maintenance、它的 expiration 和 kind 元数据。参考通知没有 KV 过期时间。现在读取实时消息:
curl -i "$WORKER_URL/notice"
预期返回 HTTP 200 和 {"visible":true,"message":"Maintenance starts soon","kind":"maintenance"}。在显示窗口结束前立即运行本步骤的检查。该检查会验证实际的云端值、元数据、KV 过期时间、绑定和实时响应。仅保存时间戳不能证明通知已成功存储。
如果错过了窗口,请重新执行两个时间赋值命令,重新写入 notice.json,再使用新的截止时间重复远程写入命令。不要快速重复写入。之前缓存的读取结果可能需要一段时间才能反映替换后的值;请等待并重复主动检查。只有检查通过后才能继续。
有效期内的检查通过后,在同一个学习账户的 Dashboard 中打开 Storage & databases → Workers KV,选择本实验的命名空间。打开 KV Pairs,点击 notice:maintenance 旁的 View,查看消息及 displayUntil 值。KV 删除时间和元数据应通过 CLI 键列表核对;此 Dashboard 视图显示的是已存储的值。这里只进行读取,查看时计时仍在继续。如果键已经过期,请继续下一步,不要仅为查看页面重新创建它。截图中的唯一名称和时间戳只是示例,不要照抄。

观察隐藏内容和自动过期
在本步骤中,你将观察两个截止时间,不手动删除维护通知键。保持 notice.json 不变,以便将原始数据与结果进行比较。
输出计划的两个时间和当前时间:
printf 'displayUntil=%s
KV expiration=%s
now=%s
' "$DISPLAY_UNTIL" "$KV_EXPIRES" "$(date +%s)"
等待当前时间达到 displayUntil。下面的命令只计算剩余等待时间。如果截止时间已经过去,条件判断会跳过休眠。sleep 接收秒数;if 可以避免将负数延迟传给它。
WAIT_SECONDS=$((DISPLAY_UNTIL - $(date +%s) + 1))
if [ "$WAIT_SECONDS" -gt 0 ]; then sleep "$WAIT_SECONDS"; fi
curl -i "$WORKER_URL/notice"
消息不应再显示。在 KV 过期之前,预期返回 {"visible":false,"reason":"expired"}。如果你回来时 KV 已经使该键过期,reason 可能是 missing;这两种结果都会阻止消息显示。保留的参考通知仍可直接检查应用截止时间行为:
curl -i "$WORKER_URL/notice?key=notice:reference"
npx wrangler kv key get notice:reference --binding NOTICES --remote --text
端点会以 expired 原因隐藏参考通知,而 KV 读取仍会返回旧 JSON。这说明即使存储的数据仍然可用,应用截止时间仍然有用。
现在等待 KV 过期时间:
WAIT_SECONDS=$((KV_EXPIRES - $(date +%s) + 1))
if [ "$WAIT_SECONDS" -gt 0 ]; then sleep "$WAIT_SECONDS"; fi
npx wrangler kv key list --binding NOTICES --remote
curl -i "$WORKER_URL/notice"
列表中应只保留 notice:reference;默认端点应返回 {"visible":false,"reason":"missing"}。不要对维护通知键运行删除命令:本次观察的重点是自动过期。如果该条目仍然存在,请以 15 秒为间隔重试只读检查,最长重试两分钟。这只是实验观察窗口,不代表删除时间一定精确。如果状态仍未收敛,请报告结果无法判定,不要声称操作成功。授权错误或网络错误不能证明条目不存在。
KV 过期和读取缓存是两个不同的概念。即使请求了更长的读取缓存时长,过期时间仍然会生效。不过,存储配置的变更可能需要时间传播,因此在先前读取之后写入的新截止时间,并不能保证立即成为全局调度结果。本实验检查的是处理程序实际读取到的记录中包含的截止时间。
删除临时云端资源
在本步骤中,Wrangler 仍处于授权状态时,你将删除两个资源。命名空间的生命周期可以超过 Worker,因此仅删除应用不会清理其中的数据。
停止在当前终端中启动的本地开发进程:
kill "$DEV_PID"
删除任何资源前,先检查已保存的资源引用:
cat wrangler.jsonc
确认其中包含 labex-notices-... Worker 名称和 NOTICES 命名空间 ID。删除此配置选中的 Worker:
npx wrangler delete
如果出现提示,请确认显示的名称与本实验匹配,然后输入 y 确认。接着仅删除 NOTICES 引用的命名空间。这也会删除保留的参考通知记录:
npx wrangler kv namespace delete --binding NOTICES
接受确认提示前,检查其中显示的命名空间。保留 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 元数据中读取通知类别。保留一条旧记录证明了隐藏内容不需要先删除数据。第二条记录则使用绝对时间戳演示了 KV 过期,并通过独立检查验证自动删除。
你区分了显示截止时间、存储过期时间和读取缓存行为,然后删除了临时资源并退出登录。接下来,你将把一个小型重定向目录导入 KV 并进行维护。



