同一 TCP 连接中包含三条 HTTP/2 流:A 被取消,B 完整读取正文,后续 C 复用原连接。共享 TCP 约束仍然限制进度。

HTTP/2 多路复用消除排队

HTTP/2 通过多路复用解除 HTTP 响应的顺序约束,但不同流仍然共享 TCP 连接及其故障边界。取消就是一个值得单独验证的场景:终止某个请求,不一定需要破坏相邻请求或可复用连接。这是一项有明确作用范围的能力,不能扩写成所有流在任何条件下都互不影响。 取消一条 HTTP/2 请求后,应分别核验相邻正文与原连接复用。共享 TCP 约束依然存在;取消也不能撤销已经发生的业务操作。此图不增加运行证据。 流不是底层套接字 HTTP/2 在连接内部标识单条流。RFC 9113 定义的 RST_STREAM 用于终止某条流,既可以表达取消,也可以报告流错误;它并不是重置整个套接字的 TCP reset。只要连接本身仍然可用,实现就可以停止一次交换,同时让其他流继续推进,而不必为每次取消重新建立连接。 ...

一月 16, 2027 · 3 分钟 · XB Field Notes
不透明会话模型中,浏览器保存含不透明标识和存储期限的 Cookie。标识查找服务端保存用户状态和服务端期限的会话记录。退出时既清除 Cookie,也作废会话。

Cookie 与会话守住边界

Cookie 是浏览器的存储与传输机制。服务端会话则是一种应用设计,用 Cookie 的值查找服务器保存的状态。分开这两种责任,登录、过期和退出才容易验证。本文沿着不透明会话标识的生命周期展开,不再重复主机、路径或 SameSite 匹配规则;这些发送条件不能回答旧会话是否已经真正失效。 Cookie 期限控制浏览器存储,会话有效性和操作授权由服务端负责。替换并作废旧标识后,还要测试重放。两种计时示例使用原文实验单位,并未证明浏览器或分布式系统行为。 持有标识仍可能获得权限 在这种设计中,服务器生成不可预测的标识,并把它关联到一条会话记录。客户端不需要理解该值,但持有它仍可能获得访问资格:窃取有效 bearer 标识的人,可以尝试直接重放。称它“只是指针”不能让读者以为它无害。不要把它放入 URL、日志、诊断截图或支持材料中,随机字符串也可能是实际凭据。 不能直接信任客户端控制的明文角色字段。不过,Cookie 中含有有意义的数据,也不等于必然可以伪造:经过正确认证和签名的会话格式属于另一种设计。签名保护完整性,不提供保密性;无状态方案仍须明确过期、密钥管理与撤销策略。应选择维护良好的会话框架,不要仅为了省一次服务端查询就自行发明格式。 不透明方案需要使用密码学安全随机生成器和足够熵值。未知标识应被拒绝,而不能用客户端任意指定的值建立特权状态。服务器解析会话后,还须根据当前权限授权每项操作。某个会话已经认证,不代表它可以读取每一个对象、操作每一个账号,或自动获得所有管理功能;会话存在与具体动作获准是两项检查。 两种寿命需要两处控制 Cookie 的 Max-Age 与 Expires 管理客户端存储,不会自动使服务端会话记录过期。非浏览器客户端可以保留这个值,并在浏览器本该删除后继续重放。浏览器会话 Cookie 也可能通过会话恢复机制保留。因此必须在服务端强制执行绝对期限与空闲期限,使用明确的边界比较和一致的时间来源,而不能靠标签页里的倒计时作为安全判断。 退出时既要作废服务端状态,也要按匹配的作用域清除浏览器 Cookie,否则复制出来的令牌仍可能有效。认证后与权限变化后轮换标识,有助于防止会话固定,但前提是旧标识确实失去权限。并发请求和分布式存储需要额外协调,避免一个较晚完成的旧请求悄悄恢复已经撤销的会话;只覆盖单进程正常路径远远不够。 ...

一月 12, 2027 · 2 分钟 · XB Field Notes
一次 HTTP 升级在客户端与服务端之间打开 WebSocket 会话,同一条连接上的消息可以双向流动。

WebSocket 是会话,不是页面

封面所示及本文讨论的是 HTTP/1.1 升级流程;其他 HTTP 版本的建立方式不同。HTTP 描述请求与响应,并不要求每个请求都新建一条 TCP 连接。WebSocket 提供面向消息的双向会话。选择它而不是轮询,是为了让任一端及时主动发送更新,同时接手会话身份、队列、存活判断与恢复的责任。连接建立只是起点,后续的资源与状态也必须有人管理。 图示 HTTP/1.1 场景。心跳期限与认证属于应用策略;重新连接不会自动补回遗漏的消息。 升级买到什么 本文的练习限定在 HTTP/1.1。服务端接受开启握手后返回 101,此后连接中传输的是 WebSocket 帧。检查 Upgrade 与 Connection 时,要匹配列表中完整的成员,并按协议规定忽略大小写;仅仅发现 notupgrade 中含有 upgrade 子串不能算通过。还要检查版本为 13,以及 Base64 密钥解码后恰好十六字节。接受值证明对方理解了握手,却不能证明用户身份。RFC 6455,第 4.2 与 10.5 节。 101 没有 HTTP 消息体,不允许携带 Content-Length,值为零也不例外;同样不能发送 Transfer-Encoding。头部结束后的字节属于 WebSocket 分帧,不能把它理解为普通空响应后随意追加的一段字符串。这种边界必须由实际收发与解析来验证。RFC 9110,第 8.6 节 与 RFC 9112,第 6.1 节。 ...

一月 8, 2027 · 9 分钟 · XB Field Notes
同一实例的必需依赖失败;/livez 处理器仍响应 200,而 /readyz 返回 503。大号不等号区分两个判断。

一个为用途而建的健康检查值得保留

健康端点是一份会驱动后续动作的契约。处理器能够返回 200,并不证明实例可以完成有价值的业务请求。写代码之前应先明确观察者要据此做什么:等待初始化、接入新流量,还是重启一个无法自行恢复的进程。问题不同,需要的证据就不同,不能把它们压成一个名字叫“健康”的布尔值。 区分进程恢复和流量准入 存活检查应该对应重启能够改善的本地故障,例如无法继续工作的死锁。共享数据库不可用,通常不能靠把所有调用方重启一遍来修复。就绪检查回答这个实例现在是否适合接收它负责的流量,只纳入这条业务路径必需的依赖;可选的推荐功能失败,不应自动把仍能结算的实例整个摘出流量池。 启动检查保护耗时初始化不被过早判定为故障。在 Kubernetes 中配置 startup probe 后,存活和就绪探针要等启动探针成功才运行。就绪失败影响流量准入;启动或存活探针连续失败达到配置容忍度后,才可能结合重启策略触发重启。单独收到一份 503,并不代表重启、摘流或恢复已经在控制面完成。 状态契约必须以真实消费者为准。Kubernetes 的 HTTP 探针把大于等于 200、小于 400 的响应视作成功,所以误接入登录跳转的探针可能显示绿色。下面的三个 GET 健康路由刻意只返回 200 或 503,不靠正文里的“正常”字样决定健康,也不允许通过自动跟随重定向掩盖原始结果。 给探针引入的工作设预算 选择便宜、只读而且确实代表关键依赖的操作。取得连接或者完成一次小请求,只能证明那条具体路径,不代表所有业务事务都成功。内部期限应短于外层调用者的超时,并把取消信号传到下游;响应体和计时器都要释放。外层提前返回错误、内部请求却继续排队和执行,并不算真正限制了探针成本。 总负载还取决于实例数量、独立观察者数量和轮询频率。不要每次探测都展开整张依赖图,也不要让健康检查抢光业务恢复所需的连接。如果项目已有后台检查器,可以复用其结果,但必须定义结果年龄和失败含义,不能把很久以前的绿色无限期当成当前事实。中间缓存同样不能把旧成功响应发给负载均衡,让已经失败的实例继续被接入。 坏掉的样子 永远返回成功的端点,会在数据库断连、工作池耗尽或初始化尚未完成时继续放行流量;相反,把共享依赖故障挂到存活检查上,会让整批实例进入无效重启循环。探针自己也可能消耗稀缺连接,或者因容忍阈值过低,在短暂暂停时同时摘掉过多容量。应当用实际失败场景证明状态含义,而不是仅仅证明路由可以访问。 测试至少覆盖未启动、依赖明确失败、依赖不响应、主动排空以及恢复。本设计中的存活路由不能被共享依赖拖着失败。排空首先禁止新流量准入,之后才能等已有请求结束;但一个健康端点本身不会等待负载均衡传播,也不会替应用完成连接排空。公开响应保持简短,不返回内部依赖地址、凭据或堆栈,详细原因放在受控的运维观测中。 Go 实验按启动、排空及有期限的必需依赖检查决定就绪;存活处理器仍可响应。探针结果本身不证明平台已执行动作。 快速验证 保存下面程序为 health_probe.go,运行 go run health_probe.go。两个回环服务分别模拟应用及其唯一必需依赖,依赖返回 204 才算通过。应用通过一百毫秒的子 context 限制下游请求,外层测试客户端允许两秒;启动与排空由内存标志表示,不会改动真实服务、负载均衡或集群配置。 ...

十二月 31, 2026 · 3 分钟 · XB Field Notes
curl 探测分成进程退出、HTTP 状态、响应头与正文;文章夹具中退出零可以同时对应 HTTP 404,正文文本为 missing。

用 curl 做第一轮 HTTP 探测

命令行 HTTP 请求提供一个范围较小、容易检查的服务视角:不用经过浏览器渲染,就能看到状态、响应头、正文和耗时。开始之前先明确问题,是检查原地址是否正确跳转、最终页面能否读取,还是某种请求方法是否被接受。不同问题需要不同探测方式。一条 curl 命令成功,只能说明这次请求满足了对应条件,不能证明网站所有功能都正常,也不能直接代替用户实际访问路径的验证。 先确定问题再选择选项。这些实际观察到的本地合成夹具结果,不验证 TLS、浏览器行为或生产服务。 核验记录,2026 年 10 月 17 日:这份修订后的本地示例在 macOS 26.5.1(arm64)、Python 3.14.4 与 curl 8.7.1 上运行,中英文两份代码都通过了六项基线探测。另行检查分别在捕获文件不存在和残留旧内容时测试连接拒绝及截断响应:四项结果都产生了预期的传输失败(退出码 7 或 18),没有混入旧捕获数据,并被传输退出状态断言拒绝。拒绝连接检查会在连接前释放预留端口,因此其他进程可能在这段间隔中重用该端口。这些已记录结果只覆盖本次合成的回环夹具,不验证 TLS、浏览器行为、生产服务、负载或下游依赖。 ...

十二月 11, 2026 · 2 分钟 · XB Field Notes
当前资源分别指向下一页与替代资源。箭头描述资源关系,不表示重定向,也不授予访问权限。

Link 头把相邻的资源关联起来

Link 响应头描述当前资源与其他资源之间的关系。一个返回 JSON 的接口可以用它给出下一页,让客户端不必解析 HTML,也不必猜测分页参数。这个字段提供目标地址和关系类型,是否访问仍由客户端决定。它是可供理解的元数据,不是强制浏览器下载资源、要求爬虫收录页面或授予访问权限的指令。 相对于有效请求地址解析返回的目标。保留关系语法,并分别处理目标策略与浏览器暴露规则。 链接包含上下文与目标 例如 </items?cursor=b>; rel="next",表示从当前上下文资源指向指定目标。没有显式的 anchor 参数时,上下文通常是被请求的资源。相对目标应根据重定向之后的有效请求地址解析,不能根据程序工作目录或想象中的接口根路径拼接。只包含查询串的目标会替换查询参数而保留路径,这与以斜杠开头、从站点根部解析的地址不同。 rel 指出关系类型,注册关系包括 next、prev、alternate 和 canonical,扩展关系还可以使用 URI 标识。type="application/json" 等参数描述的是目标资源,不会改写当前响应实际的内容类型,也不保证目标可信。如果一个链接通过 anchor 改变了上下文,无法处理这种上下文的客户端就不应擅自把它解释成当前页面的关系,否则链接含义已经改变。 ...

十二月 4, 2026 · 2 分钟 · XB Field Notes
状态、正文与应用三列表格区分完整 200 携带 ok:false、截断 200 和没有响应。

结合传输与业务结果读取状态码

调用方需要的不只是三位数字,还要知道是否收到 HTTP 响应、正文是否完整到达,以及应用结果是否满足预期操作。这是三个不同的问题。二百响应可以按既定契约携带应用层失败,也可能在响应头之后发生传输中断;没有收到响应之前就连接失败,则根本没有 HTTP 状态。诊断和指标应保留这些区别,不能把它们压成一个过于简单的成功标志。 按实际取得的证据分类 HTTP 错误响应仍然是一份响应,其中可能包含有用的表示、认证挑战、请求标识或 Retry-After 字段。有些客户端库会为四开头和五开头状态抛出异常,但仍允许读取这些信息。处理时应先捕获具体 HTTP 异常,再处理更宽泛的传输异常;否则,一个带明确限流提示的四二九,就可能被归成和连接失败完全相同的通用网络错误,丢失下一步判断依据。 HTTP 交换成功也可能只代表业务信封成功传递。例如批处理或 RPC 契约可以返回二百,同时在正文里说明某个子操作失败。应读取文档定义的结构,而不是默认这种响应里的业务全部成功,也不能断言所有此类接口都违反 HTTP。监控需要验证自己承诺监控的结果;如果业务契约要求检查正文,那么只读状态码就没有完成相应检查。 坏掉的样子 在收到响应头时记录状态,可能掩盖随后出现的正文截断。Content-Length、流结束、解压与解析,都影响最后是否得到可用表示。应把已经收到的状态与未完成的传输分别报告,不能因为状态指标已经计入二百,就把只有半份内容的响应视为应用成功。收到响应头,是比完整收到并理解业务结果更早的一个验证点。 是否重试也不能只看第一位数字。四二九表示请求受到限流,可以附带 Retry-After;五零三也可能建议等待。该字段可以使用延迟秒数或 HTTP 日期,但两种形式都没有保证服务器到时一定恢复。客户端应结合服务器提示、有界重试策略、退避和操作语义,避免一遇到错误就立即重放。等待提示不是无限重试的授权,也不是恢复成功的证据。 非幂等请求的响应丢失,可能让业务效果处于未知状态:客户端失去联系之前,服务器也许已经提交。没有合适的幂等契约就重复操作,可能产生重复工作。另一方面,有些四开头错误会随着身份、状态或限流条件改变而变得可恢复。按照整个类别制定“总是重试”或“绝不重试”,会抹掉真正需要用于决策的信息,而不是简化正确的处理逻辑。 状态码本身也不能说明究竟是哪一个组件生成了响应。源站、网关和中间层都可能返回错误,归因时应结合请求标识与相应日志。curl 的 000 一类诊断值,不是注册的 HTTP 响应状态,通常表示没有取得可用的 HTTP 状态。把它当成某种服务器错误,会把连接阶段的问题错误地归到已经收到响应之后的处理阶段。 HTTP:分别判断三类结果。保留的 Mac 试验范围见下文;绘图与本次采纳审阅均未重跑示例。 快速验证 将脚本保存为 status-client.py,使用保留断言的 Python 3.14。五种回环响应和第六个已绑定但未监听的端点,用于检查分类。私有 urllib opener 只为实验关闭代理发现,不更改系统代理。第六个键为 no-response,只检查没有返回 HTTP 状态,不要求特定的拒绝连接错误。保留的 Mac 试验中,该端点始终由实验持有、没有监听,实际观察到的是超时。具体执行设置和证据限制见下文。 ...

十二月 3, 2026 · 2 分钟 · XB Field Notes
旧红色字节保留旧摘要文件名,变化后的蓝色字节使用新摘要文件名。

不可变资源与内容指纹

资源指纹把文件字节的摘要放进地址。样式表发生变化,构建就为新内容生成另一个地址,页面因此能够选择新版本,而不用等待所有旧缓存同时过期。这项方法依赖发布流程遵守承诺:某个地址一旦对外提供,就不能随意替换成与名称摘要不符的内容。文件名看起来像摘要,不代表服务器已经落实这种约束。 字节变化应使用新的摘要地址;先让资源可用,再发布引用它们的 HTML,并保留仍被引用的旧文件。覆盖旧地址会让新旧客户端看到不同内容。文件名摘要不是浏览器完整性检查;本图未重新执行实验。 名称配合新鲜度契约 构建程序可以对生成文件计算完整的 SHA-256,再把页面中的逻辑名称替换成相应地址。同样的字节保留相同名称,变化的字节通常得到不同名称;摘要截断越短,碰撞风险越需要认真评估。浏览器不会因为名称含有一串十六进制字符,就自动计算文件摘要验证完整性。实际缓存键还受到请求上下文和 Vary 等因素影响,命名方法不能取代 HTTP 缓存规则。 公开指纹资源常用的响应头是 Cache-Control: public, max-age=31536000, immutable。前者给出一年新鲜期,后者承诺该表示在新鲜期内不会改变,因此 RFC 8246 建议客户端避免不必要的条件请求。这不是永久保存许可:缓存可能被淘汰,用户可以强制刷新,客户端也可能不支持扩展。规范还建议在 HTTPS 等经过认证的上下文之外忽略 immutable,不能把它当作内容可信性的替代品。 负责引用这些资源的 HTML 需要独立缓存策略,例如较短新鲜期或合适的重新验证,让用户能够发现新版本。发布顺序应先让新资源可用,再暴露引用它们的页面或清单。旧页面、旧标签页和回退版本仍可能使用旧地址,资源保留期限必须覆盖这些真实使用方式。仅有指纹,不能让整个多文件发布自动成为原子事务。 public 只适合允许共享的表示。某份个性化下载即使带有内容摘要,也不会因此变成可供共享缓存保存的公开资源。响应策略仍然必须服从身份鉴别与保密要求;摘要只是命名依据,不能充当授权或隔离用户数据的措施。 坏掉的样子 覆盖旧指纹路径会制造两套互相矛盾的事实:已有用户继续从缓存读取红色样式,新客户端却在同一个地址读到蓝色字节。清理一个 CDN 节点,也不能把所有浏览器里的私有副本同步删除。发布检查应核对真实文件字节和记录的摘要,在开放流量之前拒绝这种变更,而不是依赖上线后提醒用户刷新。 立即删除上一版资源会产生另一类故障。缓存中的页面还可能引用昨日脚本,回退后的 HTML 也可能依赖已经移除的文件。保留源码提交不等于保留可直接恢复的构建产物。即使所有摘要都正确,先发布页面、后上传文件仍会造成暂时的缺失资源;多节点传播期间也需要检查依赖是否已经就绪。 ...

十一月 25, 2026 · 2 分钟 · XB Field Notes