我们按照 RabbitMQ 的官网学习地址:https://www.rabbitmq.com/getstarted.html
点对点模式(简单队列)
“P”是我们的生产者,“C”是我们的消费者。中间的框是一个队列-RabbitMQ代表使用者保留的消息缓冲区。
一个生产者投递消息给队列,只允许有一个消费者进行消费。
消费者应答的方式有2种:自动应答和手动应答。
自动应答:消费者不管对消息处理是否成功,都会通知队列服务器去删除这条消息。如果消息处理失败,会自动补偿。
手动应答:消费者处理完消息,会返回通知(ACK)告诉队列服务器是否删除该消息。
废话少说,直接上代码!创建一个 SpringBoot 项目:RabbitMQ,然后创建P2P模块,如截图:
最外层的 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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.study</groupId>
<artifactId>RabbitMQStudy</artifactId>
<packaging>pom</packaging>
<version>1.0-SNAPSHOT</version>
<modules>
<module>Rabbit_P2P</module>
<module>Rabbit_Work</module>
<module>Rabbit_Publish</module>
<module>Rabbit_Routing</module>
<module>Rabbit_Topic</module>
</modules>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.0.8.RELEASE</version>
</parent>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<!--Spring boot 集成包-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!-- 阿里巴巴 fastjson -->
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.75</version>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>Finchley.SR4</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
</project>
点对点模块的代码结构如图:
bootstrap.yml 配置如下:
spring:
rabbitmq:
#主机名
host: 127.0.0.1
#端口号
port: 5672
#账号
username: guest
#密码
password: guest
#虚拟主机,这里配置的是我们的测试主机
virtual-host: /test_host
# 队列名
queueConfig:
name: P2PQueueName
server:
port: 8080
# 将SpringBoot项目作为单实例部署调试时,不需要注册到注册中心
eureka:
client:
fetch-registry: false
register-with-eureka: false
配置类 QueueConfig:
package com.study.config;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.DefaultConsumer;
import org.springframework.amqp.AmqpException;
import org.springframework.amqp.core.AcknowledgeMode;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.connection.Connection;
import org.springframework.amqp.rabbit.connection.ConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Scope;
import java.io.IOException;
/**
* @author biandan
* @description
* @signature 让天下没有难写的代码
* @create 2021-04-05 上午 12:39
*/
@Configuration
public class QueueConfig {
@Value("${spring.rabbitmq.host}")
private String host;
@Value("${spring.rabbitmq.port}")
private Integer port;
@Value("${spring.rabbitmq.username}")
private String username;
@Value("${spring.rabbitmq.password}")
private String password;
@Value("${spring.rabbitmq.virtual-host}")
private String virtualHost;
//获取配置信息的队列名
@Value("${queueConfig.name}")
private String queueName;
/**
* 封装连接类
*
* @return
*/
@Bean
public CachingConnectionFactory connectionFactory() {
CachingConnectionFactory connectionFactory = new CachingConnectionFactory();
connectionFactory.setHost(host);
connectionFactory.setPort(port);
connectionFactory.setUsername(username);
connectionFactory.setPassword(password);
connectionFactory.setVirtualHost(virtualHost);
return connectionFactory;
}
/**
* 动态的创建队列(这里仅创建配置文件里的一个)
*
* @return
* @throws Exception
*/
@Bean
public String getQueueName() throws Exception {
//获取连接
Connection connection = connectionFactory().createConnection();
//创建通道。true表示有事务功能
Channel channel = connection.createChannel(true);
/*
创建队列声明,参数说明:
1.队列名queue
2.是否持久化durable。是否持久化, 队列的声明默认是存放到内存中的,
如果rabbitmq重启会丢失,如果想重启之后还存在就要使队列持久化,
保存到Erlang自带的Mnesia数据库中,当rabbitmq重启之后会读取该数据库
3.是否排外exclusive。有两个作用,一:当连接关闭时connection.close()该队列是否会自动删除;二:该队列是否是私有的private,
如果不是排外的,可以使用两个消费者都访问同一个队列,没有任何问题,
如果是排外的,会对当前队列加锁,其他通道channel是不能访问的,如果强制访问会报异常。
一般等于true的话用于一个队列只能有一个消费者来消费的场景
4.是否自动删除autoDelete。当最后一个消费者断开连接之后队列是否自动被删除,
可以通过RabbitMQ Management,查看某个队列的消费者数量,当consumers = 0时队列就会自动删除
5.其它参数 Map<String, Object> arguments
*/
channel.queueDeclare(queueName, false, false, false, null);
//关闭通道
channel.close();
//关闭连接
connection.close();
return queueName;
}
}
消息生产者代码如下 P2PProducer:
package com.study.producer;
import org.springframework.amqp.core.AmqpTemplate;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import java.text.SimpleDateFormat;
import java.util.Date;
/**
* @author biandan
* @description 消息生产者
* @signature 让天下没有难写的代码
* @create 2021-04-04 下午 10:49
*/
@Component
public class P2PProducer {
private SimpleDateFormat SDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
/**
* 队列名称
*/
@Value("${queueConfig.name}")
private String queueName;
/**
* 注入消息模板
*/
@Autowired
private RabbitTemplate template;
/**
* 每隔3秒产生一条消息
*/
@Scheduled(fixedRate = 1000 * 3)
public void sendMsg(){
String msg = "P2P消息生产者:"+SDF.format(new Date());
System.out.println(msg);
//发送消息
template.convertAndSend(queueName,msg);
}
}
启动类代码如下:
package com.study;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.scheduling.annotation.EnableScheduling;
@SpringBootApplication
@EnableScheduling //启用任务调度
@EnableEurekaClient
public class RabbitMQP2PApplication {
public static void main(String[] args) {
SpringApplication.run(RabbitMQP2PApplication.class, args);
}
}
启动项目,看到控制台每隔3秒输出一条信息:
然后进入 RabbitMQ 管理后台:http://127.0.0.1:15672/ 在 Queues 菜单下,看到有 P2PQueueName 队列名,同时有消息生成了。
接下来,我们编写消费者。
消费者代码如下 Consumer (队列名可以动态的获取到配置文件里的):
package com.study.consumer;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
/**
* @author biandan
* @description 消费者
* @signature 让天下没有难写的代码
* @create 2021-04-04 下午 11:39
*/
@Component
@RabbitListener(queues = "${queueConfig.name}")
public class Consumer {
@RabbitHandler
public void receiveMsg(String msg){
System.out.println("消费者消费掉的消息:"+msg);
}
}
OK,再次启动项目,看到控制台输出:
说明:红色框出部分,表示消费者监听到队列有消息,直接消费掉。
蓝色部分,表示生产者一旦生产消息,就被消费者监听到,直接消费掉了。
我们再看 RabbitMQ 管理台,已经没有待消费的消息了:
提取码:xnjd