概述
RabbitMQ采用Erlang语言书写,不容易扩展,但是使用较为方便,属于常用消息中间件的一种。
本篇文章主要为入门所用,并没有对RabbitMQ进行详细的介绍,旨在能以最简单的代码能跑出结果,且代码都有详细注释。
基础概念
其中主要需要了解的概念如下:
Queue 队列,是RabbitMQ的内部对象,用于存储消息
Exchange 交换机,生产者将消息发送到交换机,交换机再发送给对应的队列
Binding 绑定,用于绑定交换机和对应的队列,并设置他们之间的路由键
routing key 路由键,用于指定消息的路由规则,也就是消息的传递路径
交换机的种类:
Direct Exchange
直连交换机,要求消息的路由键与绑定的路由键完全一致
Fanout Exchange
扇形交换机,路由键无效,消息会发送到所有与交换机绑定的队列上
Topic Exchange
主题交换机,会进行路由键的匹配,使用符号“#”匹配多个词,符号“ ”匹配不少于一个词,例如topic.1.2 与topic.1的区别
Header Exchange
头交换机,路由键无效,使用头属性进行匹配。
以下会有两个简单的RabbitMQ整合SpringBoot的简单例子,介绍直连与主题交换机的使用
安装Rabbitmq
这里使用Docker进行一个简单的安装
- 一台腾讯云服务器,CentOS7,已经关闭linux系统防火墙
- 云控制台防火墙已经打开15672/5672两个端口
- 安装好docker
docker拉取镜像,默认最新版
docker pull rabbitmq
查看自己的镜像
docker images
直接启动,这里没有设置密码,使用的是默认的
docker run -d -p 5672:5672 -p 15672:15672 --name rabbitmq rabbitmq:latest
因为默认是没有开启Web显示的版本,所以要手动打开
进入RabbitMQ内部
docker exec -it 43f546fbb437 /bin/bash
打开插件
rabbitmq-plugins enable rabbitmq_management
直接访问 ip:15672,默认账号与密码均为 guest
使用
1.整合SpringBoot
添加依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.配置文件
这里如果是本地,填写127.0.0.1
在浏览器上登录时的端口是15672,端口号为安装时自己设定的
用户名和密码这里都是使用默认的
#RabbitMQ的连接配置
spring:
rabbitmq:
host: 你的ip
port: 5672
username: 默认账号(guest)
password: 默认密码(guest)
virtual-host: /
server:
port: 8555
3.创建直连交换机
(1)创建配置类
这里选用的是直连交换机
配置类中需要定义队列,交换机,以设置队列和交换机互相绑定
简单用网上的一个图介绍以下的代码:
- 定义直连交换机TestDirectExchange(),以Bean注入
- 定义队列TestDirectQueue(),以Bean注入
- 将交换机和队列进行绑定,使其形成通路
- 消息在被生产出来后,指定对应的交换机发送到对应的队列中
- 消费者监听这个队列便可直接消费消息
@Configuration
public class RabbitMQConfig {
//定义队列
@Bean
public Queue TestDirectQueue(){
// durable:是否持久化,默认是false,持久化队列:会被存储在磁盘上,当消息代理重启时仍然存在,暂存队列:当前连接有效
// exclusive:默认也是false,只能被当前创建的连接使用,而且当连接关闭后队列即被删除。此参考优先级高于durable
// autoDelete:是否自动删除,当没有生产者或者消费者使用此队列,该队列会自动删除。
// return new Queue("TestDirectQueue",true,true,false);
//一般设置一下队列的持久化就好,其余两个就是默认false
return new Queue("TestDirectQueue",true);
}
/**
* 定义一个直连交换机
* 开启持久化
* 当没被使用时,自动删除
* @return 返回交换机
*/
@Bean
DirectExchange TestDirectExchange(){
return new DirectExchange("TestDirectExchange",true,false);
}
/**
* 使用路由键TestDirectRouting
* 绑定当前的 队列 TestDirectQueue() 和 直连交换机 TestDirectExchange()
* @return 返回绑定
*/
@Bean
Binding bindingDirect(){
return BindingBuilder.bind(TestDirectQueue()).to(TestDirectExchange()).with("TestDirectRouting");
}
@Bean
DirectExchange lonelyDirectExchange() {
return new DirectExchange("lonelyDirectExchange");
}
}
(2)创建生产者
这里是采用了接口的方式,将消息存储到一个map集合中
- 创建消息id,消息内容,消息日期,封装到Map集合中
- 使用rabbitTemplate指定对应的交换机和队列发送消息
@RestController
public class SendMessageController {
@Resource
RabbitTemplate rabbitTemplate;
@GetMapping("/{messageData}")
public String sendDirectMessage(@PathVariable("messageData") String msg){
//设置一个随机数的消息id
String messageId = String.valueOf(UUID.randomUUID());
//设置消息内容
// String messageData = "hello,my friends";
//设置创建时间
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
//使用map存储
Map<String,Object> map = new HashMap<>();
map.put("messageId",messageId);
map.put("messageData",msg);
map.put("createTime",createTime);
//将消息绑定路由key,发送到直连交换机TestDirectExchange
rabbitTemplate.convertAndSend("TestDirectExchange","TestDirectRouting",map);
return "OK!";
}
}
(3)创建消费者
两种方式都可以进行监听:
- 第一种是放在类上面监听,使用@RabbitHandler注解来表示该方法需要进行rabbit信息的处理
- 第二种方式是采用方法直接监听
监听时可以直接使用对应的Map集合来获取数据
//类监听,并选择具体的方法来处理监听
@Component
@RabbitListener(queues = "TestDirectQueue")
public class DirectReceiver {
@RabbitHandler
public void process(Map<String,Object> testMessage){
System.out.println("Direct直连消费者收到消息: "+testMessage.get("messageData"));
}
}
*********************************************************************************************
//直接方法监听
@Component
public class DirectReceiver {
@RabbitListener(queues = "TestDirectQueue")
public void process(Map<String,Object> testMessage){
System.out.println("Direct直连消费者收到消息: "+testMessage.get("messageData"));
}
}
4.创建topic交换机
(1)配置文件
topic交换机会进行绑定建的匹配,只有当消息的路由规则和队列绑定的路由规则相符合时,消息才会被发送到对应的队列中
- 建立两个队列topic.man与topic.woman
- 队列与交换机进行绑定
- 绑定时会按照路由键进行适配,topic.#表示两个队列都可以收到消息
@Configuration
public class TopicRabbitConfig {
//设置绑定建
public static final String man = "topic.man";
public static final String woman = "topic.woman";
//定义两个队列
@Bean
public Queue firstQueue(){
return new Queue("topic.man");
}
@Bean
public Queue secondQueue(){
return new Queue("topic.woman");
}
//定义一个交换机
@Bean
TopicExchange topicExchange(){
return new TopicExchange("topicExchange");
}
//使用两个链路,绑定交换机与队列,并设置路由key
@Bean
Binding firstBinding(){
return BindingBuilder.bind(firstQueue()).to(topicExchange()).with(man);
}
@Bean
Binding secondBinding(){
return BindingBuilder.bind(secondQueue()).to(topicExchange()).with("topic.#");
}
(2)生产者
这里有两个生产者,topic1发出的消息只能被一种路径接收
@RequestMapping(value = "/topic1" ,method = RequestMethod.GET)
public String sendTopicMessage1(String msg){
//设置一个随机数的id
String messageId = String.valueOf(UUID.randomUUID());
//设置时间
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String, Object> manMap = new HashMap<>();
manMap.put("messageId", messageId);
manMap.put("messageData", msg);
manMap.put("createTime", createTime);
//发送消息
rabbitTemplate.convertAndSend("topicExchange", "topic.man", manMap);
return "ok1";
}
@RequestMapping(value = "/topic2" ,method = RequestMethod.GET)
public String sendTopicMessage2(String msg){
//设置一个随机数的id
String messageId = String.valueOf(UUID.randomUUID());
//设置时间
String createTime = LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"));
Map<String, Object> womanMap = new HashMap<>();
womanMap.put("messageId", messageId);
womanMap.put("messageData", msg);
womanMap.put("createTime", createTime);
//发送消息
rabbitTemplate.convertAndSend("topicExchange", "topic.woman", womanMap);
return "ok2";
}
(3)消费者
以路由topic.#推送出去的消息,两个路由都能接收到消息
@Component
public class TopicManReceiver {
@RabbitHandler
@RabbitListener(queues = "topic.man")
public void receive1(Map textMessage){
System.out.println("topic man收到消息: "+textMessage.toString());
}
@RabbitHandler
@RabbitListener(queues = "topic.woman")
public void receive2(Map message){
System.out.println("topic woman收到消息: "+message.toString());
}
}