拒绝策略是背压机制的执行开关,需分层配置:api层用callerrunspolicy+限流熔断,内部调用用自定义策略仅记录日志、上报指标、告警,关键任务拒绝后落库重试;队列须有界且可观察,拒绝动作禁io,上游须感知压力并减速。

拒绝策略不是“兜底摆设”,而是背压机制的执行开关。真正起作用的,是让上游感知压力、主动减速,而不是等下游崩了再补救。
选对策略:按调用层级分层配置
对外 API 层和内部服务调用,必须用不同策略:
-
API 网关或 Controller 层:用
CallerRunsPolicy,但必须搭配限流(如 Sentinel QPS 控制)和熔断(失败率 >50% 自动降级)。它让 HTTP 线程自己执行任务,天然拖慢请求节奏,给系统“喘息窗口” -
内部 RPC 或消息消费端:禁用
AbortPolicy和DiscardPolicy;改用自定义策略,只做三件事:打 warn 日志(带采样)、上报 Prometheus 拒绝计数、触发告警(如队列满率 >90%) - 关键变量任务(如订单状态更新、支付回调):拒绝即落库(MySQL/Redis),字段含任务类型、业务 ID、拒绝时间、重试次数;后续由独立调度器捞取重试,且重试前校验幂等性
队列长度要“可观察”,不能拍脑袋
队列不是越大越安全。设成 10000 可能 2 秒就满,你连告警都没收到就 OOM 了。
- 计算公式:
queueSize ≈ (目标峰值 QPS × P95 任务耗时) × 1.5。例如支撑 300 QPS、P95 耗时 200ms → 建议队列长度为 90,取整到 128 - 必须用有界队列:
ArrayBlockingQueue或带容量限制的LinkedBlockingQueue;禁用默认的无界队列(Integer.MAX_VALUE) - 监控核心指标:队列使用率、每分钟拒绝数、平均排队时长;设置两级水位告警(>70% 预警,>95% 触发自动降级)
拒绝动作里禁止 IO,否则雪上加霜
很多团队在自定义拒绝策略里加了远程日志、HTTP 上报、甚至数据库写入——这等于把压力从线程池转移到网络或 DB,反而加剧阻塞。
- 拒绝策略内只允许内存操作:记录日志用
logger.warn(加采样,比如 100 次打 1 条)、更新本地原子计数器、发 Redis Pub/Sub 信号 - 需要持久化?交给异步补偿模块,拒绝策略只负责“序列化+入 Redis(TTL)”,不等写入完成
- PHP 场景下可用
publish('backpressure', ['action'=>'slow_down'])通知上游降速,生产者通过usleep()主动延迟,形成闭环
让上游真正“听懂”压力信号
光下游拒绝没用,上游得知道该怎么做。不能只返回 500 或静默丢包。
- HTTP 接口被拒时,返回
429 Too Many Requests+Retry-After: 1,驱动客户端指数退避 - 内部 gRPC 调用可在响应 header 中携带
x-backpressure: true,调用方 SDK 自动延缓下一批请求 - 消息队列消费者可通过
pause()/resume()(如 ReactPHP)或反向信用机制(如 Flink 的 Credit-Based Flow Control)实现毫秒级响应










