RabbitMQ笔记(一)SpringBoot整合RabbitMQ之simple容器(消费者)

目录

    • 一、简介
      • 1.1 本文中注解说明
    • 二、Maven依赖
    • 三、配置类
      • 3.1 基础配置
      • 3.2 交换机、路由、队列配置
      • 3.3 RabbitMq配置(Jackson2JsonMessageConverter序列化对象)
    • 四、消费者(注意要点要看
    • 五、配置文件
    • 未完待续

一、简介

  消息中间件具有一系列功能如低耦合、可靠投递、广播、流量控制、最终一致性等,成为异步RPC的主要手段之一,常见的ActiveMQ、RabbitMQ、Kafka、RocketMQ等。消息中间件主要作用如下:

  • 异步处理
  • 应用解耦
  • 流量削峰
  • 日志处理

  RabbitMQ默认容器是simple容器,从2.0版本之后就多了一个容器direct容器,我们从分布式架构的角度一起来看看到底有什么不同吧。本文主要用使用Spring Boot(2.5.2)来整合RabbitMQ(2.5.2),使用simple容器实现一个消费者。本文的前提是有一个安装好的RabbitMQ的环境。

1.1 本文中注解说明

  • @Configuration:用于定义配置类,被注解的类可以包含有一个或多个被@Bean注解的方法,这些方法将会被AnnotationConfigApplicationContextAnnotationConfigWebApplicationContext类进行扫描,并用于构建bean定义,初始化Spring容器
  • @RabbitListener:@RabbitListener注解指定目标方法来作为消费消息的方法,通过注解参数指定所监听的队列或者Binding。 @RabbitListener标注在类上面表示当有收到消息的时候,就交给 @RabbitHandler 的方法处理
  • @RabbitHandler: 需配合 @RabbitListener注解一起使用,当收到消息后,根据 MessageConverter 转换后的参数类型调用相关的方法

二、Maven依赖

pom.xml


<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.0modelVersion>
    <parent>
        <groupId>org.springframework.bootgroupId>
        <artifactId>spring-boot-starter-parentartifactId>
        <version>2.5.2version>
        <relativePath/>
    parent>

    <groupId>com.aliangroupId>
    <artifactId>rabbitmq-simpleartifactId>
    <version>0.0.1-SNAPSHOTversion>
    <name>rabbitmq-simplename>
    <description>SpringBoot整合RabbitMQ之simple容器(消费者)description>

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

    <dependencies>

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

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

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

        <dependency>
            <groupId>com.alibabagroupId>
            <artifactId>fastjsonartifactId>
            <version>1.2.68version>
        dependency>

        
        <dependency>
            <groupId>com.fasterxml.jackson.coregroupId>
            <artifactId>jackson-databindartifactId>
            <version>2.9.10version>
        dependency>

        
        <dependency>
            <groupId>com.fasterxml.jackson.datatypegroupId>
            <artifactId>jackson-datatype-jsr310artifactId>
            <version>2.9.10version>
        dependency>

        
        <dependency>
            <groupId>com.aliangroupId>
            <artifactId>commonartifactId>
            <version>0.0.1-SNAPSHOTversion>
        dependency>

        <dependency>
            <groupId>org.projectlombokgroupId>
            <artifactId>lombokartifactId>
            <version>1.18.16version>
        dependency>

    dependencies>

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

  这里需要注意的是下面这个包,是我本人打包到私服的,其实一个员工类,支付类,加上一个常量类,大家也自己打包自己的实体到私服,或者直接通过模块开发的方式实现我这个实例。

<dependency>
    <groupId>com.aliangroupId>
    <artifactId>commonartifactId>
    <version>0.0.1-SNAPSHOTversion>
dependency>

三、配置类

3.1 基础配置

  本文开始说了,会从分布式架构的角度来完成本次整合。我们会用两个系统消费者和生产者来完成本次的测试,采用这种麻烦的方式,不是不会单元测试或者模块开发,而是为了避免大家踩坑,这样也比较贴近我们实际开发。所以我提前准备了几个简单的类(MQConstants.javaEmployee.javaPayRecord.java)打成一个jar包到maven私服,一个配置类存放MQ常量,一个员工类,一个支付类,里面的属性都是常用类型,具体如下。

MQConstants .java

package com.alian.common.constant;

public class MQConstants {
     

    /**
     * 交换机
     */
    public final static String ALIAN_EXCHANGE_NAME = "ALIAN_EXCHANGE";

    /**
     * 队列名
     */
    public final static String ALIAN_QUEUE_NAME = "ALIAN_QUEUE";

    /**
     * 路由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;

    /**
     * 员工编号
     */
    private String id = "";

    /**
     * 员工姓名
     */
    private String name = "";

    /**
     * 员工年龄
     */
    private int age;

    /**
     * 工资
     */
    private double salary = 0.00;

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

    /**
     * 入职时间
     */
    private LocalDate hireDate = LocalDate.of(1970, 1, 1);

    /**
     * 注意:被序列化对象应提供一个无参的构造函数,否则会抛出异常
     */
    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;

    /**
     * 支付流水
     */
    private String payTranSeq = "";

    /**
     * 支付金额(单位分)
     */
    private int payAmount;

    /**
     * 支付方式(00:现金,01:微信,02:支付宝,03:银联,04:其他)
     */
    private String payType = "01";

    /**
     * 支付状态(00:支付成功,01:待支付,02:支付失败,03:已取消)
     */
    private String status = "01";

    /**
     * 支付时间
     */
    private LocalDateTime payTime = LocalDateTime.now();

    /**
     * 第三方流水
     */
    private String payNo = "";

    /**
     * 注意:被序列化对象应提供一个无参的构造函数,否则会抛出异常
     */
    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 交换机、路由、队列配置

  具体的解释,我相信代码里说得很清楚了。com.alian.common.constant.MQConstants是我公共包里的,具体的代码在上面的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 {
     

    /**
     * 定义交换机(持久化)
     *
     * name:交换机的名称
     * durable:设置是否持久化。持久化可以将交换机存盘,在RabbitMQ服务重启的时候不会丢失相关信息
     * autoDelete:在所在消费者都解除订阅的情况下自动删除
     */
    @Bean
    public DirectExchange defaultExchange() {
     
        return new DirectExchange(MQConstants.ALIAN_EXCHANGE_NAME, true, false);
    }

    /**
     * 定义一个队列(持久化)
     *
     * name:队列的名称
     * durable:设置是否持久化。持久化的队列会存盘,在RabbitMQ服务重启的时候可以保证不丢失相关信息
     *
     * @return
     */
    @Bean
    public Queue aLianQueue() {
     
        return new Queue(MQConstants.ALIAN_QUEUE_NAME, true);
    }

    /**
     * 绑定队列,通过指定交换机和路由key把消息发送到指定的队列(一个队列可以绑定多个路由key)
     *
     * @return
     */
    @Bean
    public Binding aLianBinding() {
     
        return BindingBuilder.bind(aLianQueue()).to(defaultExchange()).with(MQConstants.ALIAN_ROUTINGKEY_NAME);
    }

}

关于交换机、路由、队列的配置,我有几点建议,大家可以参考(曾经也被那些不注重这些的人坑惨了,一个MQ搞得乱七八糟的)。

  • 交换机、路由、队列的定义最好通过程序,而不是去控制台进行手动操作,控制台主要用于检查三者间的关系,或者查看连接及队列消息运行情况
  • 关于交换机的命名,建议一个平台用一个,比如一个平台10个系统,都共用一个交换机
  • 关于路由的命名,最好是关联到系统,让大家知道这个路由是去哪个系统
  • 关于队列的命名,最好是知道哪个系统或者功能使用,并且要实现队列持久化,也就是定义队列时durable为true
  • 关于交换机、路由、队列的名字放到一个公共的jar里,让所有的系统轻松查阅,统一命名规范,统一管理,避免mq的滥用、重复、关系混乱等

  我假设我这里有一个支付平台,里面有查询系统,通知系统,业务请求系统,退款系统等等,查询系统和通知系统都用到死信队列进行相应的查询或者通知功能。大致的命名如下:

队列类型 交换机 路由 队列
通知系统普通队列 PT_EXCHANGE NTS_ROUTINGKEY NTS_QUEUE
查询系统普通队列 PT_EXCHANGE OIS_ROUTINGKEY OIS_QUEUE
通知系统死信队列 PT_DELAY_EXCHANGE NTS_DELAY_ROUTINGKEY NTS_DELAY_QUEUE
查询系统死信队列 PT_DELAY_EXCHANGE OIS_DELAY_ROUTINGKEY OIS_DELAY_QUEUE

3.3 RabbitMq配置(Jackson2JsonMessageConverter序列化对象)

  注意:本文中的com.fasterxml.jackson使用的依赖的版本号为2.9.10,不要使用到最新的版本2.12.4。Spring BootRedis的redis的版本最好使用最新版(当前最新为2.5.2),并且保持两者版本一致,避免不兼容的情况。
  关于本配置类:应答模式是手动确认模式,消息通过交换器无法匹配到队列会返回给生产者,支持消息序列化。代码都有注释,大家可以仔细研读。

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();
        //设置连接工厂
        simpleContainerFactory.setConnectionFactory(connectionFactory);
        //接收消息采用Jackson2JsonMessageConverter序列化
        simpleContainerFactory.setMessageConverter(this.jackson2JsonMessageConverter());
        //设置初始消费者数量(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
        simpleContainerFactory.setConcurrentConsumers(2);
        //设置最大消费者数量(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
        simpleContainerFactory.setMaxConcurrentConsumers(10);
        //设置消费者每次获取的消息数,默认250(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
        simpleContainerFactory.setPrefetchCount(30);
        //消费者listener抛出异常,是否重回队列,默认true:重回队列, false为不重回队列(结合死信交换机)
        simpleContainerFactory.setDefaultRequeueRejected(false);
        //应答模式NONE:不确认模式,MANUAL:手动确认模式,AUTO:自动确认模式
        simpleContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);
        return simpleContainerFactory;
    }

    @Bean
    public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
     
        RabbitTemplate rabbitTemplate = new RabbitTemplate();
        //设置连接工厂
        rabbitTemplate.setConnectionFactory(connectionFactory);
        //接收消息采用Jackson2JsonMessageConverter序列化(支持java 8时间)
        rabbitTemplate.setMessageConverter(this.jackson2JsonMessageConverter());
        //Mandatory为true时,消息通过交换器无法匹配到队列会返回给生产者,为false时匹配不到会直接被丢弃
        rabbitTemplate.setMandatory(true);
        return rabbitTemplate;
    }

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

    /**
     * 使用com.fasterxml.jackson.databind.ObjectMapper
     * 对数据进行处理包括java8里的时间
     *
     * @return
     */
    private ObjectMapper getMapper() {
     
        ObjectMapper mapper = new ObjectMapper();
        //设置可见性
        mapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        //默认键入对象
        mapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        //设置Java 8 时间序列化
        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")));
        //禁用把时间转为时间戳
        mapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        //遇到未知属性或者属性不匹配的时候不抛出异常
        mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        mapper.registerModule(timeModule);
        return mapper;
    }

}

  注意1:有ack的模式下,需要考虑setDefaultRequeueRejected(false),否则当消费消息抛出异常没有catch住时,这条消息会被rabbitmq放回到queue头部,再被推送过来,然后再抛异常再放回…死循环了。设置false的作用是抛异常时不放回,而是直接丢弃,所以可能需要对这条消息做处理,以免丢失。
  注意2:Mandatory为true时,消息通过交换器无法匹配到队列会返回给生产者,为false时匹配不到会直接被丢弃
  注意3:simple容器配置时,配置类的配置优先级比配置文件配置高如:

        SimpleRabbitListenerContainerFactory simpleContainerFactory = new SimpleRabbitListenerContainerFactory();
        //设置连接工厂
        simpleContainerFactory.setConnectionFactory(connectionFactory);
        //接收消息采用Jackson2JsonMessageConverter序列化
        simpleContainerFactory.setMessageConverter(this.jackson2JsonMessageConverter());
        //设置初始消费者数量(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
        simpleContainerFactory.setConcurrentConsumers(2);
        //设置最大消费者数量(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
        simpleContainerFactory.setMaxConcurrentConsumers(10);
        //设置消费者每次获取的消息数,默认250(SimpleRabbitListenerContainerFactory配置类的配置优先级比配置文件高)
        simpleContainerFactory.setPrefetchCount(30);
        //应答模式NONE:不确认模式,MANUAL:手动确认模式,AUTO:自动确认模式
        simpleContainerFactory.setAcknowledgeMode(AcknowledgeMode.MANUAL);

的优先级要比下面配置的优先级要要

#消费者的最小数量
spring.rabbitmq.listener.simple.concurrency=3
#消费者的最大数量
spring.rabbitmq.listener.simple.max-concurrency=10
#每次获取消息的数量
spring.rabbitmq.listener.simple.prefetch=50
#消息确认模式:manual/auto/none
spring.rabbitmq.listener.simple.acknowledge-mode=manual

四、消费者(注意要点要看

  这里我写了一个消费者,可以支持传递各种对象,比如,字符串、json,HashMap,字节数组,自定义实体对象。
实际工作中我们得统一消息的传递方式,如果是分布式系统,建议采用对象模式,因为不同的系统可能是不同的人开发,这样对消息的内容容易阅读,也更符合java面向对象开发的方式。

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 {
     

    /**
     * 注意是:com.alian.common.dto.Employee
     */
    @RabbitHandler
    public void processEmployee(Employee employee, Channel channel, Message message) throws Exception {
     
        log.info("----------开始处理Employee----------");
        log.info("接收到的Employee信息: {}", employee);
        log.info("----------Employee处理完成----------");
        //如果是手动应答模式:AcknowledgeMode.MANUAL 则需要调用
        //deliveryTag:该消息的index; multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    /**
     * 注意是:com.alian.common.dto.PayRecord
     */
    @RabbitHandler
    public void processPayRecord(PayRecord payRecord, Channel channel, Message message) throws Exception {
     
        log.info("----------开始处理PayRecord----------");
        log.info("channel={}", channel);
        log.info("message={}", message);
        log.info("接收到的PayRecord信息: {}", payRecord);
        log.info("----------PayRecord处理完成----------");
        //如果是手动应答模式:AcknowledgeMode.MANUAL 则需要调用
        //deliveryTag:该消息的index; multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processStr(String str, Channel channel, Message message) throws Exception {
     
        log.info("----------开始处理String----------");
        log.info("接收到的字符串信息: {}", str);
        log.info("----------String处理完成----------");
        //deliveryTag:该消息的index; multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processJson(JSONObject json, Channel channel, Message message) throws Exception{
     
        log.info("----------开始处理JSONObject----------");
        log.info("接收到的JSONObject信息: {}", json);
        log.info("----------JSONObject处理完成----------");
        //deliveryTag:该消息的index; multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processHashMap(HashMap hashMap, Channel channel, Message message) throws Exception{
     //可以接收Message
        log.info("----------开始处理HashMap----------");
        log.info("接收到的HashMap信息: {}", hashMap);
        log.info("----------HashMap处理完成----------");
        //deliveryTag:该消息的index; multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

    @RabbitHandler
    public void processByte(byte[] bytes, Channel channel, Message message) throws Exception{
     
        log.info("----------开始处理byte[]----------");
        log.info("接收到的byte[]信息: {}", new String(bytes));
        log.info("----------byte[]处理完成----------");
        //deliveryTag:该消息的index; multiple:是否批量.true:将一次性ack所有小于deliveryTag的消息。
        channel.basicAck(message.getMessageProperties().getDeliveryTag(), false);
    }

}

  注意:
  很多人就在本地建一个包装类,然后拷贝这个类到另外一个系统,发送消息后,可能总是收不到(包的路径可能就变了);还有就是本项目内进行发送消息,本项目正常接收,以为跨系统就行了,丝毫未意识到问题的严重性。消息序列化后,需要注意的反序列化时类型应该同包同名,否者会抛出找不到类异常,这也是我为什么要打一个包到私服,给大家提出这个很关键的问题。

五、配置文件

  我这里是按照消费者身份来配置的,那些关于生产者的配置我就不会过多的配置,没有必要。因为simple容器里工厂类SimpleRabbitListenerContainerFactory 的配置,比配置文件的优先级要高,而我要使用jackson2JsonMessageConverter序列化,所有都配置在我的配置类里了。

application.properties

#项目名和端口
server.port=8888
server.servlet.context-path=/simpleRabbitmq
#RabbitMQ配置
#地址
spring.rabbitmq.addresses=192.168.0.194
#端口
spring.rabbitmq.port=5672
#用户名
spring.rabbitmq.username=test
#密码
spring.rabbitmq.password=test
#连接到代理时用的虚拟主机
spring.rabbitmq.virtual-host=/

#容器类型(simple,direct)
spring.rabbitmq.listener.type=simple

启动项目后运行结果:

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://[email protected]: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)

通过RabbitMQ客户端查看信息如下:
RabbitMQ笔记(一)SpringBoot整合RabbitMQ之simple容器(消费者)_第1张图片

未完待续

  到此消费者已经写完了,接下里要写生产者了,关注我下一篇:
链接: RabbitMQ笔记(二)SpringBoot整合RabbitMQ之simple容器(生产者).

你可能感兴趣的:(RabbitMQ笔记,rabbitmq,java,spring,boot)