SelectConnection 基于 select 事件驱动,不兼容 asyncio;需手动创建 channel 并严格按 AMQP 协议顺序调用;回调参数必须完整,阻塞操作会卡死循环;关闭连接需显式 close() 并再次 start() 等待异步清理;未捕获异常将导致循环崩溃且无自动重连。

SelectConnection 不是 asyncio,别把它当 async/await 用
它本质是基于 select 的事件驱动模型,靠回调链推进流程,和 asyncio 的协程调度完全不兼容。你写一个 async def 函数,直接塞进 SelectConnection 的回调里会报错或静默失败——因为它的 ioloop 不认识 await,也不托管协程生命周期。
为什么 on_open_callback 之后必须手动调 channel()?
SelectConnection 不自动创建通道,连接建立只是“握手完成”,AMQP 协议要求所有操作(声明队列、消费消息)都必须在 Channel 上进行。所以你在 on_open_callback 里必须显式调用 connection.channel(on_channel_open),否则后续一切都会卡住,且无任何错误提示。
-
on_channel_open回调接收的是新创建的channel对象,不是连接对象 - 这个
channel是单次有效的,不能缓存后跨回调复用(比如在on_queue_declared里再用一次旧引用) - 如果漏掉
channel.queue_declare(..., callback=...)的callback参数,队列声明就“没下文”——既不会报错,也不会触发后续basic_consume
basic_consume 的 callback 必须能处理完整 AMQP 三元组
basic_consume 注册的回调函数签名固定为:def handle_delivery(channel, method, properties, body)。少一个参数(比如只写 (channel, method, body)),Python 会抛 TypeError;多一个(比如加了 **kwargs)则可能掩盖 properties 中的 headers 或 delivery_mode 等关键字段。
立即学习“Python免费学习笔记(深入)”;
-
method.delivery_tag是手动 ack/nack 的唯一依据,丢了就无法确认消息 -
properties.content_type和properties.headers需要主动解包,SelectConnection不做自动序列化/反序列化 - 若处理逻辑里有阻塞操作(如同步 HTTP 请求、文件读写),整个事件循环会被拖慢——它没有协程让出控制权的机制
ioloop.start() 后的 KeyboardInterrupt 处理容易漏掉 close()
直接 Ctrl+C 会中断 ioloop.start(),但连接对象仍处于打开状态。如果不显式调用 connection.close(),RabbitMQ 侧会维持 TCP 连接数,直到超时断开,可能触发连接数限制告警。
- 必须在
except KeyboardInterrupt:块中调用connection.close() - 调用
close()后,还需再调一次ioloop.start()等待关闭完成——这是 Pika 的设计:关闭是异步过程,需要事件循环继续跑完清理逻辑 - 若使用自定义
custom_ioloop(如集成到 Tornado),需确保该循环未被其他组件提前 stop()
最常被忽略的一点:所有回调函数内部都不能抛未捕获异常。一旦 handle_delivery 抛出异常,整个事件循环会崩溃退出,且不会自动重连——Pika 不提供内置的错误恢复策略,重连逻辑得自己用 on_close_callback + 定时器手写。

















