系统被压垮之前,先把压力变成信号

反应式系统的核心不是'更快',是'在压力下不崩'——通过消息驱动、弹性和回压机制让系统保持响应

本页目录

流量翻了三倍,服务开始超时。超时请求被重试,重试带来更多请求,更多请求导致更多超时。十分钟后全站不可用。

运维把流量切走,服务恢复了。但谁也说不清楚:系统的承载能力到底在哪条线上?超过这条线之后,降级策略是什么?

这不是一个性能问题。CPU 没到 100%,内存没爆,磁盘没满。这是一个架构问题——系统在设计时没有回答"压力超过承受能力时怎么办"。

响应性是第一优先级

Reactive Manifesto 把"responsive"放在最上面不是偶然的。系统存在的意义是提供服务,不能响应就等于不存在。

传统架构先追求"正确",再追求"快",最后考虑"在异常情况下还能不能用"。反应式的优先级不同:先保证在任何情况下都能响应,哪怕响应的内容是"我现在处理不了,请稍后再试"。

返回一个明确的"我忙不过来"比静默超时好得多。调用方拿到拒绝信号可以做降级处理;拿到超时只能猜——是慢还是死了?

消息驱动替代同步调用

同步调用的问题不在于"慢",在于"耦合"。A 同步调用 B,B 同步调用 C——A 的响应时间等于 A+B+C 的总和。C 挂了,B 也挂了,A 也挂了。故障沿着调用链传播,速度比人的反应快。

消息驱动把同步调用替换成异步消息传递。A 把请求放进消息队列,B 从队列里取。A 不等 B 的响应,可以立刻服务下一个请求。B 挂了,消息在队列里等着,B 恢复后继续处理。故障被队列隔断了。

代价是编程模型变复杂了。回调、Future、流处理——异步代码比同步代码更难写、更难调试。但在需要弹性的场景里,这个代价是值得付的。

回压不是限流,是对话

限流是单方面拒绝。回压是告诉上游"我处理不过来了,你慢点"。

Reactive Streams 规范里的 request(n) 就是回压的最小实现:下游告诉上游"我还能处理 n 个",上游据此控制发送速度。不是上游猜下游能处理多快,而是下游显式告知自己的容量。

没有回压的异步系统会在高负载下把消息堆到内存里,然后 OOM。有回压的系统会在高负载下自动放慢速度,用延迟换稳定。

故障不是异常,是常态

反应式设计的底层假设和传统架构不同:故障不是"可能发生的意外",是"一定会发生的日常"。网络会断、服务会挂、磁盘会满、时钟会漂移。

基于这个假设,故障隔离必须内建到架构里。每个组件都应该有自己的失败边界——一个组件挂了不应该把其他组件拖下水。

Circuit Breaker 模式做的就是这件事:检测到下游故障后主动断开调用,让上游走降级路径而不是继续往一个已经挂了的服务发请求。等下游恢复后再逐渐恢复调用。

同分类继续看