# Client Best Practices — RabbitMQ

Source: https://www.skillbyai.com/en/rabbitmq/p-clients

> Manage connections, channels and recovery correctly in client applications.

## Long-lived connections, careful channels

Most RabbitMQ problems in production come from client code. **Connections are expensive**: open one (or a few) per process and keep it for the life of the application, never one per message or per HTTP request. **Channels** are cheap but not free; in most client libraries a channel must **not be shared between threads**, so use a channel per thread or a pool, and use separate channels (or connections) for publishing and consuming so publisher flow control does not stall consumers. Enable **automatic connection recovery** (built into the Java and .NET clients; implemented with reconnect loops in others) and make topology declarations idempotent so they can be replayed after reconnects. Set **heartbeats** so dead TCP connections are detected. In Spring, **Spring AMQP** provides `RabbitTemplate` with confirms and returns, and `@RabbitListener` with configurable acknowledgement, prefetch, concurrency and retry with dead-lettering. Name connections (a client property) so they are identifiable in the management UI.

## One connection, several channels

A process keeps one long-lived connection and opens channels for publishing and consuming.

![A thick pipe between an application box and a broker box, with several thinner coloured lines running inside it.](assets/figures/rabbitmq/section-8-map.svg) — Figure 8.1 — Multiplexing channels over a long-lived connection.

## A Spring AMQP listener with manual concerns configured

Prefetch, concurrency and dead-lettering on rejection come from configuration.

```java
@Configuration
class RabbitConfig {
    @Bean
    SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory cf) {
        var f = new SimpleRabbitListenerContainerFactory();
        f.setConnectionFactory(cf);
        f.setPrefetchCount(20);
        f.setConcurrentConsumers(4);
        f.setMaxConcurrentConsumers(8);
        f.setDefaultRequeueRejected(false);      // failures go to the DLX, not back to the queue
        return f;
    }
}

@Component
class BillingListener {
    @RabbitListener(queues = "billing.orders")
    void onOrderPlaced(OrderPlaced event) {
        invoices.createFor(event);              // exception -> rejected -> dead-lettered
    }
}
```

## Never open a connection per message

A web handler that connects, publishes and disconnects for every request can overwhelm the broker with connection churn and TLS handshakes. Create the connection at start-up and reuse it.

**Quiz:** Which practice is recommended for RabbitMQ client connections?

- [ ] Open a new connection for every message
- [ ] Share one channel across all threads
- [x] Keep a long-lived connection per process and use channels for operations
- [ ] Disable heartbeats

*Answer:* Keep a long-lived connection per process and use channels for operations. Long-lived connections with appropriately scoped channels avoid churn and thread-safety problems.
