从愿景转成架构适应度检查:把约束写成可执行的判定
"高可用、低延迟"写进文档不会自动成立,发布流水线一放行就可能被新代码破坏。适应度检查把这些目标变成持续运行的判定:构建期查约束,发布期查灰度坏事件率,运行期查用户结果。本页用一次灰度发布完成从输入到决策的闭环,并说明检查自身的失败路径。
适应度函数可以是自动化约束、架构测试、运行门槛或人工评审。它检查重要质量目标是否持续成立,例如 API 错误预算、schema 兼容、最大队列、跨租户访问隔离或服务依赖上限。自动化约束必须有明确所有者、适用边界和例外流程:错误的硬门槛会驱动团队绕开系统;没有维护人的检查会随业务变化过时。检查所依据的目标怎么定义,见SLI、SLO 与预算与质量属性必须描述场景;候选比较与决策证据的一般方法由ADR、RFC 与运行证据专题负责(已由 #29 整合为唯一权威)。
从单组件目标推导端到端目标
端到端质量由用户可见的完整路径定义。若一次请求必须依次经过网关、应用、数据库和授权服务,且各阶段必须全部成功,则在故障独立这一强假设下,路径可用性近似为各阶段可用性的乘积。共享区域、控制面、身份系统或网络会形成相关故障,乘积模型会高估实际结果;应把公共依赖单独作为故障域建模,方法见故障域、RTORPO 和切换。
延迟预算也要从调用图推导。串行阶段占用同一 deadline,任一阶段超时都消耗后续预算;并行扇出则由业务完成条件决定是等待全部、首个成功,还是达到法定数量。把每个依赖各自的 P99 相加不能得到整体 P99,推导见延迟预算与尾部放大。预算用于限制每阶段可用时间,验收仍应观察端到端分布和用户完成率。
SLO 统计对象必须与业务成功条件一致。若只把 HTTP 2xx 当成功,可能把"任务已接收但永久未处理"计为健康;若只统计已完成任务,又可能遗漏排队过久。指标应同时说明事件定义、排除规则、时间窗口、流量权重和数据缺失处理。错误预算是目标容许的失败事件额度,不是某次发布导致事故的概率。
完整算例:发布检查需要什么输入
订单创建依赖鉴权、库存与数据库,用户旅程目标为 99.9% 的有效创建请求在 400ms 内得到正确结果。发布前检查 API 兼容性、数据库约束和依赖方向,发布中检查业务完成率、超时率、最老未决订单年龄,发布后核对订单、库存预占与 Outbox 的差异。三类检查的时间尺度不同,不能用构建通过证明生产容量,也不能用某次生产成功证明所有兼容输入都正确。
固定输入:对照组 100,000 次请求,坏事件 80;灰度组 10,000 次请求,坏事件 50。坏事件均定义为截止 400ms 后尚未正确完成的请求,未知结果也计入,合法用户取消按预先契约排除。对照错误率 0.08%,灰度 0.5%,绝对差 0.42 个百分点,灰度燃烧率是 99.9% 目标下的 5 倍。此时"50 次很少""CPU 都正常"都不足以继续放量。
但是直接判断新代码一定导致失败也过强:灰度组可能集中某地域、某设备或热租户。需要比较同类请求,记录分组规则与样本时段;如有明确业务风险可先停止扩大暴露,再验证因果。低流量时 1/100 与 0/100 差异也不能机械套同一个阈值。检查必须声明最小样本、数据缺失处理、观察窗口和可接受的不确定性,不在数据不足时默认通过。
把约束写成可执行的判定
执行 python3 labs/quality_models.py gate,输入上述两组计数。教学检查包含两个独立判断:灰度坏事件率不高于 0.1%,以及相对对照组没有超过 0.2 个百分点的绝对增加。本例两个条件都失败,脚本输出两个字段的 passed=false。它们是教学门槛,不是统计显著性检验;若业务要做严格归因,还需实验设计、置信区间和混杂控制。完整脚本与主题其他算例共用。
静态检查可以禁止领域模块直接依赖 Web 控制器,可以检查事件字段是否兼容;运行检查必须追到真实用户动作。比如任务接受接口永远返回 202,待处理任务持续累积,HTTP 错误率依然零。适应度检查应同时查看按时完成率和最老年龄,而不是再加一个无业务含义的实例存活探针。SRE 的 SLO 实施提供用户结果视角,多窗口告警讨论持续风险和当前风险的结合;本页的灰度门槛由案例自行定义。
检查本身也需要失败路径。监控导出器停了,查询可能返回空结果;若脚本把空值转成零错误,发布会失去保护。规则应区分"通过、失败、证据不足",证据不足暂停扩大灰度,允许负责人使用账本抽样等替代证据做有记录的例外。若所有检查都依赖同一个监控控制面,该控制面就是共同故障域,不能当作独立保护层。
两方案比较与阈值敏感性
方案 A 是所有变更通过统一硬门槛:实现简单、行为可预测,适合不变量、权限越界和明显兼容破坏;对有不同延迟需求的业务可能误杀,而且团队可能通过改指标绕过规则。方案 B 是按用户旅程配置检查,明确负责人、适用范围和到期例外:更贴合业务,但维护成本更高,失去所有权后容易退化为无人处理的告警。
本例选择不变量硬阻断与性能门槛分层。库存不能为负、同键不能重复提交,是不能被高吞吐抵消的条件;延迟目标允许有审计的风险接受,但不能把错误事件移出分母。例外记录必须包含为何接受、影响用户、最长窗口、替代信号和恢复负责人,结束时重新验证而非永久豁免。
敏感性分析输入可用性和成本。候选 A 同区服务每月 8,000 元,候选 B 双区 14,000 元;若共享数据库仍单区,B 不会消除数据库区域故障。只比较应用实例数会把冗余收益高估。若将数据库也分区部署,跨区 RTT 增加 50ms,原端到端预算中只剩 30ms 余量,需调整调用关系、减少串行依赖或重订目标,不能只在评审表给 B 可用性加分。
事故时序与恢复验证
t0 进入 5% 灰度;t5 新代码把事务锁持有时间加长;t8 请求超时,客户端自动重试;t10 旧版流量也因共享数据库排队受损;t12 关闭新代码流量。此时灰度停止并不等于恢复,队列、在途事务和重试仍可能保持高压。恢复检查需验证总到达率下降、连接等待回落、未知业务单完成核对以及陈旧 worker 不能再提交。
把风险只限制在"5% 流量"是错误答案,因为共享资源把影响传给剩余 95%。可用隔离连接池、按租户限流、限制并发和预设恢复速率减小爆炸半径,但这些控制不能自动隔离同一数据库磁盘或全局锁。需要明确隔离的是请求、资源还是数据故障。
交付产物为需求到检查的映射、脚本输入输出、灰度停止决策、条件变化记录和恢复核验清单。首次演练把监控数据设为空,再让系统出现队列积压,检查应输出证据不足或失败;若仍为通过,说明检测机制尚不合格。脚本仅验证演算和边界处理,不宣称执行了真实灰度发布。
检查输入契约与三态判定
适应度检查的结论质量由输入契约决定。每次运行必须记录:计划样本数(发生器意图)、实际发出数、有效样本数(按契约排除合法取消等)、窗口起止与对齐方式、数据缺失处理。分母不完整时,检查只能输出三态之一:通过、失败、证据不足;把缺失样本当通过,或把空结果当零错误,都会在故障最严重时给出绿色。
最小样本要围绕风险声明,而不是一个通用数字。灰度 100 个请求、1 个坏事件,绝对差看着大,却不足以区分新代码与噪声;正确输出是证据不足,先扩大窗口或补充账本抽样,而不是直接判定新版本更差。对照 100,000 次坏 80、灰度 10,000 次坏 50 的算例已在正文给出:灰度 0.5%、对照 0.08%,两个门槛同时失败,判定失败——样本充足时三态退化为二态,结论才可执行。
执行 python3 labs/quality_models.py fitness:输入三组计数——(1) 灰度 1/100 对照 0/100(最小样本 1,000);(2) 50/10,000 对照 80/100,000;(3) 0/20,000 对照 0/20,000。预期输出 INSUFFICIENT_EVIDENCE、STOP、PASS。脚本只验证判定逻辑与边界,不执行真实灰度;1/100 的置信区间不在这里计算,因为教学门槛不是统计显著性检验。
两个选择:A 静态约束(schema 兼容、依赖方向、权限默认值)在构建期阻断,反馈快、成本低;B 运行门槛(坏事件率、最老任务年龄)在发布期与运行期判定,贴近用户但依赖遥测完整性。本例分层使用:静态约束硬阻断,运行门槛三态放行,证据不足时暂停扩大灰度,允许负责人用替代证据做有记录的例外。任何例外都要写影响、最长窗口、替代信号与恢复责任人,到期重新验证,而不是永久豁免。
架构师变化条件
把灰度门槛从固定阈值改为按燃烧率动态放行:低流量期最小样本不足时输出"证据不足"而不是"通过",并给出租户分层比较替代全局均值。把共享数据库改为按租户分片,重算 5% 灰度的爆炸半径与传播链。若值班能力减半,例外审批的瓶颈从技术判断转移到人力,需要预先分级哪些例外可自动放行、哪些必须人工——每次变化都重算排序是否反转。
闭卷验收
本页 8 道闭卷题收进了下方自测面板:先回忆,再展开参考答案与评分点,最后给三档自评。