知识卡片

防御性编程六法:从阻断、协议、仲裁三原型展开

结构图卡

内容

[[防御性的本质:应对不确定性,边界是冲突的高发地|现实世界应对边界冲突的三种方法]]直接映射到防御性编程的六种具体方法。对应”阻断”的有两种:身份认证——只在获得同意的情况下才允许访问,未经同意的一律拒绝(携带令牌、会话ID、签名等);最小知识法则——是阻断方法的延伸,允许调用方进入系统内部,但只给最小够用的授权(面向对象的封装原则、菜单权限、数据权限控制都是这一原则的应用)。对应”协议”的是契约式编程——调用方和被调用方通过约定尽量一次成功,即便不成功也及时止损并明确告知处理方式;一份良好的契约至少要包含前置条件(对输入参数、对调用方的要求,不满足时该如何处理)和后置条件(对输出结果、对被调用方的要求,包括返回结果的格式、字段取值范围,以及失败时如何告知失败原因)。对应”第三方仲裁”的是仲裁机制——当某个节点或服务无法自行察觉自身问题时,引入第三方监测其状态,发现问题后重启恢复或让其他节点接管(负载均衡本身就是一种仲裁机制;Redis、RabbitMQ集群模式下的仲裁程序了解所有节点状态,节点出问题时立即替换为其他可用节点)。此外还有两类应对”超出契约范围的未知错误”的方法:通信问题处理——重发(未收到结果或结果不符合契约时重新发送)、熔断/限流/降级(请求量超阈值时保护自身)、超时(响应过长时主动终止);资源处理问题——遵循”有始有终”原则,无论发生何种意外都要确保正确关闭已使用的资源,防止资源泄漏(Java里用try-catch-finally手动关闭资源,或更推荐的try-with-resources,后者在异常处理和资源释放上更简洁安全)。可迁移启发:设计一个新接口时,可以直接把这六种方法当作一份检查清单过一遍——有没有做身份认证、有没有把授权限制到最小够用、契约的前后置条件写清楚了没有、依赖的下游有没有仲裁机制兜底、通信失败时的重发/熔断/超时策略定义了没有、涉及的资源是不是保证了有始有终——六项里漏掉任何一项,这个接口在生产环境里都可能在某个边界条件下暴露出脆弱点。

结构图

flowchart TB
  A["阻断"] --> A1["身份认证(令牌/会话ID/签名)"]
  A --> A2["最小知识法则(最小够用授权,如封装/权限控制)"]
  P["协议"] --> P1["契约式编程(前置条件+后置条件)"]
  R["第三方仲裁"] --> R1["仲裁机制(负载均衡/集群仲裁程序,监测并接管故障节点)"]
  U["超出契约的未知错误"] --> U1["通信问题:重发/熔断限流降级/超时"]
  U --> U2["资源处理:有始有终(try-with-resources)"]

参考来源

- 位置:《架构师启示录:知识模型、落地方法与思维模式》第8章《系统实现》之"8.3.3 防御性编程的思路"(源文件:_epub-src/EPUB/xhtml/chapter12.xhtml) - 结论依据:原文说明"一份良好的契约至少应该包括前置条件和后置条件……负载均衡就是一个仲裁机制,当下游的某个节点出现问题时,不会再给该节点发送请求……使用try-with-resources块……在异常处理和资源释放方面具有更高的安全性,因此推荐使用该方式", 直接支撑本卡关于防御性编程六法及其与三种现实防范原型对应关系的结构图。 - 原始内容:有一个非常重要的原则是"有始有终",即一旦使用了某个资源,无论发生何种意外情况,都要确保能够正确关闭该资源,以防止发生资源泄漏和系统稳定性相关问题。