79 lines
2.8 KiB
Markdown
79 lines
2.8 KiB
Markdown
[← 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)
|