重新部署后 RabbitMQ 客户端报 “PRECONDITION_FAILED - inequivalent arg”,几乎总是因为改了队列声明的某个参数。这篇用 RabbitMQ 4.4.0 在本机逐个复现:哪些改动会触发、报错文字怎么读(包括值一样还报错的情形)、出错对 channel 和未确认消息的影响,以及两种修法各自的代价。
x-message-ttl 60000 → 5000:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-message-ttl' for queue 'q_ttl' in vhost '/': received '5000' but current is '60000'")
原来没有 x-message-ttl,现在加上:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-message-ttl' for queue 'q_ttl2' in vhost '/': received the value '5000' of type 'signedint' but current is none")
原来有 x-message-ttl,现在不带参数:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-message-ttl' for queue 'q_ttl3' in vhost '/': received none but current is the value '60000' of type 'signedint'")
参数完全相同(对照):第二次声明成功(等价)再次声明必须与现有队列等价,否则 406。
durable True → False:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'durable' for queue 'q_dur' in vhost '/': received 'false' but current is 'true'")
auto_delete False → True:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'auto_delete' for queue 'q_ad' in vhost '/': received 'true' but current is 'false'")
classic → quorum(x-queue-type):
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-queue-type' for queue 'q_type' in vhost '/': received 'quorum' but current is 'classic'")
quorum → classic:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-queue-type' for queue 'q_type2' in vhost '/': received 'classic' but current is 'quorum'")
x-max-length 100 → 200:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-max-length' for queue 'q_ml' in vhost '/': received '200' but current is '100'")
x-dead-letter-exchange dlx1 → dlx2:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-dead-letter-exchange' for queue 'q_dlx' in vhost '/': received 'dlx2' but current is 'dlx1'")
x-message-ttl 5000(整数)→ "5000"(字符串):
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'x-message-ttl' for queue 'q_ty' in vhost '/': received '5000' but current is '5000'")
交换机:
exchange direct → topic:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'type' for exchange 'ex1' in vhost '/': received 'topic' but current is 'direct'")
exchange durable True → False:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'durable' for exchange 'ex2' in vhost '/': received 'false' but current is 'true'")
exchange 声明为 internal 前后:
ChannelClosedByBroker: (406, "PRECONDITION_FAILED - inequivalent arg 'internal' for exchange 'ex3' in vhost '/': received 'true' but current is 'false'")durable、auto_delete、队列类型、最大长度、死信交换机、交换机类型等;值相同但类型不同也算不同。
同一个 channel 上收到 3 条且没有 ack;(ready, unacked) = (0, 3) 在这个 channel 上用不同参数重新声明 -> ChannelClosedByBroker(406) channel.is_closed = True,connection.is_open = True;(ready, unacked) = (3, 0)(没 ack 的消息回到了队列) 已关闭的 channel 再声明 -> ChannelWrongStateError: Channel is closed. 同一个连接上新开一个 channel -> 正常
只关闭 channel,未 ack 的消息回队列;声明和消费最好分开 channel。
queue_declare(q_ttl, passive=True)(队列带 x-message-ttl=60000,这里没写参数)-> 成功,messages = 0 不存在的队列 passive 声明 -> (404, "NOT_FOUND - no queue 'no_such_queue' in vhost '/'")
只检查存在性,不比较参数;不存在是 404。
$ rabbitmqctl set_policy ttl_pol "^q_pol$" '{"message-ttl":60000}' --apply-to queues
Setting policy "ttl_pol" for pattern "^q_pol$" to "{"message-ttl":60000}" with priority "0" for vhost "/" ...
客户端 queue_declare(q_pol) 不带任何参数 -> 成功
rabbitmqctl list_queues name policy effective_policy_definition 里 q_pol 那行:q_pol | ttl_pol | [{<<"message-ttl">>, 60000}]
$ rabbitmqctl set_policy ttl_pol "^q_pol$" '{"message-ttl":5000}' --apply-to queues
Setting policy "ttl_pol" for pattern "^q_pol$" to "{"message-ttl":5000}" with priority "0" for vhost "/" ...
改成 5000 之后:q_pol | ttl_pol | [{<<"message-ttl">>, 5000}]
客户端再次 queue_declare(q_pol)(仍不带参数)-> 仍然成功把可变参数交给 policy,客户端声明不带参数,可随时改。
classic 队列 q_mig 里有 5 条消息(ready) 声明为 quorum -> (406, "PRECONDITION_FAILED - inequivalent arg 'x-queue-type' for queue 'q_mig' in vhost '/': received 'quorum' but cu") queue_delete 返回 message_count = 5(这些消息被丢弃) 重建成 quorum 之后消息数 = 0
不可变属性只能删除重建,消息会丢。