消息中间件具有一系列功能如低耦合、可靠投递、广播、流量控制、最终一致性等,成为异步RPC的主要手段之一,常见的ActiveMQ、RabbitMQ、Kafka、RocketMQ等。消息中间件主要作用如下:
RabbitMQ默认容器是simple容器,从2.0版本之后就多了一个容器direct容器,我们从分布式架构的角度一起来看看到底有什么不同吧。本文主要用使用Spring Boot(2.5.2)来整合RabbitMQ(2.5.2),使用simple容器实现一个消费者。本文的前提是有一个安装好的RabbitMQ的环境。
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>
本文开始说了,会从分布式架构的角度来完成本次整合。我们会用两个系统消费者和生产者来完成本次的测试,采用这种麻烦的方式,不是不会单元测试或者模块开发,而是为了避免大家踩坑,这样也比较贴近我们实际开发。所以我提前准备了几个简单的类(MQConstants.java、Employee.java、PayRecord.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());
}
}
具体的解释,我相信代码里说得很清楚了。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搞得乱七八糟的)。
我假设我这里有一个支付平台,里面有查询系统,通知系统,业务请求系统,退款系统等等,查询系统和通知系统都用到死信队列进行相应的查询或者通知功能。大致的命名如下:
队列类型 | 交换机 | 路由 | 队列 |
---|---|---|---|
通知系统普通队列 | 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 |
注意:本文中的com.fasterxml.jackson使用的依赖的版本号为2.9.10,不要使用到最新的版本2.12.4。Spring Boot和Redis的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笔记(二)SpringBoot整合RabbitMQ之simple容器(生产者).