SpringBoot整合RabbitMQ学习记录
RabbitMQ安装过程在此就不说了,去官网下载安装启动运行即可
RabbitMQ下载地址
启动后可进入后台管理控制台查看如下:
RabbitMQ简介
RabbitMQ是一个开源的AMQP实现,服务器端用Erlang语言编写,支持多种语言平台的客户端,如:Python、Ruby、.NET、Java、JMS、C、PHP、ActionScript、XMPP、STOMP等。用于在分布式系统中存储转发消息,在易用性、扩展性、高可用性等方面表现不俗。
通常我们谈到消息队列, 会有三个概念: 消息生产者(Provider)、队列(Queue)、消息消费者(Consumer),RabbitMQ 在这个基本概念上, 多做了一层抽象, 在消息生产者和队列之间, 加入了交换器 (Exchange)。这样消息生产者和队列就没有直接联系, 变成消息生产者把消息发送给交换器, 交换器根据调度策略再把消息发送给队列。
- 左侧P代表消息生产者,也就是往RabbitMQ发消息的程序。
- 中间即是RabbitMQ,其中包括交换机(Exchange)和队列(Queue)。
- 右侧C代表消费者,也就是往RabbitMQ拿消息的程序。
其中比较重要的概念有:虚拟主机(Virtual Host)、交换机(Exchange)、队列(Queue)、绑定(Binding)。
-
虚拟主机(Virtual Hosts):
在上面已经说明如何为一个用户创建一个Virtual Host,一个虚拟主机持有一组交换机、队列和绑定。在RabbitMQ当中,用户只能在虚拟主机这个粒度上进行权限的控制。 如果需要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个RabbitMQ服务器都有一个默认的虚拟主机“/”。 -
交换机(Exchange):
交换机的功能主要是接收消息并且根据转发策略转发到对应的队列,交换机不存储消息,在启用ack模式后,交换机找不到队列会返回错误,这个ack模式后面再详细讨论。交换机有四种类型:Direct, topic, Headers and Fanout -
队列(Queue):
队列用于存放消息的载体,一般是和交换机进行绑定,交换机根据转发策略把消息转发到队列里。 -
绑定(Binding):
也就是交换机需要和队列相绑定,这其中如上图所示,是多对多的关系。
交换机类型介绍
Direct Exchange:
direct 类型的行为是”先匹配, 再投送”. 即在绑定时设定一个binding_key, 消息的routing_key与binding_key匹配时, 才会被交换器投送到绑定的队列中去.
Topic:
转发消息主要是根据通配符。 在这种交换机下,队列和交换机的绑定会定义一种路由模式,通配符就要在这种路由模式和路由键之间匹配后交换机才能转发消息。
- 路由键必须是一串字符,用句号(.)隔开,比如说 topic.message,或者 topic.message.detail 等。
- 路由模式必须包含一个星号(),主要用于匹配路由键指定位置的一个单词,比如说,一个路由模式是这样:topic.,那么就只能匹配路由键是:topic.message、topic.other等,第一个单词是 topic,第二个单词可以是任意一个单词。 井号(#)就表示一个或者多个单词,例如一个匹配模式是topic.#,那么可以匹配到例如:topic.message、topic.message.detail等,以topic.开头的路由键都可以匹配到。
Fanout:
Fanout类型类似于消息广播,不管路由键或者是路由模式,会把消息发给绑定给它的全部队列,如果配置了routing_key会被忽略。
Headers:
设置header attribute参数类型的交换机
show you code
本文是基于Springboot-1.5.15整合RabbitMQ来进行讲解,在真实工作中,生产者和消费者一般是在不同的项目里,各自负责不同的职责,这里为了模拟真实环境,创建两个不同的项目进行演示。创建两个maven项目,消息生产者mq-rabbit-provider和消息消费者mq-rabbit-consumer,两个项目的pom.xml文件添加相同依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
mq-rabbit-provider项目的application.properties内容如下:
server.port=8080
spring.application.name=springboot-rabbitmq-provider
spring.rabbitmq.host=10.211.55.3
spring.rabbitmq.port=5672
spring.rabbitmq.username=root
spring.rabbitmq.password=root
#RabbitMQ的虚拟host
spring.rabbitmq.virtual-host=CalonHost
mq-rabbit-consumer项目的application.properties内容如下:
server.port=9090
spring.application.name=springboot-rabbitmq-consumer
spring.rabbitmq.host=10.211.55.3
spring.rabbitmq.port=5672
spring.rabbitmq.username=root
spring.rabbitmq.password=root
#RabbitMQ的虚拟host
spring.rabbitmq.virtual-host=CalonHost
这里只是端口和应用名不同,其他都一样。
接下来分别介绍Direct、Topic、Fanout等3种不同交换机的使用例子。
Direct Exchange
在mq-rabbit-provider项目建一个配置类DirectRabbitConfig.java,配置交换机、队列、BindingKey=CalonDirectRouting的绑定关系,代码如下:
@Configuration
public class DirectRabbitConfig {
//队列
@Bean
public Queue CalonDirectQueue() {
return new Queue("CalonDirectQueue",true);
}
//Direct交换机
@Bean
DirectExchange CalonDirectExchange() {
return new DirectExchange("CalonDirectExchange");
}
//绑定
@Bean
Binding bindingDirect() {
return BindingBuilder.bind(CalonDirectQueue()).to(CalonDirectExchange()).with("CalonDirectRouting");
}
}
创建一个实体类User.java,这里说明一下,该实体类是消息的主体,所以必须实现Serializable接口,否则在消息消费者项目读取消息时会报错,代码如下:
package mq.rabbit.entity;
import java.io.Serializable;
public class User implements Serializable{
private static final long serialVersionUID = 1L;
private String id;
private String username;
private String password;
private String type;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getType() {
return type;
}
public void setType(String type) {
this.type = type;
}
public User() {
super();
}
public User(String id, String username, String password, String type) {
super();
this.id = id;
this.username = username;
this.password = password;
this.type = type;
}
}
下面创建一个Controller,利用http请求进行调试,CalonDirectExchange是上面配置的交换机标识,CalonDirectRouting就是上面绑定好的queue名字,由于上面已经配置好交换机和队列的绑定关系,这两个组合就可以知道消息最终是发送到队列CalonDirectQueue里面去了,Controller类的代码如下:
@Controller
public class SendController {
@Autowired
private RabbitTemplate template;
@GetMapping("/sendDirect")
private @ResponseBody String sendDirect(String message) throws Exception {
User user = new User(UUID.randomUUID().toString(), message, "123456", "sendDirect");
template.convertAndSend("CalonDirectExchange", "CalonDirectRouting", user);
return "OK,sendDirect:" + message;
}
}
启动mq-rabbit-provider项目,在浏览器输入:
http://localhost:8080/sendDirect?message=123
再去RabbitMQ的web管理后台查看,你会发现在Queue里找到刚刚添加的那个队列,后面的数字就是消息数量有变化,说明消息已经存储进去了:
把mq-rabbit-provider项目里的User类和DirectRabbitConfig类复制到mq-rabbit-consumer项目,User类用于读取消息时接收消息对象,DirectRabbitConfig可以不复制,但是如果RabbitMQ里还没有被监听的队列时会报错,复制过来是为了让RabbitMQ里还没有被监听的队列时自动创建该队列,防止报错。
创建队列监听类DirectReceiver.java,代码如下:
package mq.rabbit.receiver;
import org.springframework.amqp.rabbit.annotation.RabbitHandler;
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.stereotype.Component;
import mq.rabbit.entity.User;
@Component
@RabbitListener(queues = "CalonDirectQueue")//CalonDirectQueue为队列名称
public class DirectReceiver {
@RabbitHandler
public void process(User user) {
System.out.println("DirectReceiver消费者收到消息 : " + user.getId()+","+user.getUsername()+","+user.getPassword()