质量属性必须描述场景:从一句愿望建立可反驳的要求
"秒级生效、高可用"这类愿望无法实现也无法验收:它没有说明在什么环境下、对哪些请求、以什么代价满足。本页把一句愿望转成可反驳的质量场景,并给出"发布控制面受理≠节点加载≠业务生效"的三个裁决点。可用性、耐久性与正确性为何是三个目标,见可用性、耐久性和正确性是不同质量目标;愿望如何变成持续检查,见从愿景转成架构适应度检查。
本页使用合成的营销规则发布系统:运营发布折扣,Java 服务计算订单优惠。错误折扣直接影响金额,拒绝一次优惠则损害可用性。生效速度、正确性、故障隔离和成本在这里互相冲突,不是都能同时最大化。
六要素场景与三种约束
质量属性场景至少包含刺激源、刺激、环境、受影响对象、响应、响应度量。比如"运营在单区失联、入口每秒 3,000 次报价的环境下发布规则 v42,发布控制面给出确定结果;服务在提交后 5 秒内使用 v42,无法确认版本的节点停止执行优惠,任何成功订单都记录使用的规则版本"。这比"缓存最终一致"多出了失败时的业务义务。六要素来自 SEI 质量属性需求讲义,具体数字和停止策略是本案例假设,不是 SEI 给出的通用门槛。
先区分三种约束。功能要求是能创建、撤销和查询规则;质量要求是传播延迟、正确率与故障恢复;设计约束是两区部署、必须沿用现有数据库、不能把完整规则放入客户端。不能把"必须上 Redis"当作尚未论证的质量目标,也不能把"所有节点始终在线"作为网络失败时可满足的要求。
完整案例:规则传播的状态、观察点与预算
规则记录包含 rule_id、business_version、effective_at、payload_hash、status。发布状态为草稿、已提交、待生效、已生效、已撤回。配置持久化提交是"发布受理"的裁决点,各服务加载并校验版本是"节点准备好"的观察点,用户报价记录的版本才是"业务生效"的证据。三者不能共用一个布尔成功——这与会话过期后"谁有资格执行"的分层裁决同类,见ZooKeeper协调与会话恢复。
设提交需 80ms,推送排队上界 600ms,节点拉取和编译 120ms,定时生效检查间隔 200ms,观测汇总延迟 1s。正常路径的工程预算为 80+600+120+200=1,000ms,剩余 4 秒留给一次失败拉取、连接恢复和调度抖动。这个加法是在分配阶段时限,不能解释为相加各阶段 P99 后得到端到端 P99(分位不可加的推导见延迟预算与尾部放大)。监控在 2 秒之后才看到传播完成,不代表实际生效一定发生在第 2 秒;要保留节点实际应用时间与采集时间。
故障路径:t0 数据库提交 v42;t1 区域 A 加载完成;t2 区域 B 链路中断仍持有 v41;t3 订单来到 B。若系统允许 v41 继续算价,响应率可能很好,但"提交后 5 秒统一生效"已失败。若拒绝 B 的优惠请求,规则正确性保住,可用性预算被消耗。必须让业务负责人选择:是否允许过渡期旧价、是否按订单创建时间固定版本、是否仅停止优惠而保留原价购买。组件只能执行这个选择,不能替业务定义损失。
两种候选方案以及何时改变结论
方案 A 是带版本的本地快照与后台推送。报价无需远程依赖,单次多耗约 0.2ms 的本地匹配;失联仍可读,但要定义最大陈旧时间,过期后进入明确的降级状态。控制面应监控加载版本、最老版本年龄、失败原因以及按流量加权的旧版本请求。节点数量达标不代表用户覆盖率达标,一个大流量节点未升级可能影响大部分报价。快照的容灾价值与授权隐患两面,见Nacos注册发现与配置一致性。
方案 B 是每次报价同步向权威服务查询版本。单节点立即获得权威版本的机会更高,代价是每秒新增 3,000 次依赖请求、跨区延迟和依赖故障传播;请求可能在查版本之后、提交订单之前遇到规则撤回,因此仍需订单侧验证使用的版本是否被允许结算。同步读取没有消除状态机。
假设快照模式每月固定运行成本 4,000 元,同步模式为 6,000 元,均为教学输入。若旧规则误用的单次损失上限 50 元,不能简单用平均故障概率乘损失就宣布 A 更便宜:法规禁止旧规则、撤回涉及安全时,正确性是硬约束,平均成本无法把不可行方案变为可行。硬约束与偏好的区分及敏感性分析见从愿景转成架构适应度检查。若规则仅影响推荐排序,允许 60 秒陈旧,A 的自治收益明显增大。权重变化要与硬约束变化分开记录。
可执行推演:制作一份可以验收的场景记录
运行 python3 labs/quality_models.py all 核对本主题各算例(脚本与说明见labs 目录);本页的传播推演按以下步骤在纸面或电子表格完成,结果标为"模型推演"。输入固定为 100 个节点,A/B 各 50 个;B 承载 40% 请求;正常传播 1 秒,B 失联 8 秒,硬性生效期限 5 秒。第一步画时间轴,逐区填写 v41/v42 的可用时间。第二步计算 t5 到 t8 的旧版本暴露窗口:若流量恒定,受影响报价为 3,000×40%×3=3,600 次。第三步分别应用"允许旧价"与"停止优惠"两种策略,记录正确性违约次数或降级次数,而不是同时将两者写零。
第四步把 B 的流量比例改为 70%,预期暴露上升到 6,300 次,说明按节点数统计传播率会误导。第五步增加"撤回请求也被分区阻断"的故障,说明只给新版本加 TTL 不足以保证紧急撤回即时生效,必须选择离线授权的有效期和最长风险窗口。此处没有生产测量;传播时延和流量分布需要运行时补证。
验收产物是场景表、状态转换表、时间轴、两方案对比和一份需求到指标的映射。每个指标注明分母、窗口、数据缺失策略、所有者及失败动作。检查者应能从一条具体规则和订单追到采用的版本;仅提交架构图不合格。
决策输入清单:缺哪一项就输出证据不足
上面把愿望写成了可检查场景,但评审里更常见的问题是"输入不齐却先给结论"。本页把场景压缩成一张可交付的决策输入清单,任何缺项都必须降级为"证据不足",而不是用默认值补齐。
| 字段 | 本例取值 | 类型 | 缺失后果 |
|---|---|---|---|
| 刺激源与负载 | 运营发布规则 v42;3,000 次/秒;区域 B 占总流量 40% | 假设(来自容量与流量分解) | 无法计算暴露量,只能给区间 |
| 故障环境 | B 区链路失联 8 秒;硬性生效期限 5 秒 | 业务决策(硬约束) | 允许陈旧的上限变成工程自选,越权 |
| 受影响对象 | 使用优惠算价的报价与订单 | 事实(业务边界) | 可能把展示排序与金额裁决当成同一目标 |
| 响应与度量 | 订单记录使用的规则版本;违约次数与降级次数 | 定义 | 无法唯一判定"生效" |
| 业务裁决 | 过渡期是否允许旧价、是否只停优惠保留原价 | 业务决策 | 工程替业务定义损失,评审无法通过 |
| 证据等级 | 传播时延为模型推演;分区时长为注入假设 | 事实/假设分列 | 把推演写成实测,结论越界 |
流量份额不齐时的算法要显式给出区间,而不是拿节点数凑。若只知道 B 区有 50 个节点、不知道流量份额,按 3,000 次/秒、3 秒窗口,暴露区间是 [0, 3,000×3],即 [0, 9,000]。若业务上限是 5,000 次,区间跨越上限,正确判定是"证据不足",必须先补按流量加权的旧版本请求计数。只有当测得 B 占 40% 时暴露为 3,000×40%×3=3,600 次、判定通过;测得 70% 时为 3,000×70%×3=6,300 次、判定失败。
执行 python3 labs/quality_models.py decision 复算:脚本输入速率 3,000、窗口 3 秒、份额 40%/70%、上限 5,000,预期输出 {0.4: 3600, 0.7: 6300}、未知份额区间 (0, 9000) 与判定 INSUFFICIENT_EVIDENCE。这是确定性算术,不证明线上传播时延;分区行为仍需运行时补证。
两种处理缺项的方案:A 为缺项填组织默认值(如"份额按节点数均分")继续评审,快但把假设伪装成事实;B 冻结结论、指定补测责任人与期限,慢但可追溯。本例选 B,并只允许对展示型规则使用 A,因为展示错误的损失可被业务接受——裁决型规则不能。谁有权把缺项降级为可接受风险,必须在清单里写名字,不能留给"评审共识"。
面试答案与架构师条件变化
可口述答案:我先明确用户动作、正确性不变量和故障环境,再把愿望转成响应和量化边界。规则提交、节点加载、用户实际使用是三个观察点。选择本地快照就接受有界陈旧并设计过期降级;选择同步权威读取就支付依赖和延迟成本,同时仍需在订单裁决处约束版本。通过按流量加权的版本证据和分区推演验证,不用"用了多副本"替代论证。
架构师变式一:业务允许旧折扣继续结算,但禁止新规则跨地域传播。此时要重画规则所有权和地域路由,重新定义"全局 5 秒"是否仍是合理目标。变式二:生效期限从 5 秒收紧到 1 秒——1,000ms 的正常预算已无余量,必须压缩推送排队或改推送为同步失效通知,并用分层 SLO 区分单区与全局目标。变式三(选学):分析发布撤回的并发历史,证明每笔订单对应一个合法规则快照;不要求为纯需求分析强贴某个组件源码。
闭卷验收
本页 8 道闭卷题收进了下方自测面板:先回忆,再展开参考答案与评分点,最后给三档自评。