一.安装
RabbitMQ使用Erlang语言编写,运行时需要依赖Erlang语言环境,安装RrabbitMQ前需要先安装Erlang,且两者的版本要配套。
Windows系统上安装成功后,只需在开始菜单点击RabbitMQ Service -start便可以启动RabbitMQ服务,RabbitMQ提供了web页面对其服务进行可视化管理,要使用web界面需要在RabbitMQ的安装目录下执行如下命令
rabbitmq-plugins enable rabbitmq_management
二.RabbitMQ工作模型
模型中的相关概念解释
Producer | 消息生产者。主要将消息投递到对应的Exchange上面。一般是独立的程序。 |
Routing Key | 路由关键字。Exchange根据Routing Key进行消息投递 |
Exchange | 消息交换机。指定消息按照什么规则路由到哪个队列Queue。 |
Queue | 消息队列。消息的载体。每条消息都会被投送到一个或多个队列中。 |
Broker | 即RabbitMQ的实体服务器。提供一种传输服务,维护一条从生产者到消费者的传输线路,保证消息数据能按照指定的方式传输。 |
Virtual host | 虚拟主机。一个Broker可以有多个虚拟主机,用作不同用户的权限分离。一个虚拟主机持有 一组Exchange、Queue和Binding。 |
Binding | 绑定。作用就是将Exchange和Queue按照某种路由规则绑定起来。 |
Binding key | Exchange和Queue之间的绑定规则 |
Connection | Producer 和 Consumer 与Broker之间的TCP长连接。 |
Channel | 消息通道,也称信道。在客户端的每个连接里可以建立多个Channel,每个Channel代表一 个会话任务。在RabbitMQ Java Client API中,channel上定义了大量的编程接口。 |
三.常用交换机介绍
交换机主要是接收消息并且转发到与其绑定的队列,常用的交换机有类型有:Direct、Topic、Fanout
Direct Excange
直连交换机,其路由规则是:routing key与binding key完全匹配时,消息才会被投递到绑定的队列中。
Topic Exchange
主题交换机,其路由规则是通配符模糊匹配,若routing key与binding key匹配成功,则消息会被投递到绑定的队列中。binding key通常为包含通配符的字符串,如 gjw.test.*,其中*表示任意一个单词,该binding key可以匹配诸如gjw.test.abcd、gjw.test.xxx、gjw.test.msg等格式的routing key;除了*通配符外,还有#通配符,它表示零个或多个单词,比如gjw.test.#可以匹配gjw.test.abc、gjw.test.abc.def、gjw.test.aaa.bbb.ccc等格式的routing key。
Fanout Exchange
广播交换机,广播类型的交换机与队列绑定时不需要指定binding key,当发送消息到广播交换机时也不需要指定routing key,它会将消息投递到所有与之绑定的队列中。
四.Spring Boot集成RabbitMQ
1.引入消息队列的maven依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.在配置文件中加入RabbitMQ的连接信息
#rabbitmq连接信息配置
spring.rabbitmq.host=localhost
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
3.配置交换机、队列、以及二者的绑定关系
@Configuration
public class Configure {
//配置一个直连交换机,交换机名为gjwDirectExchange
@Bean
public DirectExchange DirectExchange(){
return new DirectExchange("gjwDirectExchange");
}
//配置一个队列,队列名为testQueue1
@Bean
public Queue Queue1(){
return new Queue("testQueue1");
}
//将交换机gjwDirectExchange与队列testQueue1绑定,binding key为test.direct.key
@Bean
public Binding Binding(@Qualifier("Queue1") Queue queue, @Qualifier("DirectExchange") DirectExchange directExchange){
return BindingBuilder.bind(queue).to(directExchange).with("test.direct.key");
}
}
4.创建消息生产者与消费者
/**
* 消息生产者
*/
@Component
public class Producer {
@Autowired
private RabbitTemplate template;
public void sendMessage(){
//向gjwDirectExchange交换机发送消息,routing key为test.direct.key
template.convertAndSend("gjwDirectExchange","test.direct.key","hello direct exchange");
}
}
/**
* 消息消费者
*/
@Component
//配置消费者监听testQueue1队列中的消息
@RabbitListener(queues = "testQueue1")
public class Consumer {
@RabbitHandler
public void msgReceive(String msg){
System.out.println("receive msg : "+msg);
}
}
5.测试消费发送
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProducerTest {
@Autowired
private Producer producer;
@Test
public void sendMessage() throws Exception {
producer.sendMessage();
}
}
运行结果
由于发送消息时的routing key与binding key是完全一致的,因此消息会被转发到testQueue1队列中,监听此队列的消费者便可以收到消息。
6.主题交换机demo
@Configuration
public class Configure {
//配置一个主题交换机,交换机名为gjwTopicExchange
@Bean
public TopicExchange TopicExchange(){
return new TopicExchange("gjwTopExchange");
}
//配置一个队列,队列名为testQueue1
@Bean
public Queue Queue1(){
return new Queue("testQueue1");
}
//将交换机gjwTopicExchange与队列testQueue1绑定,binding key为test.topic.#
@Bean
public Binding Binding(@Qualifier("Queue1") Queue queue, @Qualifier("TopicExchange") TopicExchange topicExchange){
return BindingBuilder.bind(queue).to(topicExchange).with("test.topic.#");
}
}
/**
* 消息生产者
*/
@Component
public class Producer {
@Autowired
private RabbitTemplate template;
public void sendMessage(String routingKey){
//向gjwTopicExchange交换机发送消息
template.convertAndSend("gjwTopicExchange",routingKey,"hello topic exchange");
}
}
//测试消息发送
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProducerTest {
@Autowired
private Producer producer;
@Test
public void sendMessage() throws Exception {
producer.sendMessage("test.topic.abc");
producer.sendMessage("test.topic.def.ghi");
}
}
运行结果
可以看到,使用两个不同的routing key发送消息,消费者都可以收到,这是因为test.topic.abc和test.topic.def.ghi这两个routing key都可以模糊匹配到test.topic.#
7.广播交换机demo
@Configuration
public class Configure2 {
//配置一个广播交换机,交换机名为gjwFanoutExchange
@Bean
public FanoutExchange FanoutExchange(){
return new FanoutExchange("gjwFanoutExchange");
}
//配置一个队列,队列名为testQueue1
@Bean
public Queue Queue1(){
return new Queue("testQueue1");
}
//配置一个队列,队列名为testQueue2
@Bean
public Queue Queue2(){
return new Queue("testQueue2");
}
//将交换机gjwFanoutExchange与队列testQueue1绑定,不需要指定binding key
@Bean
public Binding Binding1(@Qualifier("Queue1") Queue queue, @Qualifier("FanoutExchange") FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue).to(fanoutExchange);
}
//将交换机gjwFanoutExchange与队列testQueue2绑定,不需要指定binding key
@Bean
public Binding Binding2(@Qualifier("Queue2") Queue queue, @Qualifier("FanoutExchange") FanoutExchange fanoutExchange){
return BindingBuilder.bind(queue).to(fanoutExchange);
}
}
/**
* 消息生产者
*/
@Component
public class Producer {
@Autowired
private RabbitTemplate template;
public void sendMessage(){
//向gjwFanoutExchange交换机发送消息,不指定routing key
template.convertAndSend("gjwFanoutExchange","","hello fanout exchange");
}
}
/**
* 消息消费者
*/
@Component
//配置消费者1监听testQueue1队列中的消息
@RabbitListener(queues = "testQueue1")
public class Consumer1 {
@RabbitHandler
public void msgReceive(String msg){
System.out.println("consumer1 receive msg : "+msg);
}
}
/**
* 消息消费者
*/
@Component
//配置消费者2监听testQueue2队列中的消息
@RabbitListener(queues = "testQueue2")
public class Consumer2 {
@RabbitHandler
public void msgReceive(String msg){
System.out.println("consumer2 receive msg : "+msg);
}
}
@RunWith(SpringRunner.class)
@SpringBootTest
public class ProducerTest {
@Autowired
private Producer producer;
@Test
public void sendMessage() throws Exception {
producer.sendMessage();
}
}
运行结果
可以看到,消息被投递到两个队列中,分别被两个消费者消费。