← 系统设计与交付 · 第 3 篇

把服务放到正确的域名后面

沿域名、证书、代理、源站与缓存逐层检查资料站交付,明确多语言缓存、私有响应和托管产品的边界。

学完这一篇,你能做到

设计语言隔离的公开缓存键,并用路由、证书与版本验证表检查域名切换。

资料站准备把预览服务放到公开域名后面,同时提供中文和英文课程页面。浏览器需要先找到服务,再验证连接,最后取得正确版本的内容。域名已经解析成功,只能证明其中一段链路成立,不能说明证书、路由、源站与缓存都正确。

案例使用 /learn/ 作为公开课程入口,/account/ 作为个人页面。前者允许共享缓存,并随语言变化;后者含用户数据,不进入共享缓存。路径是教学契约,没有真实账号或线上测试结果。

先画出请求经过的每一层

一次访问可以经过 DNS 解析、TLS 终止、反向代理或网关、应用源站和存储。CDN 可能在源站之前直接返回已有内容。每一层都可能产生自己的错误,所以诊断时应记录目标域名、协议、路径、响应状态、部署版本与请求标识。

源站负责生成或提供内容,反向代理负责转发,负载均衡器负责在多个后端间选择,网关可能集中实施身份与流量规则。某个平台会把这些功能合并,但职责仍值得分开检查。代理返回的 502 与应用返回的 404 对应不同调查方向。

先确定哪些路由是静态资源,哪些需要应用执行,哪些访问私有对象。把路径契约写出来:/learn/ 应返回课程索引,缺失课程返回真实 404,个人页面需要身份验证。不能让所有未知路径都返回首页而仍声称路由正确。

DNS 只解决名称定位

A 记录把名称映射到 IPv4 地址,AAAA 对应 IPv6,CNAME 指向另一名称。DNS 不包含 URL 路径,也不会自动为服务配置 TLS 证书。把域名指向新机器之后,机器上仍需要服务监听、路由规则和与域名匹配的证书。

解析器与客户端可能缓存记录,TTL 指示缓存的有效时间。更新并非全球同时发生,已有连接也不会因为 DNS 变化立即切换。切换前可安排更低 TTL,但应考虑旧记录仍按原 TTL 缓存,以及平台对记录的限制。

核查应区分权威记录、当前解析器看到的记录与浏览器最终使用的地址。只查看管理控制台的值,无法说明用户链路已经取到新值。若开放 IPv6,也要核对 AAAA 指向的服务,否则不同网络可能进入不同版本。

TLS 与代理需要明确可信入口

TLS 保护传输过程,并通过证书等机制验证连接端点。它不能证明应用返回的内容没有被污染,也不为业务请求授予权限。证书需要覆盖访问的主机名,续期与到期监控也属于持续交付责任。

若 TLS 在代理处终止,应决定代理到源站是否仍加密,以及源站如何限制可达入口。原始 HTTP 客户端可以提交转发头,应用不能无条件信任 X-Forwarded-For 或 X-Forwarded-Proto。只有经过受信任代理并按明确规则重写的值,才适合作为客户端信息的依据。

代理与后端超时需要一起设计。后端处理很久,代理先超时,客户端可能重试,引发重复任务。长预览任务可以返回任务 ID 再查询,避免靠任意增加超时解决。负载均衡健康检查还要考虑新实例预热、旧连接排空与所有后端失效时的行为。

缓存键必须覆盖响应差异

缓存把某次响应保存下来,让后续匹配的请求复用。判断能否共享前,应问内容是否随用户、语言、权限、查询参数或部署版本变化。把中文和英文请求放进同一个键,可能让先到的语言覆盖另一种;把个人页面放进去,则可能暴露数据。

本例只为公开课程路径与两种明确语言构造键。private 参数表示已经由可信应用逻辑判定为私有,真实系统不能靠用户自己传一个布尔值选择隐私等级。

def cache_key(path, language, private=False):
    if private:
        return None
    if language not in {"zh", "en"}:
        raise ValueError("unsupported language")
    return path + "|" + language
assert cache_key("/learn/", "zh") != cache_key("/learn/", "en")
assert cache_key("/account/", "zh", private=True) is None
print(cache_key("/learn/", "zh"))

实际输出是 /learn/|zh。断言确认中英文键不同,个人页返回 None 表示不进入这份共享缓存。未知语言会抛出 ValueError。函数没有解析 HTTP 请求,也没有设置真实 CDN 规则,返回 None 本身不会禁止外部缓存。

实施时还要配置响应缓存策略,并核对代理、浏览器与 CDN 各自的行为。静态文件可用带内容指纹的 URL 区分版本;经常更新的课程索引需要刷新或较短有效期。缓存键太粗会返回错误内容,太细则降低复用并增加存储,两者都应通过请求样本检查。

选择托管方式时核对产品边界

VPS 给开发者较多运行控制,同时带来补丁、进程管理、备份与入口维护。静态托管适合预构建页面,但动态认证与后台任务需要其他运行能力。托管平台减少一部分运维步骤,仍要检查构建条件、运行时、权限、地域、配额和恢复方法。

Cloudflare Workers 的静态资源功能可以结合 Worker 路由与资源绑定提供内容;R2 是对象存储,不能把存储对象等同于应用运行环境。公开桶、私有读取与域名接入也应分别配置。选择时先列需要执行的逻辑,再判断产品是否覆盖它。

现有 ModelScope Studio 仓库说明的是基于 Gradio 的组件库,不能据此推断任意静态网站都有生产托管能力或 SLA。若考虑相关托管服务,应继续核对当前平台文档与账号可用条件。缺少证据时,把结论限制为组件能力,而不编造上线流程。

切换计划还应保留上一版产物与路由配置,说明恢复动作怎样作用于每层。源站回到旧版本之后,CDN 可能继续返回新版本缓存;反过来,清空缓存也不能修复错误源站。恢复验收应重新请求关键页面,核对内容和状态,再宣布切换结束。

练习:按链路验证一次版本切换

准备一张验证表,覆盖 HTTP 到 HTTPS、主域名与备用域名、中文与英文、缺失路由、个人页面和静态资源。重定向需核对状态码与 Location。对于非 GET 方法,应根据契约选择适当的重定向语义,不能假定所有客户端对 301 都保持方法。

验收时,同一公开路径的两种语言不能串缓存;个人内容不被共享复用;缺失路径有正确状态;证书主机名匹配;静态文件和页面能核对部署版本。模拟旧 DNS、过期缓存和后端不可用,记录在哪一层观察到问题、怎样恢复。真实发布时再用外部请求复核这张表,本文离线函数只证明键的局部规则。

资料来源与延伸阅读

资料核验:2026-11-10。本文为原创工程讲解,教学示例不代表线上实测。