背压只有改变生产者行为,才真正控制了有界接入
背压把消费者的有限容量与生产者行为联系起来。通道已满时,生产者可能等待、拒绝新任务,或执行经过明确选择的丢弃策略。关键是容量限制真正改变上游怎么做。队列不断增长,或仪表盘显示积压,只能说明供需失衡,本身不自动形成控制接入的机制。需要从观察到过载,继续追到生产者是否确实减速或停止提交。 坏掉的样子 无界队列可以吸收短暂突发,却也会隐藏持续过载。如果到达速度长期超过完成速度,未完成任务和等待时间就持续增长。把积压挪到待处理线程、重试任务或另一个无界缓冲,并没有消除压力。容量边界必须覆盖任务真正被保存的位置,不能只限制一个容易观察的队列对象,却允许周围继续无限保留工作。 有界队列本身也没有定义完整过载策略。无限阻塞可能耗尽生产者线程或让关闭流程卡住;立即拒绝时,调用方若误认为成功,就可能丢失工作;每次拒绝后立即重试,又可能加重负载。应依据应用契约明确选择:在截止时间内等待、拒绝并稍后重试,还是有意丢弃。不同选择改变交付语义,不能在实现中含糊处理。 队列容量与处理完成是两件事。消费者取出项目可以释放槽位,但此时可能仍在处理这个项目。等待容量为二,也可能同时还有额外执行中的工作。生产者成功入队,仅说明队列接受了项目,不证明已经执行、持久保存或完成下游副作用。若把所有这些状态都叫作“成功”,过载和失败时就无法判断责任归属。 快速验证 将脚本保存为 check-backpressure.py,运行 python3 check-backpressure.py。它只使用 Python 标准库,在 Python 3.14.4 上执行。线程通过明确事件和有界等待协调,不依赖远端消息代理、数据库或刻意拖慢的外部服务。所有队列和任务都只存在当前进程中,结果用于观察接入与完成的语义,不代表分布式队列的持久化保证: from queue import Queue,Full from threading import Event,Thread import json unbounded=Queue() for item in range(1000):unbounded.put_nowait(item) assert unbounded.qsize()==1000 channel=Queue(maxsize=2) channel.put('a');channel.put('b') try: channel.put('deadline-rejected',timeout=.02) raise AssertionError('a full bounded queue accepted another item') except Full:rejected=True assert channel.qsize()==2 entered,delivered=Event(),Event() def produce(): entered.set();channel.put('c',timeout=2);delivered.set() producer=Thread(target=produce,daemon=True);producer.start();assert entered.wait(1) assert not delivered.wait(.05) and channel.qsize()==2 first=channel.get(timeout=1);assert first=='a' assert delivered.wait(1);producer.join(timeout=1);assert not producer.is_alive() assert channel.qsize()==2 rest=[channel.get(timeout=1),channel.get(timeout=1)];assert rest==['b','c'] assert channel.empty() completed=Event() def join():channel.join();completed.set() waiter=Thread(target=join,daemon=True);waiter.start();assert not completed.wait(.05) for item in [first,*rest]:channel.task_done() assert completed.wait(1);waiter.join(timeout=1);assert not waiter.is_alive() print(json.dumps({'unbounded_backlog':unbounded.qsize(),'bounded_capacity':2,'deadline_rejected':rejected, 'blocked_producer_resumed_after_get':delivered.is_set(),'delivered_items':[first,*rest], 'empty_queue_did_not_mean_processing_complete':True,'join_finished_after_task_done':completed.is_set()})) 第一个队列使用默认无界容量。没有消费者运行时,一千次 put_nowait 全部成功,留下同样数量的积压。这是小规模行为演示,不是耗尽内存测试。它说明接入成功本身无法证明下游已有进展,也不能证明到达速度可持续。监控若只统计提交成功率,就可能在处理能力不足时仍显示表面正常。 ...