RabbitMQ notes SpringBoot integrates RabbitMQ's simple container (consumer)

Posted by GarroteYou on Fri, 31 Dec 2021 05:17:06 +0100

1, Introduction

   message middleware has a series of functions, such as low coupling, reliable delivery, broadcasting, flow control, final consistency, etc. it has become one of the main means of asynchronous RPC, such as ActiveMQ, RabbitMQ, Kafka, RocketMQ, etc. The main functions of message oriented middleware are as follows:

  • Asynchronous processing
  • Application decoupling
  • Flow peak clipping
  • Log processing

   RabbitMQ's default container is the simple container. Since version 2.0, there has been a direct container. Let's see the difference from the perspective of distributed architecture. This article mainly uses Spring Boot (2.5.2) to integrate RabbitMQ (2.5.2) and simple container to implement a consumer. The premise of this article is to have an installed RabbitMQ environment.

1.1 notes in this document

  • @Configuration: used to define the configuration class. The annotated class can contain one or more methods annotated by @ bean. These methods will be scanned by AnnotationConfigApplicationContext or AnnotationConfigWebApplicationContext classes, and used to build bean definitions and initialize the Spring container
  • @RabbitListener: @ RabbitListener annotation specifies the target method as the method of consuming messages, and specifies the listening queue or Binding through the annotation parameter@ The RabbitListener tag on the class indicates that when a message is received, it will be handed over to the method of @ RabbitHandler for processing
  • @RabbitHandler: it should be used together with the @ RabbitListener annotation. When a message is received, relevant methods will be called according to the parameter type converted by the MessageConverter

2, Maven dependency

pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.5.2</version>
        <relativePath/>
    </parent>

    <groupId>com.alian</groupId>
    <artifactId>rabbitmq-simple</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>rabbitmq-simple</name>
    <description>SpringBoot integration RabbitMQ of simple Container (consumer)</description>

    <properties>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
            <version>${parent.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
            <version>${parent.version}</version>
        </dependency>

        <!--rabbitMq The best version and springboot bring into correspondence with-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-amqp</artifactId>
            <version>${parent.version}</version>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.68</version>
        </dependency>

        <!--For serialization-->
        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10</version>
        </dependency>

        <!--java 8 Time serialization-->
        <dependency>
            <groupId>com.fasterxml.jackson.datatype</groupId>
            <artifactId>jackson-datatype-jsr310</artifactId>
            <version>2.9.10</version>
        </dependency>

        <!--It is packaged and uploaded to the private server for testing-->
        <dependency>
            <groupId>com.alian</groupId>
            <artifactId>common</artifactId>
            <version>0.0.1-SNAPSHOT</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.16</version>
        </dependency>

    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>
</project>

   it should be noted here that the following package is packaged into the private server by myself. In fact, an employee class, a payment class, and a constant class. You can also package your own entities into the private server, or directly implement my example through module development.

<dependency>
    <groupId>com.alian</groupId>
    <artifactId>common</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

3, Configuration class

3.1 basic configuration

  at the beginning of this article, we will complete this integration from the perspective of distributed architecture. We will use two system consumers and producers to complete this test. This troublesome way is not to avoid unit testing or module development, but to avoid everyone stepping on the pit, which is closer to our actual development. Therefore, I prepared several simple classes (MQConstants.java, Employee.java and PayRecord.java) in advance and put them into a jar package to maven private server. A configuration class stores MQ constants, an employee class and a payment class. The properties inside are common types, as follows.

MQConstants .java

package com.alian.common.constant;

public class MQConstants {

    /**
     * Switch
     */
    public final static String ALIAN_EXCHANGE_NAME = "ALIAN_EXCHANGE";

    /**
     * Queue name
     */
    public final static String ALIAN_QUEUE_NAME = "ALIAN_QUEUE";

    /**
     * Routing key
     */
    public final static String ALIAN_ROUTINGKEY_NAME = "ALIAN_ROUTINGKEY";
}

Employee.java

package com.alian.common.dto;

import java.io.Serializable;
import java.time.LocalDate;
import java.util.Objects;

public class Employee implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * Employee number
     */
    private String id = "";

    /**
     * Employee name
     */
    private String name = "";

    /**
     * Employee age
     */
    private int age;

    /**
     * wages
     */
    private double salary = 0.00;

    /**
     * department
     */
    private String department = "";

    /**
     * Entry time
     */
    private LocalDate hireDate = LocalDate.of(1970, 1, 1);

    /**
     * Note: the serialized object should provide a parameterless constructor, otherwise an exception will be thrown
     */
    public Employee() {

    }

    public String getId() {
        return id;
    }

    public void setId(String id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }

    public String getDepartment() {
        return department;
    }

    public void setDepartment(String department) {
        this.department = department;
    }

    public LocalDate getHireDate() {
        return hireDate;
    }

    public void setHireDate(LocalDate hireDate) {
        this.hireDate = hireDate;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id='" + id + '\'' +
                ", name='" + name + '\'' +
                ", age=" + age +
                ", salary=" + salary +
                ", department='" + department + '\'' +
                ", hireDate=" + hireDate +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof Employee)) return false;
        Employee employee = (Employee) o;
        return getAge() == employee.getAge() &&
                Double.compare(employee.getSalary(), getSalary()) == 0 &&
                Objects.equals(getId(), employee.getId()) &&
                Objects.equals(getName(), employee.getName()) &&
                Objects.equals(getDepartment(), employee.getDepartment()) &&
                Objects.equals(getHireDate(), employee.getHireDate());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getId(), getName(), getAge(), getSalary(), getDepartment(), getHireDate());
    }
}

PayRecord.java

package com.alian.common.dto;

import java.io.Serializable;
import java.time.LocalDateTime;
import java.util.Objects;

public class PayRecord implements Serializable {

    private static final long serialVersionUID = 1L;

    /**
     * Payment flow
     */
    private String payTranSeq = "";

    /**
     * Payment amount (unit: minute)
     */
    private int payAmount;

    /**
     * Payment method (00: cash, 01: WeChat, 02: Alipay, 03: UnionPay, 04: other)
     */
    private String payType = "01";

    /**
     * Payment status (00: payment succeeded, 01: to be paid, 02: payment failed, 03: cancelled)
     */
    private String status = "01";

    /**
     * Payment time
     */
    private LocalDateTime payTime = LocalDateTime.now();

    /**
     * Third party flow
     */
    private String payNo = "";

    /**
     * Note: the serialized object should provide a parameterless constructor, otherwise an exception will be thrown
     */
    public PayRecord() {

    }

    public String getPayTranSeq() {
        return payTranSeq;
    }

    public void setPayTranSeq(String payTranSeq) {
        this.payTranSeq = payTranSeq;
    }

    public int getPayAmount() {
        return payAmount;
    }

    public void setPayAmount(int payAmount) {
        this.payAmount = payAmount;
    }

    public String getPayType() {
        return payType;
    }

    public void setPayType(String payType) {
        this.payType = payType;
    }

    public String getStatus() {
        return status;
    }

    public void setStatus(String status) {
        this.status = status;
    }

    public LocalDateTime getPayTime() {
        return payTime;
    }

    public void setPayTime(LocalDateTime payTime) {
        this.payTime = payTime;
    }

    public String getPayNo() {
        return payNo;
    }

    public void setPayNo(String payNo) {
        this.payNo = payNo;
    }

    @Override
    public String toString() {
        return "PayRecord{" +
                "payTranSeq='" + payTranSeq + '\'' +
                ", payAmount=" + payAmount +
                ", payType='" + payType + '\'' +
                ", status='" + status + '\'' +
                ", payTime=" + payTime +
                ", payNo='" + payNo + '\'' +
                '}';
    }

    @Override
    public boolean equals(Object o) {
        if (this == o) return true;
        if (!(o instanceof PayRecord)) return false;
        PayRecord payRecord = (PayRecord) o;
        return getPayAmount() == payRecord.getPayAmount() &&
                Objects.equals(getPayTranSeq(), payRecord.getPayTranSeq()) &&
                Objects.equals(getPayType(), payRecord.getPayType()) &&
                Objects.equals(getStatus(), payRecord.getStatus()) &&
                Objects.equals(getPayTime(), payRecord.getPayTime()) &&
                Objects.equals(getPayNo(), payRecord.getPayNo());
    }

    @Override
    public int hashCode() {
        return Objects.hash(getPayTranSeq(), getPayAmount(), getPayType(), getStatus(), getPayTime(), getPayNo());
    }
}

3.2 switch, route and queue configuration

  specific explanation, I believe the code makes it very clear. com.alian.common.constant.MQConstants is in my public package. The specific code is in mqconstants java

ExchangeConfig.java

package com.alian.rabbitmq.config;

import com.alian.common.constant.MQConstants;
import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class ExchangeConfig {

    /**
     * Define switch
     *
     * @return
     */
    @Bean
    public DirectExchange defaultExchange() {
        return new DirectExchange(MQConstants.ALIAN_EXCHANGE_NAME);
    }

    /**
     * Define a queue (persistence)
     *
     * @return
     */
    @Bean
    public Queue aLianQueue() {
        return new Queue(MQConstants.ALIAN_QUEUE_NAME, true);
    }

    /**
     * Bind queue, and send messages to the specified queue through the specified switch and routing key (a queue can bind multiple routing keys)
     *
     * @return
     */
    @Bean
    public Binding aLianBinding() {
        return BindingBuilder.bind(aLianQueue()).to(defaultExchange()).with(MQConstants.ALIAN_ROUTINGKEY_NAME);
    }

}

As for the configuration of switches, routes and queues, I have a few suggestions that you can refer to (they have been miserable by those who don't pay attention to these, and an MQ has made a mess).

  • It is better to define the switch, route and queue through the program rather than manual operation on the console. The console is mainly used to check the relationship between the three, or to view the operation of connection and queue messages
  • For the naming of switches, it is recommended that one switch be used for one platform, for example, 10 systems on one platform share one switch
  • For the naming of the route, it is best to associate it with the system to let everyone know which system the route goes to
  • For the naming of queues, it is best to know which system or function is used, and to realize queue persistence, that is, when defining queues, durable is true
  • The names of switches, routes and queues are put into a public jar, so that all systems can be easily consulted, named and managed in a unified manner, so as to avoid mq abuse, duplication and relationship confusion

  I suppose I have a payment platform here, which has query system, notification system, business request system, refund system, etc. both query system and notification system use dead letter queue for corresponding query or notification functions. The approximate name is as follows:

Queue typeSwitchroutequeue
Notification system general queuePT_EXCHANGENTS_ROUTINGKEYNTS_QUEUE
Query system general queuePT_EXCHANGEOIS_ROUTINGKEYOIS_QUEUE
Notification system dead letter queuePT_DELAY_EXCHANGENTS_DELAY_ROUTINGKEYNTS_DELAY_QUEUE
Query system dead letter queuePT_DELAY_EXCHANGEOIS_DELAY_ROUTINGKEYOIS_DELAY_QUEUE

3.3 RabbitMq configuration (Jackson2JsonMessageConverter serialized object)

  note: com.com in this article fasterxml. Jackson uses a dependent version number of 2.9 10. Do not use the latest version 2.12 4. It is better to use the latest version of Spring Boot and redis (currently 2.5.2), and keep the two versions consistent to avoid incompatibility.
   about this configuration class: the reply mode is the manual confirmation mode. If the message cannot be matched to the queue through the exchange, it will be returned to the producer. Message serialization is supported. The code has comments, which you can study carefully.

SimpleRabbitMqConfig.java

package com.alian.rabbitmq.config;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.fasterxml.jackson.datatype.jsr310.JavaTimeModule;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalTimeDeserializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
import com.fasterxml.jackson.datatype.jsr310.ser.LocalTimeSerializer;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.config.SimpleRabbitListenerContainerFactory;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.amqp.support.converter.Jackson2JsonMessageConverter;
import org.springframework.amqp.support.converter.MessageConverter;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.format.DateTimeFormatter;

@Configuration
public class SimpleRabbitMqConfig {

    /**
     * SimpleMessageListenerContainer
     *
     * @param connectionFactory
     * @return
     */
    @Bean(name = "simpleContainerFactory")
    public SimpleRabbitListenerContainerFactory simpleRabbitListenerContainerFactory(ConnectionFactory connectionFactory) {
        SimpleRabbitListenerContainerFactory simpleContainerFactory = new SimpleRabbitListenerContainerFactory();
        //Set up connection factory
        simpleContainerFactory.setConnectionFactory(connectionFactory);
        //The received message is serialized by Jackson2JsonMessageConverter
        simpleContainerFactory.setMessageConverter(this.jackson2JsonMessageConverter());
        //Set the initial number of consumers (the configuration priority of the SimpleRabbitListenerContainerFactory configuration class is higher than the configuration file)
        simpleContainerFactory.setConcurrentConsumers(2);
        //Set the maximum number of consumers (the configuration priority of the SimpleRabbitListenerContainerFactory configuration class is higher than the configuration file)
        simpleContainerFactory.setMaxConcurrentConsumers(10);
        //Set the number of messages that consumers get each time. The default is 250 (the configuration priority of simplerabbitlistenercontainerfactory configuration class is higher than that of the configuration file)
        simpleContainerFactory.setPrefetchCount(30);
        //The consumer listener throws an exception, whether to return to the queue. The default is true: return to the queue, and false means not to return to the queue (combined with dead letter switch)
        simpleContainerFactory.setDefaultRequeueRejected(false);
        //Response mode NONE: unconfirmed mode, MANUAL: MANUAL confirmation mode, AUTO: automatic confirmation mode
        simpleContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return simpleContainerFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        //Set up connection factory
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //The received message is serialized by Jackson2JsonMessageConverter (java 8 time is supported)
        rabbitTemplate.setMessageConverter(this.jackson2JsonMessageConverter());
        //When Mandatory is true, messages that cannot be matched to the queue through the exchange will be returned to the producer. When Mandatory is false, messages that cannot be matched will be directly discarded
        rabbitTemplate.setMandatory(true);
        return rabbitTemplate;
    }

    @Bean("jacksonMessageConverter")
    public MessageConverter jackson2JsonMessageConverter() {
        ObjectMapper mapper = getMapper();
        return new Jackson2JsonMessageConverter(mapper);
    }

    /**
     * Use COM fasterxml. jackson. databind. ObjectMapper
     * Data processing, including time in Java 8
     *
     * @return
     */
    private ObjectMapper getMapper() {
        ObjectMapper mapper = new ObjectMapper();
        //Set Visible 
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //Default type object
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //Set Java 8 time serialization
        JavaTimeModule timeModule = new JavaTimeModule();
        timeModule.addSerializer(LocalDateTime.class, new LocalDateTimeSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addSerializer(LocalDate.class, new LocalDateSerializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addSerializer(LocalTime.class, new LocalTimeSerializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        timeModule.addDeserializer(LocalDateTime.class, new LocalDateTimeDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")));
        timeModule.addDeserializer(LocalDate.class, new LocalDateDeserializer(DateTimeFormatter.ofPattern("yyyy-MM-dd")));
        timeModule.addDeserializer(LocalTime.class, new LocalTimeDeserializer(DateTimeFormatter.ofPattern("HH:mm:ss")));
        //Disable time to timestamp
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //No exception is thrown when an unknown attribute or attribute mismatch is encountered
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.registerModule(timeModule);
        return mapper;
    }

}

   note 1: in the mode with ack, you need to consider setDefaultRequeueRejected(false). Otherwise, when the consumer message throws an exception and does not catch, the message will be pushed back to the queue header by rabbitmq, then thrown an exception and put back... Dead loop. Setting false is used to discard the exception directly instead of putting it back. Therefore, this message may need to be processed to avoid loss.
   note 2: when Mandatory is true, messages that cannot be matched to the queue through the exchange will be returned to the producer. When it is false, messages that cannot be matched will be directly discarded
  note 3: when configuring the simple container, the configuration priority of the configuration class is higher than that of the configuration file, such as:

        SimpleRabbitListenerContainerFactory simpleContainerFactory = new SimpleRabbitListenerContainerFactory();
        //Set up connection factory
        simpleContainerFactory.setConnectionFactory(connectionFactory);
        //The received message is serialized by Jackson2JsonMessageConverter
        simpleContainerFactory.setMessageConverter(this.jackson2JsonMessageConverter());
        //Set the initial number of consumers (the configuration priority of the SimpleRabbitListenerContainerFactory configuration class is higher than the configuration file)
        simpleContainerFactory.setConcurrentConsumers(2);
        //Set the maximum number of consumers (the configuration priority of the SimpleRabbitListenerContainerFactory configuration class is higher than the configuration file)
        simpleContainerFactory.setMaxConcurrentConsumers(10);
        //Set the number of messages that consumers get each time. The default is 250 (the configuration priority of simplerabbitlistenercontainerfactory configuration class is higher than that of the configuration file)
        simpleContainerFactory.setPrefetchCount(30);
        //Response mode NONE: unconfirmed mode, MANUAL: MANUAL confirmation mode, AUTO: automatic confirmation mode
        simpleContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);

The priority of is higher than that configured below

#Minimum number of consumers
spring.rabbitmq.listener.simple.concurrency=3
#Maximum number of consumers
spring.rabbitmq.listener.simple.max-concurrency=10
#Number of messages per fetch
spring.rabbitmq.listener.simple.prefetch=50
#Message confirmation mode: manual/auto/none
spring.rabbitmq.listener.simple.acknowledge-mode=manual

4, Consumer (key points)

   here I write a consumer that can support the transfer of various objects, such as string, json, HashMap, byte array and user-defined entity objects.
In practice, we have to unify the message transmission mode. If it is a distributed system, it is recommended to adopt the object mode, because different systems may be developed by different people, so that the message content is easy to read and more in line with the java object-oriented development mode.

ConsumeService.java

package com.alian.rabbitmq.service;

import com.alian.common.constant.MQConstants;
import com.alian.common.dto.Employee;
import com.alian.common.dto.PayRecord;
import com.alibaba.fastjson.JSONObject;
import com.rabbitmq.client.Channel;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.core.Message;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Service;
import java.util.HashMap;

@Slf4j
@Service
@RabbitListener(queues = MQConstants.ALIAN_QUEUE_NAME, containerFactory = "simpleContainerFactory")
public class ConsumeService {

    /**
     * Note: com alian. common. dto. Employee
     */
    @RabbitHandler
    public void processEmployee(Employee employee, Channel channel, Message message) throws Exception {
        log.info("----------Start processing Employee----------");
        log.info("Received Employee information: {}", employee);
        log.info("----------Employee Processing complete----------");
        //In case of manual response mode: acknowledge mode Manual needs to be called
        //deliveryTag: the index of the message; Multiple: batch true: all messages less than deliveryTag will be ack ed at one time.
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    /**
     * Note: com alian. common. dto. PayRecord
     */
    @RabbitHandler
    public void processPayRecord(PayRecord payRecord, Channel channel, Message message) throws Exception {
        log.info("----------Start processing PayRecord----------");
        log.info("channel={}", channel);
        log.info("message={}", message);
        log.info("Received PayRecord information: {}", payRecord);
        log.info("----------PayRecord Processing complete----------");
        //In case of manual response mode: acknowledge mode Manual needs to be called
        //deliveryTag: the index of the message; Multiple: batch true: all messages less than deliveryTag will be ack ed at one time.
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processStr(String str, Channel channel, Message message) throws Exception {
        log.info("----------Start processing String----------");
        log.info("Received string information: {}", str);
        log.info("----------String Processing complete----------");
        //deliveryTag: the index of the message; Multiple: batch true: all messages less than deliveryTag will be ack ed at one time.
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processJson(JSONObject json, Channel channel, Message message) throws Exception{
        log.info("----------Start processing JSONObject----------");
        log.info("Received JSONObject information: {}", json);
        log.info("----------JSONObject Processing complete----------");
        //deliveryTag: the index of the message; Multiple: batch true: all messages less than deliveryTag will be ack ed at one time.
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processHashMap(HashMap hashMap, Channel channel, Message message) throws Exception{//Can receive messages
        log.info("----------Start processing HashMap----------");
        log.info("Received HashMap information: {}", hashMap);
        log.info("----------HashMap Processing complete----------");
        //deliveryTag: the index of the message; Multiple: batch true: all messages less than deliveryTag will be ack ed at one time.
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processByte(byte[] bytes, Channel channel, Message message) throws Exception{
        log.info("----------Start processing byte[]----------");
        log.info("Received byte[]information: {}", new String(bytes));
        log.info("----------byte[]Processing complete----------");
        //deliveryTag: the index of the message; Multiple: batch true: all messages less than deliveryTag will be ack ed at one time.
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

}

  note:
  many people create a wrapper class locally, then copy this class to another system, and send messages, You may never get it (the path of the package may change); in addition, the project sends messages within the project. The project receives messages normally and thinks that it is OK to cross the system. It is not aware of the seriousness of the problem. After the message is serialized, it should be noted that the type of deserialization should have the same name as the package. Otherwise, it will throw an exception that can not be found. This is why I want to send a package to the private server and put it forward to you This is a key issue.

5, Configuration file

  I configure here according to the identity of consumers. I won't configure too much about producers. It's unnecessary. Because the configuration of the factory class SimpleRabbitListenerContainerFactory in the simple container has higher priority than the configuration file, and I want to serialize it with jackson2JsonMessageConverter. All of them are configured in my configuration class.

application.properties

#Project name and port
server.port=8888
server.servlet.context-path=/simpleRabbitmq
#RabbitMQ configuration
#address
spring.rabbitmq.addresses=192.168.0.194
#port
spring.rabbitmq.port=5672
#user name
spring.rabbitmq.username=test
#password
spring.rabbitmq.password=test
#The virtual host to use when connecting to the agent
spring.rabbitmq.virtual-host=/

#Container type (simple, direct)
spring.rabbitmq.listener.type=simple

Running results after starting the project:

2021-07-30 16:25:37 718 [main] INFO logStarting 55:Starting RabbitmqApplication using Java 1.8.0_111 on DESKTOP-EIGL04G with PID 7140 (C:\workspace\study\RabbitMq-simple\target\classes started by admin in C:\workspace\study\RabbitMq-simple)
2021-07-30 16:25:37 720 [main] INFO logStartupProfileInfo 659:No active profile set, falling back to default profiles: default
2021-07-30 16:25:38 375 [main] INFO initialize 108:Tomcat initialized with port(s): 8888 (http)
2021-07-30 16:25:38 382 [main] INFO log 173:Initializing ProtocolHandler ["http-nio-8888"]
2021-07-30 16:25:38 382 [main] INFO log 173:Starting service [Tomcat]
2021-07-30 16:25:38 382 [main] INFO log 173:Starting Servlet engine: [Apache Tomcat/9.0.48]
2021-07-30 16:25:38 433 [main] INFO log 173:Initializing Spring embedded WebApplicationContext
2021-07-30 16:25:38 433 [main] INFO prepareWebApplicationContext 290:Root WebApplicationContext: initialization completed in 683 ms
2021-07-30 16:25:39 053 [main] INFO log 173:Starting ProtocolHandler ["http-nio-8888"]
2021-07-30 16:25:39 064 [main] INFO start 220:Tomcat started on port(s): 8888 (http) with context path '/simpleRabbitmq'
2021-07-30 16:25:39 066 [main] INFO connectAddresses 638:Attempting to connect to: [192.168.0.194:5672]
2021-07-30 16:25:39 129 [main] INFO createBareConnection 589:Created new connection: rabbitConnectionFactory#18137eab:0/SimpleConnection@562919fe [delegate=amqp://test@192.168.0.194:5672/, localPort= 53315]
2021-07-30 16:25:39 208 [main] INFO logStarted 61:Started RabbitmqApplication in 1.776 seconds (JVM running for 2.487)

The following information can be viewed through the RabbitMQ client:

Unfinished to be continued

  now that the consumer has finished writing, the next step is to write about the producer. Pay attention to my next article:
Link: RabbitMQ notes (2) spring boot integrates RabbitMQ's simple container (producer).

Topics: Java RabbitMQ Spring Boot