rabbitMQ getting started guide: detailed instructions based on Java client

1 Introduction

  In the previous article , ( the following examples will use the configuration that has been configured in this tutorial ) we introduced the common functions of the RabbitMQ management page in detail. By viewing and operating the switches and queues in the RabbitMQ service, we have a preliminary understanding of RabbitMQ working mechanism. Now, we will further explore the working mechanism of RabbitMQ, this time we will use the official Java client provided by RabbitMQ for practice. By using the Java client, we can gain a deeper understanding of how RabbitMQ works and interact with message queues at the code level.

2. Basic programming model of RabbitMQ

2.1 Environment configuration

When using the official client, if it is a maven project, you need to introduce related dependencies, and jdk must be 1.8+

<dependency>
    <groupId>com.rabbitmq</groupId>
    <artifactId>amqp-client</artifactId>
    <version>5.18.0</version>
</dependency>

2.2 Create connection and get channel

  When using RabbitMQ's Java client to interact with the RabbitMQ server, you first need to create a connection (Connection), and then obtain a channel (Channel) through this connection. Channel is the main working interface of almost all API methods, and each thread should have its own independent channel.

ConnectionFactory factory = new ConnectionFactory();
// 服务所在的地址,默认:localhost
factory.setHost("192.168.1.11");
// 服务的ip,可以不填,默认:5672
factory.setPort(ConnectionFactory.DEFAULT_AMQP_PORT);
// 操作用户,再管理后台的Admin->User配置
factory.setUsername("tom");
// 用户的密码
factory.setPassword("abc123");
// 虚拟机名称
factory.setVirtualHost("v-1");
// 创建连接
Connection connection = factory.newConnection();
// 获取通道
Channel channel = connection.createChannel();

  Usually only one object needs to be created on a client Channel, because it can be reused as long as it is not closed. However, if there is a need to create multiple Channels, it is necessary to ensure that the Channel is unique. When creating, you can createChannelpass in an assigned integer parameter in the method, which will serve as the unique identifier of the Channel.

Channel createChannel(int channelNumber) throws IOException;

Note: Create a new channel, using the specified channel number if possible. Use openChannel(int) if you want to use an Optional to deal with a value. Translation: Create a new channel, using the specified
channel number if possible. If you want to handle values ​​with Optional, use openChannel(int).

2.3 Declare exchange (not required)

  Declaring an exchange is a critical step in setting up message routing. When sending a message, you can choose to declare an exchange and specify its type ( direct, fanout, headersor topic), or you can not declare an exchange and use RabbitMQ's default exchange directly.

// 声明非自动删除、非持久的交换机,名称是:e-1,类型是:direct
channel.exchangeDeclare("e-1", "direct");

Declarative method for complete configuration

exchangeDeclare(String exchange,String type,boolean durable,boolean autoDelete,boolean internal,Map<String, Object> arguments) throws IOException;

The return value of this method is an Exchange.DeclareOkobject representing the successful response of the switch statement.

Parameters:
exchange : the name of the exchange
type : the type of the exchange. Specifies the type of switch, optional values ​​include: direct, topic, fanoutor headers. Different types of switches determine the routing strategy and matching rules for messages.
durable : Specifies whether the switch should be persisted to disk. If set to true, the switch will persist after the RabbitMQ server is restarted; if set to false, it will be deleted when the RabbitMQ server is restarted. It should be noted that the persistence of the message can only be ensured when both the switch is declared and the message is sent as persistent.
autoDelete : Specifies whether the exchange is automatically deleted when there are no queues bound to it. If set to true, the exchange will be automatically deleted after all queues bound to the exchange are unbound.
internal : Specifies whether the exchange is automatically deleted when there are no queues bound to it. If set to true, the exchange will be automatically deleted after all queues bound to the exchange are unbound.
arguments : Other parameters used to set the switch, passed as key-value pairs. These parameters can be used for specific needs, such as setting the backup switch of the switch, message expiration time, etc. Parameters are provided as string keys and object values.

All configurations of this method can be configured on the management page, only the name is required, which is the same as the method of the client.
insert image description here
Example:

//声明一个类型位direct,名称是e-1,持久化,不自动删除的交换机
channel.exchangeDeclare("e-1","direct", true, false, null);

After execution, go to the management page to view. The e-1 switch we just created has been added under the v-1 virtual machine .
insert image description here
  It should be noted here that when calling the method of declaring the switch, it will be created automatically if it does not exist on the service. If Yes, the parameters we call the method need to be consistent with the parameters of the existing switch. An error will be reported if it is inconsistent.
For example, if I execute the following code, modify the type of the previously created switch and call it again

channel.exchangeDeclare("e-1","topic", true, false, null);

The client will report an error:

Caused by: com.rabbitmq.client.ShutdownSignalException: channel error; protocol method: #method<channel.close>(reply-code=406, reply-text=PRECONDITION_FAILED - inequivalent arg ‘type’ for exchange ‘e-1’ in vhost ‘v-1’: received ‘topic’ but current is ‘direct’, class-id=40, method-id=10)

2.4 declare queue

  A queue is an internal object of RabbitMQ for storing messages. When declaring a queue, you need to specify the name of the queue, and you can set some attributes of the queue, such as whether it is durable (Durable), whether it is exclusive (Exclusive) or whether it is automatically deleted (Auto-delete).

queueDeclare(String queue, boolean durable, boolean exclusive, boolean autoDelete,Map<String, Object> arguments) throws IOException;

The return value of this method is a Queue.DeclareOkobject representing the successful response of the queue declaration. If the queue already exists and the parameters match, returns the declaration information for the existing queue.

Parameters:
queue : queue name
durable : whether the queue should be persisted to disk. If set to true, the queue will still exist after the RabbitMQ server is restarted; if set to false, it will be deleted when the RabbitMQ server is restarted. It should be noted that only when the queue is declared and when the message is sent is set to persistence, the persistence of the message can be ensured.
exclusive : Specifies whether the queue is an exclusive queue. If set to true, only the connection that declared it is allowed to use the queue, and the queue is automatically deleted when the connection is closed. Exclusive queues are suitable for scenarios where there is only one consumer.
autoDelete : Specifies whether the queue is automatically deleted when there are no consumer subscriptions. If set to true, the queue is automatically deleted after all consumers associated with the queue unsubscribe.
arguments : Other parameters used to set the queue, passed as key-value pairs. These parameters can be used for specific needs, such as setting the message expiration time, maximum length, etc. of the queue. Parameters are provided as string keys and object values.

2.4.1 Declaring a Classic queue

In RabbitMQ, the default queue type is the Classic queue. The simplest way to declare:

channel.queueDeclare("q-1", true, false, false, null);

Or add a parameter declaration, which later declares that the arbitration queue and the flow queue are more similar in form:

Map<String, Object> argMap = new HashMap<>();
argMap.put("x-queue-type", "classic");
channel.queueDeclare("classic_q", true, false, false, argMap);

After execution, go back to the Queues tab page of the management page, and you can see that the results declared by the two programming methods are the same
insert image description here

2.4.2 Declare Quorum Queue

  Quorum queue is a new persistent queue provided by RabbitMQ, which provides higher data security. The declaration method only needs to add the parameter x-queue-type equal to quorum , and it is recommended to add x-max-length-bytesandx-stream-max-segment-size-bytes

x-stream-max-segment-size-bytes: Set the maximum number of bytes for each data segment (segment) in the Stream queue. The data of a Stream queue is organized according to segments, and each segment contains a set of messages. This parameter allows you to control the size of each segment in order to optimize data storage and retrieval performance.

Please note that the above settings will affect the storage and performance of RabbitMQ, and should be set according to the actual situation.

Map<String, Object> argMap = new HashMap<>();
argMap.put("x-queue-type", "stream");
//stream最大字节数1G
argMap.put("x-max-length-bytes", 10_000_000_000L);
//每个segment大小是50M
argMap.put("x-stream-max-segment-size-bytes", 50_000_000);
channel.queueDeclare("stream_q", true, false, false, argMap);

2.4.3 Declare the Stream queue

  Stream queue is a special type in RabbitMQ, which is suitable for scenarios that need to process data streams. The declaration method needs to add the parameter x-queue-type equal to stream , and also needs to add

Map<String, Object> argMap = new HashMap<>();
argMap.put("x-queue-type", "stream");
channel.queueDeclare("classic_q", true, false, false, argMap);

2.4.4 Detailed description of queue parameters

As you can see from the previous management panel, RabbitMq provides us with some optional Arguments configurations. The general meanings of these configurations are as follows:

  1. Auto expire : This parameter is used to set the expiration time of the queue (in milliseconds). If the queue is not subscribed by any consumer within the set time, and there is no other operation (such as queue declaration, clearing the queue, etc.), then the queue will be automatically deleted.
    Example : The queue expires after 30 minutes of inactivity.
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-expires", 1800000);
channel.queueDeclare("myqueue", false, false, false, args);
  1. Message TTL (Time To Live) : This parameter is used to set the lifetime of messages in the queue (in milliseconds). If the message is not consumed within the set time, the message will be automatically deleted.
    Example : Messages can live up to 60 seconds
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-message-ttl", 60000);
channel.queueDeclare("myqueue", false, false, false, args);
  1. Overflow behavior : This parameter is used to set how to handle newly arrived messages when the messages in the queue reach the maximum length or maximum bytes. Possible values ​​are: " drop-head" (delete the message at the head of the queue), " reject-publish" (reject new publication requests), or " reject-publish-dlx" (reject new publication requests and route these messages to the dead letter queue).
    Example : Declare a queue with a maximum length of 10 messages, and delete the previous ones after the message is greater than 10
Map<String, Object> argsMap = new HashMap<String, Object>();
argsMap.put("x-overflow", "drop-head");
argsMap.put("x-max-length", 10);
channel.queueDeclare("overflow_q", true, false, false, argsMap);
  1. Single active consumer : When this parameter is set to true, only one consumer can consume messages from the queue. If multiple consumers try to subscribe to the same queue, RabbitMQ will ensure that only one consumer receives the message. When the consumer disconnects, RabbitMQ will choose the next consumer to receive the message.

Example: Enabling a Single Activity Consumer

Map<String, Object> argsMap = new HashMap<String, Object>();
argsMap .put("x-single-active-consumer", true);
ch.queueDeclare("single_q", false, false, false, argsMap );
  1. Dead letter exchange : This parameter is used to set the dead letter exchange. Messages are sent to this exchange when they are removed from the queue for some reason (e.g. rejected, time-to-live, etc.).
    Example: Declare a dead letter switch for the queue, dead_exc is the dead letter switch that has been created on the service, and messages in the dead_q queue that meet the rules will enter the switch
channel.exchangeDeclare("exc-1", "direct");
Map<String, Object> args = new HashMap<String, Object>();
args.put("x-dead-letter-exchange", "dead_exc");
channel.queueDeclare("dead_q", false, false, false, args);
  1. Dead letter routing key : This parameter is used to set the routing key for messages sent to the dead letter exchange.
    Example: On the basis of 5, messages can be delivered through the routing key configured by the switch, and routing-key-a is the routing key
args.put("x-dead-letter-routing-key", "routing-key-a");
  1. Max length : This parameter is used to set the maximum number of messages that the queue can hold. When new messages arrive and the number of messages in the queue has reached this number, some messages may be deleted, or new messages may be rejected, depending on the setting of the "Overflow behavior" parameter.
    For example, refer to the previous point 3

  2. Max length bytes : This parameter is used to set the maximum bytes of messages that the queue can hold. When a new message arrives, and the message in the queue has reached this byte, some messages may be deleted, or new messages may be rejected, depending on the setting of the "Overflow behavior" parameter.
    Example: Declare a queue with a maximum byte size of 32, and stop receiving messages if it is larger than 32 bytes

Map<String, Object> argsMap = new HashMap<String, Object>();
argsMap.put("x-overflow", "reject-publish");
argsMap.put("x-max-length-bytes", 32);
channel.queueDeclare("overflow_max_bytes_q", true, false, false, argsMap);

If you continue to add messages beyond the byte limit, an error will be reported: rejected Unable to publish message. Check queue limits.

  1. Leader locator : This parameter is used to set the leader selection strategy in the Quorum queue.
    The optional values ​​are:
    balanced: The leader of the queue will be evenly distributed in the cluster. This strategy can effectively distribute the load of the entire cluster
    client-local: the default option, the leader of the queue will be selected as the node closest to the client currently opening the channel. This strategy minimizes network latency.

2.5 Binding of queue and exchange (not required)

  Bindings are links between queues and exchanges. Once the binding is created, the exchange knows how to route the message to the correct queue based on the routing key. Binding is not required, if no binding is created, messages will be sent to RabbitMQ's default exchange.
Example: Bind q-1 queue to e-1 exchange

channel.exchangeDeclare("e-1","direct", true, false, null);
channel.queueDeclare("q-1", true, false, false, null);
channel.queueBind("q-1", "e-1", "");

Check the management background, you can see that a queue has been bound on the switch, and the button Unbindis to unbind the operation
insert image description here

2.6 Producer sends message

  When a producer sends a message, it needs to specify an exchange and a routing key. RabbitMQ will then decide how to route the message based on this exchange and routing key. In the Java client, the producer usually uses the channel.basicPublish() method to send messages.
Example: send 100 messages to queue q -1

Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
channel.queueDeclare("q-1", true, false, false, null);
int count = 0;
while (count++ < 100){
    
    
    channel.basicPublish("","q-1", null, ("hello world " + count).getBytes());
}
channel.close();
ConnectionUtils.closeConnection();

Check the situation in the management background queue, you can see that it has been pushed to the service at this time
insert image description here

2.7 Consumer consumption news

  There are two main ways for consumers to consume messages: one is to call the channel.basicGet() method to actively pull the message, and the other is to use the channel.basicConsume() method to register a callback function, and then RabbitMQ pushes the message to the consumer. After receiving the message, the consumer needs to send a message confirmation signal to RabbitMQ.

2.7.1 Active pull

Example: consume 100 messages

Connection connection = ConnectionUtils.getConnection();
Channel channel = connection.createChannel();
int count = 0;
while(count++ < 100){
    
    
    GetResponse getResponse = channel.basicGet("q-1", true);
    byte[] body = getResponse.getBody();
    System.out.println("消息内容:" + new String(body));
}
channel.close();
ConnectionUtils.closeConnection();

2.7.2 How to register callback function

Start the thread and wait for the server to push the message. If there is a message in the listening queue, it will also be pushed. In the example, DefaultConsumer officially provides a default listener. If you need to customize it, you only need to implement Consumerthe interface.

new Thread(() -> {
    
    
    try {
    
    
        Connection connection = ConnectionUtils.getConnection();
        Channel channel = connection.createChannel();
        channel.basicConsume("q-1",
                true,
                new DefaultConsumer(channel){
    
    
                    @Override
                    public void handleConsumeOk(String consumerTag) {
    
    
                        System.out.println("消费者启动成功");
                    }
                    @Override
                    public void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {
    
    
                        System.out.println("消息内容:" + new String(body));
                    }
                });
    } catch (Exception e) {
    
    
        e.printStackTrace();
    }
}).start();

2.8 Close the connection to release resources

  After completing all message publishing and receiving tasks, the channel and connection should be closed to release resources. In the Java client, this can be done with calls channel.close()and connection.close()methods.

channel.close();
connection.close();

3. Summary

  This article mainly introduces in detail how to use the official Java client provided by RabbitMQ. Mainly based on the programming model perspective, it introduces in detail how to declare Exchange and Queue, including Classic queue, Quorum queue, and Stream queue, the binding of Queue and Exchange, and also lists various parameters and their meanings. Finally, examples show how producers send messages and consumers consume messages. I hope this article can help you master the use of RabbitMQ Java client more quickly.

Guess you like

Origin blog.csdn.net/dougsu/article/details/131775821