SprinngBoot整合rabbitmq
基于springboot 2.x
一、概述
1、简介
RabbitMQ是消息中间件的一种,消息中间件即分布式系统中完成消息的发送和接收的基础软件.这些软件有很多,包括ActiveMQ(apache公司的),RocketMQ(阿里巴巴公司的,现已经转让给apache).
RabbitMQ是对高级消息队列协议(Advanced Message Queueing Protocol, AMQP)的实现
AMQP 的工作过程如下图:消息(message)被发布者(publisher)发送给交换机(exchange),交换机常常被比喻成邮局或者邮箱。然后交换机将收到的消息根据路由规则分发给绑定的队列(queue)。最后AMQP代理会将消息投递给订阅了此队列的消费者,或者消费者按照需求自行获取。
2、交换机
Name | Default pre-declared names |
---|---|
Direct exchange | (Empty string) and amq.direct |
Fanout exchange | amq.fanout |
Topic exchange | amq.topic |
Headers exchange | amq.match (and amq.headers in RabbitMQ) |
Direct 直连交换机:
直连型交换机(direct exchange)是根据消息携带的路由键(routing key)将消息投递给对应队列的。直连交换机用来处理消息的单播路由(unicast routing)(尽管它也可以处理多播路由)。
fanout扇形交换机:
扇型交换机(funout exchange)将消息路由给绑定到它身上的所有队列,而不理会绑定的路由键。如果N个队列绑定到某个扇型交换机上,当有消息发送给此扇型交换机时,交换机会将消息的拷贝分别发送给这所有的N个队列。
topic 主题交换机:
主题交换机(topic exchanges)通过对消息的路由键和队列到交换机的绑定模式之间的匹配,将消息路由给一个或多个队列。主题交换机经常用来实现各种分发/订阅模式及其变种。主题交换机通常用来实现消息的多播路由(multicast routing)。
3、队列
AMQP中的队列(queue)跟其他消息队列或任务队列中的队列是很相似的:它们存储着即将被应用消费掉的消息。队列跟交换机共享某些属性,但是队列也有一些另外的属性。
- Name
- Durable(消息代理重启后,队列依旧存在)
- Exclusive(只被一个连接(connection)使用,而且当连接关闭后队列即被删除)
- Auto-delete(当最后一个消费者退订后即被删除)
- Arguments(一些消息代理用他来完成类似与TTL的某些额外功能)
队列在声明(declare)后才能被使用。如果一个队列尚不存在,声明一个队列会创建它。如果声明的队列已经存在,并且属性完全相同,那么此次声明不会对原有队列产生任何影响。如果声明中的属性与已存在队列的属性有差异,那么一个错误代码为406的通道级异常就会被抛出。
4、工作流程
1、建立信息。Publisher定义需要发送消息的结构和内容。
2、建立Conection和Channel。由Publisher和Consumer创建连接,连接到Broker的物理节点上,同时建立Channel。Channel是建立在Connection之上的,一个Connection可以建立多个Channel。Publisher连接Virtual Host 建立Channel,Consumer连接到相应的Queue上建立Channel。
3、声明交换机和队列。声明一个消息交换机(Exchange)和队列(Queue),并设置相关属性。
4、发送消息。由Publisher发送消息到Broker中的Exchange中
5、路由转发。RabbitMQ收到消息后,根据消息指定的Exchange(交换机) 来查找Binding(绑定) 然后根据规则(Routing Key)分发到不同的Queue。这里就是说使用Routing Key在消息交换机(Exchange)和消息队列(Queue)中建立好绑定关系,然后将消息发送到绑定的队列中去。
6、消息接收。Consumer监听相应的Queue,一旦Queue中有可以消费的消息,Queue就将消息发送给Consumer端。
7、消息确认。当Consumer完成某一条消息的处理之后,需要发送一条ACK消息给对应的Queue。
Consumer收到消息时需要显式的向RabbitMQ Broker发送basic.ack消息或者Consumer订阅消息时设置auto_ack参数为true。
在通信过程中,队列对ACK的处理有以下几种情况:
如果Consumer接收了消息,发送ack,RabbitMQ会删除队列中这个消息,发送另一条消息给Consumer。
如果Consumer接收了消息, 但在发送ack之前断开Channel,RabbitMQ会认为这条消息没有被deliver(递送),如果有其他的Channel,会该消息将被发送给另外的Channel。如果没有,当在Consumer再次连接的时候,这条消息会被redeliver(重新递送)。
如果consumer接收了消息,但是忘记了ack,RabbitMQ不会重复发送消息。
新版RabbitMQ还支持Consumer reject某条(类)消息,可以通过设置requeue参数中的reject为true达到目地,那么Consumer将会把消息发送给下一个注册的Consumer。
8、关闭消息通道(channel)以及和服务器的连接。
二、简单示例
交换机,队列,路由都已经设置完毕。如果没有设置的话,可以再配置类中进行配置。
1、字符串数据
1.导入amqp依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-amqp</artifactId>
</dependency>
2.设置访问地址,端口及相关配置。
spring.rabbitmq.host=host
spring.rabbitmq.port=5672
spring.rabbitmq.username=guest
spring.rabbitmq.password=guest
3.使用 RabbitTemplate测试一下。
@RunWith(SpringRunner.class)
@SpringBootTest
public class DirectTests {
@Autowired
RabbitTemplate rabbitTemplate;
@Test
public void test(){
rabbitTemplate.convertAndSend("msg","Hello my message");
}
}
4.以上信息已经发送到消息队列中,在rabbitmq的客户端可以查看。也可以取出来,
@RabbitListener(queues = “msg”)监听到队列中有消息传入就会执行,消息体会封装到参数中。
或者使用receiveAndConvert,本方法可以接受并转化成原来的数据。
@Component
public class DirectReceiver {
@RabbitListener(queues = "msg")
public void receiver(String msg){
System.out.println("msg:"+msg);
}
}
2、对象数据及序列化
对普通字符串数据直接传输,但是对象数据则会序列化。默认采用jdk序列化。
1.准备一个测试用的实体类,实现了序列化接口。
public class User implements Serializable {
private Integer id;
private String name;
private String gender;
public User(Integer id, String name, String gender) {
this.id = id;
this.name = name;
this.gender = gender;
}
public User() {
}
}
2.测试类中传入对象。
@Test
public void test1(){
User user=new User(10,"tony","male");
rabbitTemplate.convertAndSend("rbmq.direct","queue01",user);
}
3.此时消息队列客户端确实可以接受到数据,但是数据是序列化的,不适合阅读。
我们可以用配置类,将其转化为json序列化。此时在客户端查看到的就是json数据。
@Configuration
public class QueueConfiguration {
@Bean
public RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {
RabbitTemplate rabbitTemplate = new RabbitTemplate(connectionFactory);
rabbitTemplate.setMessageConverter(new Jackson2JsonMessageConverter());
return rabbitTemplate;
}
}
4.接受数据,这里我们使用@RabbitListener注解接受到的数据是没有转化的。所以我们使用receiveAndConvert方法。
@Test
public void rece(){
Object o = rabbitTemplate.receiveAndConvert("rbmq.queue01");
System.out.println(o);
}