TL;DR

  • Scenario: Use Java (amqp-client) to run Hello World, and clearly write out the producer/consumer chain from connection establishment to ACK
  • Conclusion: Default exchange "" directly delivers messages with “routingKey=queueName” to queue; mandatory can fallback, immediate not supported in RabbitMQ
  • Output: A对照 end-to-end process checklist + common fault diagnosis/fix quick reference

Version Matrix

ItemVerified Description
RabbitMQ Server 4.2.2 (2025)Documentation confirms official “latest release” wording; concepts and default exchange mechanism still apply
RabbitMQ Server 3.x (AMQP 0-9-1 common deployment)Documentation confirms body and code belong to AMQP 0-9-1 approach
Java client com.rabbitmq:amqp-client 5.28.0Documentation confirms current version description for RabbitMQ official Java Client
Default exchange "" behaviorDocumentation confirms queue declaration auto-binds to default exchange, routingKey=queueName routes
immediate flagDocumentation confirms RabbitMQ does not support immediate

Workflow

Producer Flow

  1. Producer connects to RabbitMQ: Producer first establishes TCP connection (Connection) with RabbitMQ server, this is the foundation of network communication. After connection is established, producer opens a channel (Channel) on that connection for actual message transmission. Channels are lightweight, multiple channels can share the same TCP connection.

  2. Declare Exchange: Producer declares an exchange through channel, setting exchange type (direct/topic/fanout/headers), durability and other attributes.

  3. Declare Queue: Producer declares queue and configures attributes: durable, exclusive, auto-delete, etc.

  4. Bind Exchange and Queue: Producer binds exchange and queue through bindingKey. For direct type, bindingKey exactly matches RoutingKey; for topic type, wildcards can be used.

  5. Send Message to RabbitMQ Broker: Message contains RoutingKey, exchange name, message properties (durable, priority, expiration, etc.) and message body.

  6. Exchange Routes Message: Exchange finds matching queue based on RoutingKey and binding relationship:

    • Matching queue found: Message stored in queue waiting for consumer processing
    • No matching queue found: Handle based on mandatory flag
      • mandatory=true: Message returned to producer via Basic.Return
      • mandatory=false: Message directly discarded
  7. Close Channel and Connection: Call channel.close() and connection.close() in sequence to release resources.

Application Scenario Example:

  • Order system uses RabbitMQ to process order messages
  • Producer (order service) declares direct type exchange order.exchange
  • Declares durable queue order.queue, binding key is order.create
  • When sending message, set RoutingKey to order.create

Consumer Flow

  1. Establish Connection: Consumer connects to RabbitMQ Broker via TCP protocol. This is a long-lived TCP connection.

  2. Create Channel: Create new channel (Channel) in established connection to isolate different message streams.

  3. Declare Queue: Consumer ensures target queue exists, declares queue via queue.declare method.

  4. Bind for Consumption: Register consumption request to Broker using basic.consume method, set message processing callback function.

  5. Message Delivery: RabbitMQ Broker delivers messages to consumer in FIFO order.

  6. Process Message: Consumer receives and processes business logic after receiving message. Processing should be designed as idempotent.

  7. Send Acknowledgment: After processing completes, explicitly send acknowledgment (ACK) via basic.ack method, carrying the message’s delivery tag.

  8. Message Deletion: After RabbitMQ receives ACK, permanently deletes that message from queue.

  9. Close Channel and Connection: Call channel.close() and connection.close() in sequence.

Notes:

  • Production environment recommends implementing connection reconnection mechanism
  • Message processing should be in try-catch block, use NACK reasonably when handling exceptions
  • For important messages, recommend implementing manual acknowledgment mode instead of auto acknowledgment
  • Reasonably set QoS (prefetch count) to avoid consumer overload

Case Test

Hello World one-to-one simple mode, producer directly sends message to RabbitMQ, other end consumes. When no Exchange is defined and specified, the AMQP Default built-in Exchange is used.

HelloSender

package icu.wzk.demo;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * RabbitMQ: Message Broker (receives messages and forwards to downstream applications)
 *
 * Terminology
 * - Producer / Producing: Application/behavior that sends messages
 * - Queue: Internal RabbitMQ component, messages stored in queue (occupies host memory/disk, limited by resources)
 *          Can be understood as "message buffer": multiple Producers can write to same queue, multiple Consumers can read from same queue
 * - Consumer / Consuming: Application/behavior that receives (consumes) messages
 *
 * Note
 * - Producer, Consumer, Queue don't need to be on same host; usually distributed on different hosts' different applications
 * - Same application can also play both Producer and Consumer roles
 */
public class HelloSender {

    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("admin");
        factory.setPassword("secret");
        try {
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
            // Queue declaration: non-durable, non-exclusive, auto-delete
            channel.queueDeclare(QUEUE_NAME, false, false, true, null);
            String message = "hello wzk.icu !";
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println("[x] Sent '" + message + "'");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

HelloReceiver

package icu.wzk.demo;

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;

/**
 * RabbitMQ: Message Broker (receives messages and forwards to downstream applications)
 *
 * Terminology
 * - Producer / Producing: Application/behavior that sends messages
 * - Queue: Internal RabbitMQ component, messages stored in queue (occupies host memory/disk, limited by resources)
 *          Can be understood as "message buffer": multiple Producers can write to same queue, multiple Consumers can read from same queue
 * - Consumer / Consuming: Application/behavior that receives (consumes) messages
 *
 * Note
 * - Producer, Consumer, Queue don't need to be on same host; usually distributed on different hosts' different applications
 * - Same application can also play both Producer and Consumer roles
 */
public class HelloSender {

    private static final String QUEUE_NAME = "hello";

    public static void main(String[] args) {
        ConnectionFactory factory = new ConnectionFactory();
        factory.setHost("localhost");
        factory.setPort(5672);
        factory.setVirtualHost("/");
        factory.setUsername("admin");
        factory.setPassword("secret");
        try {
            Connection connection = factory.newConnection();
            Channel channel = connection.createChannel();
            // Queue declaration: non-durable, non-exclusive, auto-delete
            channel.queueDeclare(QUEUE_NAME, false, false, true, null);
            String message = "hello wzk.icu !";
            channel.basicPublish("", QUEUE_NAME, null, message.getBytes());
            System.out.println("[x] Sent '" + message + "'");
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

Note: The above HelloReceiver code is the same as HelloSender. Actual consumer code should include DeliverCallback and basicConsume methods.


Error Quick Reference

SymptomRoot CauseFix
Producer prints “Sent” but queue has no messageSent to unexpected exchange/routingKey; or queue not bound as expectedManagement console看 Exchange/Binding; confirm using default exchange "" and routingKey=queueName
Message “swallowed”, no exception on producer sidemandatory=false and message can’t route to any queue, message directly discardedWhen reproducing, turn on mandatory and listen to Basic.Return; set mandatory=true for key chains
Article mentions immediate but actual effect doesn’t matchRabbitMQ does not support immediate semanticsCheck official specifications; remove immediate-related statements
Consumer starts but doesn’t consume/no callbackMissing basicConsume and callback; or example code pasted as producerCheck if HelloReceiver includes DeliverCallback / basicConsume
PRECONDITION_FAILED - inequivalent arg…Re-declared same name queue with inconsistent parametersUnify queue declaration parameters; producer/consumer keep same queue consistent
Message “consumed once then comes back/duplicate processing”Manual ACK threw exception, connection interrupted; or NACK requeueBusiness idempotency; try/catch internally decides ACK/NACK; close requeue if necessary or transfer to DLX
Consumer overwhelmed, latency spikesprefetch too large/no QoS; single message processing slow causing backlogSet reasonable prefetch; split consumer instances; async slow operations
Queue “disappears” itselfDeclared as autoDelete or exclusive, deleted when connection disconnectsProduction use durable=true, autoDelete=false, exclusive=false