Add the rabbitmq module
This commit is contained in:
78
rabbitmq/docs/03-the-silent-drop.md
Normal file
78
rabbitmq/docs/03-the-silent-drop.md
Normal file
@@ -0,0 +1,78 @@
|
||||
[← Exchanges](02-exchanges.md) · [Module README](../README.md) · [Dead-lettering →](04-dead-lettering.md)
|
||||
|
||||
# 3. The message that goes nowhere and says nothing
|
||||
|
||||
Publish to `orders.direct` with routing key `amend`. No binding matches. The broker discards the
|
||||
message. `convertAndSend` returns normally. Nothing is logged, by the broker or by Spring, and
|
||||
the message is gone.
|
||||
|
||||
This is correct AMQP behaviour and it is the single most expensive default in RabbitMQ, because
|
||||
the symptom is "the consumer isn't running" and the cause is three services away.
|
||||
|
||||
**Two settings are required, and they live in different places:**
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
rabbitmq:
|
||||
publisher-returns: true # on the connection factory
|
||||
template:
|
||||
mandatory: true # on the template
|
||||
```
|
||||
|
||||
`publisher-returns` puts the connection into a mode where returns are listened for at all.
|
||||
`mandatory` asks the broker to return *this* publish if it is unroutable. Setting only
|
||||
`mandatory` — which is what most examples show — changes nothing, and the test in this module
|
||||
was written that way first and failed.
|
||||
|
||||
Then register a callback:
|
||||
|
||||
```java
|
||||
template.setReturnsCallback((returned) -> log.error("unroutable: exchange={} key={} {} {}",
|
||||
returned.getExchange(), returned.getRoutingKey(),
|
||||
returned.getReplyCode(), returned.getReplyText()));
|
||||
```
|
||||
|
||||
From [`docs/output/unroutable.txt`](output/unroutable.txt):
|
||||
|
||||
```
|
||||
replyCode 312
|
||||
replyText NO_ROUTE
|
||||
exchange orders.direct
|
||||
routingKey amend
|
||||
```
|
||||
|
||||
`312 NO_ROUTE` is the code worth putting in an alert.
|
||||
|
||||
## What returns still do not give you
|
||||
|
||||
The callback fires **asynchronously, on the connection's thread**. `convertAndSend` had already
|
||||
returned successfully by then. So a return tells you a message was lost; it does not let you
|
||||
stop the caller from believing it succeeded.
|
||||
|
||||
If the caller must know, you need **publisher confirms** as well:
|
||||
|
||||
```yaml
|
||||
spring:
|
||||
rabbitmq:
|
||||
publisher-confirm-type: correlated
|
||||
```
|
||||
|
||||
Confirms and returns answer different questions, and you usually want both:
|
||||
|
||||
| | answers |
|
||||
|---|---|
|
||||
| **return** | the broker had nowhere to route this |
|
||||
| **confirm** | the broker took responsibility for this (`ack`), or refused it (`nack`) |
|
||||
|
||||
A message that is unroutable gets a **return followed by an `ack`** — the broker successfully did
|
||||
nothing with it. So a confirm alone will not tell you the message vanished, which is the trap
|
||||
inside the trap.
|
||||
|
||||
## The alternate exchange
|
||||
|
||||
The topology-level version of the same fix: give an exchange an `alternate-exchange` argument and
|
||||
unroutable messages go there instead of being dropped, with no publisher-side configuration at
|
||||
all. It costs one exchange and one queue, and it catches the publishes made by services that
|
||||
forgot to set `mandatory`.
|
||||
|
||||
[Dead-lettering →](04-dead-lettering.md)
|
||||
Reference in New Issue
Block a user