RabbitMQ学习总结
一.消息队列的概述
消息队列解决的问题:
- 异步处理
- 应用解耦:在分布式服务之间添加消息中间件,达到服务之间的解耦
- 流量削峰:在高并发业务前添加一层消息中间件,过滤掉一些请求后再将请求递交给业务
- 日志处理:大数据方面的应用
主流的消息中间件:
- ActiveMQ: 老牌消息中间件,但是在性能上不如现在的其他消息队列,可以在并发量较小的场景下使用
- Kafka: 追求高吞吐量,但对于消息的重复,丢失,错误等没有严格的要求,适合产生大量数据的互联网服务的数据收集业务
- RocketMQ: 纯Java开发的具有高吞吐量,高可用性的消息队列,但商业版收费
- RabbitMQ: 使用Erlang开发,基于AMQP协议实现的消息队列,适用于对数据一致性,稳定性,可靠性要求很高的场景,对性能和吞吐量的要求在其次
二. RabbitMQ核心概念
Message, Publisher, Exchange, Queue, Binding, Connection, Channel, Consumer, Virtual Host, Broker
消息传递的流程:
- 生产者和Broker建立TCP连接,并和Broker建立通道
- 生产者通过通道将消息发送给Exchange
- Exchange根据绑定规则(路由键)将消息传递给Queue
- 消费者从监听的Queue上获取消息
所以在该过程中,生产者将消息发送给MQ任务就算完成,而消费者只需要负责从MQ中获取到消息任务就算完成,生产者和消费者之间没有直接的服务调用,也就达到了解耦的目的。
但是在这个流程中也需要思考一些问题:
- 首先在生产端,如何保证生产者的消息可靠地投递到了MQ中,如果没有投递成功,或者投递到Exchange后发现没有匹配的路由规则,该怎么处理?
- 而在消费者端,MQ如何确认消息已经可靠地被消费者所接收;当一个Queue被多个消费者监听时,该如何分发Queue中的消息?
这几个问题将在后续进一步讨论
Exchange的类型:
- direct: 完全匹配
- fanout: 广播
- topic: 模式匹配
- header(实际中很少用)
三.与SpringBoot的整合
Springboot整合RabbitMQ时,最简单的配置只需在配置文件中配置如下项:
spring:
rabbitmq:
host: 127.0.0.1
virtual-host: /
username: quest
password: quest
Springboot已经自动配置好了rabbitTemplate和AmqpAdmin
rabbitTemplate
其中rabbitTemplate可以用来操作消息队列,如发送,接收消息等
使用前先自动注入:
@Autowired
private RabbitTemplate rabbitTemplate;
然后即可调用其API发送消息
send(exchange, routingKey, message) #直接发送消息
#如果直接发送Message对象,可以使用MessageBuilder来链式构建消息体和消息属性
Message message = MessageBuilder.withBody("hello rabbitMQ".getBytes())
.setDeliveryMode(MessageDeliveryMode.PERSISTENT) //消息是否持久化
.setExpiration("5000") //消息过期时间
.build();
rabbitTemplate.send(exchange, routingkey, message);
convertAndSend(exchange, routingKey, Object) #直接传入对象,对象经转换后被发送
默认的转换器是将对象序列化后发送,也可以在配置类中修改转换器
@Bean
public MessageConverter messageConverter(){
return new Jackson2JsonMessageConverter(); //使用Json转换器
}
对于更复杂的需求可以选用ContentTypeDelegatingConverter,针对不同的消息类型可以委托给不同的消息转换器
AmqpAdmin
AmqpAdmin则用于创建和删除Queue, Exchange, Binding
可以通过AmqpAdmin的declareExchange, declareQueue, declareBingding 等方法来创建和删除Queue, Exchange, Binding
更简单但方式是在配置类中直接配置Queue, Exchange, Binding的Bean
@Bean
public Exchange exchange01(){
Exchange exchange = new DirectExchange("exchange01",true,false);
return exchange;
}
@Bean
public Queue queue01(){
Map<String, Object> arguments = new HashMap<>();
arguments.put("x-max-length",1000);
Queue queue = new Queue("queue01", true, false, false, arguments);
return queue;
}
@Bean
public Binding binding01(){
//创建绑定时可以使用BindingBuilder来链式创建
Binding binding01 = BindingBuilder.bind(queue01())
.to(exchange01())
.with("queue01").noargs();
return binding01;
}
在声明Queue, Exchange, Binding时建议在生产者和消费者端都声明,防止一端启动时发现Queue, Exchange, Binding还没创建好
@RabbitListener
消费者监听消息时,SpringBoot提供了@RabbitListener注解, 使用时现在主方法上添加@Enable注解, 然后在需要从MQ中接收消息的方法上添加@RabbitListener即可
@RabbitListener(queues = "queue01") //添加监听的队列名
public void receive(Order order, Channel channel, @Headers Map<String,Object> headers, Message message) throws IOException { //接收的参数可以有很多,选择需要的即可
log.info("收到消息:{}", order);
}