All Products
Search
Document Center

ApsaraMQ for RabbitMQ:Best practices for instance throttling

Last Updated:Aug 27, 2026

ApsaraMQ for RabbitMQ enforces per-instance TPS limits and closes channels that exceed thresholds. This guide covers prevention, monitoring, and error recovery.

How throttling works

ApsaraMQ for RabbitMQ enforces TPS limits at three levels:

  • Instance total TPS -- caps the combined send and receive throughput of the entire instance.

  • Single-node SendMessage TPS -- caps the send throughput on each backend service node within the cluster.

  • Per-API operation TPS -- caps specific operations such as basicGet, queueDeclare, and exchangeDeclare.

When any limit is exceeded, the broker applies the following behavior:

  • The broker returns reply-code=530 with reply-text=denied for too many requests. The error details are described in Error code and message.

  • The broker closes the channel that sent the request, while the connection remains open. Recovery by reopening the channel is described in Recover from channel closure.

Prevent throttling

Instance total TPS

Choose the approach that matches your traffic pattern:

Traffic pattern

Action

Testing, short-term, or unpredictable traffic

Use a serverless instance. For subscription instances, enable the elastic TPS feature.

Stable, high-volume production traffic

Upgrade the TPS specification to a higher tier.

Single-node SendMessage TPS

ApsaraMQ for RabbitMQ distributes traffic across multiple backend nodes. If traffic concentrates on one node, the per-node limit triggers even when the instance-level limit has headroom.

To distribute load evenly across nodes:

  • Open at least 10 connections per queue. Each connection may route to a different node, distributing the send workload.

  • For Spring users, set CachingConnectionFactory to CONNECTION mode. This creates a new connection per session instead of multiplexing channels on one. Spring integration.

Monitor peak TPS

Monitor peak TPS to detect when traffic nears the throttling threshold:

CloudMonitor (recommended)

  • Granularity: minute-level (1-minute period).

  • Scope: instance-level peak TPS.

  • Cost: free.

  • Shows TPS trends over the past 14 days.

  • Supports alert rules to notify before throttling occurs.

Query the peak TPS of an instance and configure an alert rule by using CloudMonitor.

Instance Details page (recommended)

  • Granularity: second-level peak TPS.

  • Scope: instance-level and per-API-operation peak TPS.

  • Cost: free.

  • Pinpoints short traffic spikes with second-level precision.

  • Supports per-API-operation filtering.

Only the first 10 minutes of results are displayed.

Query the peak TPS of an instance on the Instance Details page.

Simple Log Service

  • Granularity: second-level peak TPS.

  • Scope: instance-level peak TPS.

  • Cost: billed by Simple Log Service (Billable items of pay-by-feature).

  • Supports SLS query statements for advanced analysis.

Requires familiarity with SLS query syntax. Results may be harder to interpret than CloudMonitor or Instance Details.

Query the peak TPS of an instance by using Simple Log Service.

Throttling thresholds

The following tables list throttling limits by instance type and specification.

Instance total TPS

Serverless instances

Cluster type

Billing model

Throttling threshold

Shared cluster

Pay-by-provisioned-capacity-and-elastic-traffic / pay-by-messaging-request

Maximum: 50,000 TPS

Exclusive cluster

Pay-by-provisioned-capacity-and-elastic-traffic

2x the peak TPS included in the basic specification

Subscription instances

Edition

Elastic TPS

Throttling threshold

Enterprise Edition

Disabled

1x the peak TPS included in the basic specification

Enterprise Edition

Enabled

2x the peak TPS included in the basic specification (maximum: 50,000 TPS)

Enterprise Platinum Edition

Disabled

1x the peak TPS included in the basic specification

Enterprise Platinum Edition

Enabled

2x the peak TPS included in the basic specification (maximum: 50,000 TPS)

Professional Edition

Disabled

1x the peak TPS included in the basic specification

Professional Edition

Enabled

1.5x the peak TPS included in the basic specification

Single-node SendMessage TPS

The broker limits SendMessage TPS on each backend node in the instance.

Instance type

Throttling threshold

Serverless -- shared (by cumulative amount)

25,000 TPS

Serverless -- dedicated (reserved + elastic)

25,000 TPS

Subscription -- Enterprise Edition

None

Subscription -- Enterprise Platinum Edition (reserved + elastic)

25,000 TPS

Subscription -- Professional Edition

25,000 TPS

Per-API operation limits

These limits apply per instance. Serverless exclusive cluster instances have no per-API throttling.

Operation

API method

Serverless (shared)

Serverless (exclusive)

Subscription

Synchronous message receiving

basicGet

500 TPS

None

500 TPS

Queue clearance

purgeQueue

500 TPS

None

500 TPS

Exchange creation

exchangeDeclare

500 TPS

None

500 TPS

Exchange deletion

exchangeDelete

500 TPS

None

500 TPS

Queue creation

queueDeclare

500 TPS

None

500 TPS

Queue deletion

queueDelete

500 TPS

None

500 TPS

Binding creation

queueBind

500 TPS

None

500 TPS

Binding deletion

queueUnbind

500 TPS

None

500 TPS

Message restoration

basicRecover

500 TPS

None

500 TPS

Message requeuing

basicReject(requeue=true) / basicNack(requeue=true)

20 TPS

None

20 TPS

Handle throttling errors

Error code and message

When throttling is triggered, the broker closes the affected channel and returns:

  • Error code: reply-code=530

  • Error message: reply-text=denied for too many requests

Typical Java stack trace for a throttling error:

Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>
(reply-code=530, reply-text=denied for too many requests, ReqId:5FB4C999314635F952FCBFF6, ErrorHelp[dstQueue=XXX_test_queue,
srcExchange=Producer.ExchangeName,bindingKey=XXX_test_bk, http://mrw.so/6rNqO8], class-id=50, method-id=20)
    at com.rabbitmq.client.impl.ChannelN.asyncShutdown(ChannelN.java:516)
    at com.rabbitmq.client.impl.ChannelN.processAsync(ChannelN.java:346)
    at com.rabbitmq.client.impl.AMQChannel.handleCompleteInboundCommand(AMQChannel.java:182)
    at com.rabbitmq.client.impl.AMQChannel.handleFrame(AMQChannel.java:114)
    at com.rabbitmq.client.impl.AMQConnection.readFrame(AMQConnection.java:672)
    at com.rabbitmq.client.impl.AMQConnection.access$300(AMQConnection.java:48)
    at com.rabbitmq.client.impl.AMQConnection$MainLoop.run(AMQConnection.java:599)
    at java.lang.Thread.run(Thread.java:748)

The error includes the request ID (ReqId), destination queue (dstQueue), source exchange (srcExchange), and binding key (bindingKey).

Recover from channel closure

Because only the channel is closed, catch AlreadyClosedException and recreate it. The following Java example demonstrates retry-based recovery:

private static final int MAX_RETRIES = 5; // Maximum retry attempts
private static final long WAIT_TIME_MS = 2000; // Wait time between retries in milliseconds

private void doAnythingWithReopenChannels(Connection connection, Channel channel) {
    try {
        // ......
        // Any operation to be performed in the current channel.
        // For example, sending or consuming messages.
        // ......

    } catch (AlreadyClosedException e) {
        String message = e.getMessage();
        if (isChannelClosed(message)) {
            // Channel was closed by the broker. Recreate it.
            channel = createChannelWithRetry(connection);
            // Continue with other operations after recovery.
            // ......
        } else {
            throw e;
        }
    }
}

private Channel createChannelWithRetry(Connection connection) {
    for (int attempt = 1; attempt <= MAX_RETRIES; attempt++) {
        try {
            return connection.createChannel();
        } catch (Exception e) {
            System.err.println("Failed to create channel. Attempt " + attempt + " of " + MAX_RETRIES);
            // If channel creation fails (possibly still throttled), wait and retry.
            if (attempt < MAX_RETRIES) {
                try {
                    Thread.sleep(WAIT_TIME_MS);
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt(); // Restore the interrupted state.
                }
            } else {
                throw new RuntimeException("Exceeded maximum retries to create channel", e);
            }
        }
    }
    throw new RuntimeException("This line should never be reached");
}

private boolean isChannelClosed(String errorMsg) {
    // Check whether the error message contains "channel.close".
    // This covers both error code 530 (throttling) and 541 (internal error).
    if (errorMsg != null && errorMsg.contains("channel.close")) {
        System.out.println("[ChannelClosed] Error details: " + errorMsg);
        return true;
    }
    return false;
}

Key points:

  • The retry uses a fixed 2,000 ms wait. Adjust based on your traffic pattern.

  • The isChannelClosed method checks for channel.close in the error message, which covers both reply-code=530 (throttling) and other channel closure scenarios.

  • After recovery, resume operations on the new channel object.