过载时为什么越忙越慢
系统超过饱和点后,吞吐会随并发上升而下降:请求排队、上下文切换、锁竞争与重试叠加,让响应时间急剧恶化。过载保护的核心不是提升上限,而是限制在途请求数量并让多余请求快速失败:入口限流、队列上限、优先级降级与熔断,都是围绕「不让无限请求堆进系统」设计的。
入口限流(nginx 示例)
入口限流(nginx 示例)
# 按来源地址限速,zone 名与共享内存区域大小要事先定义
limit_req_zone $binary_remote_addr zone=api:10m rate=20r/s;
server {
location /api/ {
limit_req zone=api burst=40 nodelay; # burst 允许的突发量,nodelay 立即拒绝超出部分
proxy_pass http://app_backend;
}
}
# 连接数上限同样是过载保护的一部分
# limit_conn_zone $binary_remote_addr zone=conn:10m;
# limit_conn conn 20;应用侧降载(伪代码)
应用侧降载(伪代码)
sem = BoundedSemaphore(200) # 在途请求上限
def handle(req):
if not sem.acquire(blocking=False):
return reject_503() # 快速失败,不排队堆积
try:
return process(req)
finally:
sem.release()
# 降级路径:重查询走缓存或直接返回简化结果
def search(req):
if degraded_mode and req.is_heavy:
return cached_or_plain_result(req)常用手段
- 按用户或接口限流:区分核心接口与次要接口的配额
- 队列设长度上限:排队长度可控,超出立即拒绝而不是无限等待
- 快速失败:宁可返回失败,也不让请求在系统里滞留数倍超时时间
- 优先级降级:压测、导出类重任务在过载时先被拒绝或延后
- 熔断与断路:下游连续失败时停止调用,给下游恢复留出空间
- 数据库侧保护:短超时、只读降级、重查询走缓存
层次与要点
| 层次 | 手段 | 配置要点 |
|---|---|---|
| 入口 | 限流、连接数上限 | burst 与 nodelay 决定突发流量的处理方式 |
| 应用 | 在途请求上限、队列长度 | 队列必须有限,排队时间要计入总超时 |
| 请求分级 | 核心与次要接口区分配额 | 导出、报表类重查询优先降级 |
| 数据库 | 短超时、连接池上限、熔断 | 连接池与超时链条要内外一致 |
| 恢复 | 半开探测与逐步放量 | 熔断恢复不宜一次放全量流量 |
重试要带退避与抖动并计入重试预算:故障期间无节制重试会把原始流量放大数倍,这是过载恶化的最常见路径。
过载时的返回值要选清楚:拒绝请求会立即产生可见错误,接受后超时则占用资源更久。通常优先选择快速拒绝并配合重试与告警。