← 中文资源

简体中文版本

背压

背压是下游消费者无法跟上时通知快速生产者减速的流控信号,用于避免无界队列和内存持续增长。

阅读英文原文

为什么无界缓冲会失败

当生产者生成工作的速度快于消费者处理速度时,差额必须有个去处。无界队列把它放进内存:延迟增长,内存增长,最终进程崩溃。背压让这种不匹配在生产者处可见,而不是藏在队列里。

流中的背压

Node.js 流通过 write() 返回 false 和 drain 事件表示背压。正确的生产者会等待 drain 后再继续写入:

import { createReadStream, createWriteStream } from 'node:fs';
import { pipeline } from 'node:stream/promises';

await pipeline(
  createReadStream('large-input.log'),
  createWriteStream('copy.log'),
);

pipeline() 会在各阶段之间传播错误并遵守背压。手动循环调用 write() 而不检查返回值,可能把整个文件缓冲进内存。

队列与服务中的背压

有界队列把策略显式化:等待、拒绝或丢弃。下面的例子在接收新元素前等待容量:

class BoundedQueue {
  constructor(capacity) {
    this.capacity = capacity;
    this.items = [];
    this.waiters = [];
  }
  async push(item) {
    while (this.items.length >= this.capacity) {
      await new Promise((resolve) => this.waiters.push(resolve));
    }
    this.items.push(item);
  }
}

何时使用

在生产者与消费者速率可能分叉的每个边界使用背压:HTTP 上传、数据库批处理、日志传输、UI 事件队列。与限流相比,背压是每个消费者的容量信号,而限流是共享的准入策略。二者可以组合:限流保护服务,背压保护服务内部的内存。

陷阱

没有超时的背压可能在消费者永不推进时死锁。应为有界等待配合截止时间与指标。同时测量队列深度和消费者延迟;背压是安全机制,不能替代足够的容量。

相关资源