rabbitmq是一个消息队列,可以用作进程之间的消息传递,有助于系统之间的解耦,流量削峰,还可以用作实现延迟队列。它有交换机,消息队列的概念,消息生产者先将消息发往交换机,再由交换机按照一定的规则转发到一个或多个队列,消费者通过监听队列进行消费消息。
1、ActiveMQ:在互联网公司生产落地案例较少,无法确认是否可以保障高可用和高并发。
2、RabbitMQ:支持高可用,高并发,且低延迟。在生产落地案例较多,社区活跃度高,支持集群部署(消息的高可用,高并发),有完善便捷的管理端。缺点是基于erlang语言开发,较难维护源码。
3、RocketMQ:阿里开源,支持高并发,高可用,性能好,基于java语言开发,可维护性高。
4、kafka:常用作实时日志采集,实时数据同步。拥有超高吞吐量,在大数据领域运用较多。
所有的消息都是先投递到交换机,再由交换机分发给各个队列。交换机可以指定消息按什么规则,投递到哪个队列。
队列是消息的载体,消息到达队列后,等待消费者的消费。
绑定是指交换机和队列之间的绑定。交换机和队列之间可以通过路由键进行绑定(Routing Key),路由键就是消息从交换机发往哪个队列的规则。
当消息到达交换机后,广播到和交换机绑定的所有队列。
当消息到达交换机后,交换机会按照消息携带的路由键,和交换机与队列绑定的路由键进行匹配,匹配通过则发到对应的队列。
发送类型是topic的交换机的消息的路由键可以加上通配符,*可以代替一个单词,#可以代替零个或多个单词。
举例如下:
交换机X和队列Q1之间的绑定关系为*.orange.*
交换机X和队列Q2之间的绑定关系为*.*.rabbit
、lazy.#
如下图:
“死信”是RabbitMQ中的一种消息机制,当消费者在消费时,如果存在以下情况: (1)消息被否定确认,使用channel.basicNack或channel.basicReject,并且此时requeue属性被这支为false。 (2)消息在队列的存活时间超过设置的生存时间(TTL)时间。 (3)消息队列的消息数量已经超过了队列的最大长队。 那么该消息就会成为“死信”,Dead Letter,该消息会被RabbitMQ进行特殊处理,如果配置了死信队列
,那么该消息就会被丢进死信队列中,如果没有配置,则该消息将会被丢弃。
死信队列的消息是从业务队列丢到死信队列的,所以需要先配置业务队列,步骤如下:
(1)创建业务交换机,创建业务队列
(2)绑定业务队列到业务交换机上
(3)创建死信交换机,创建死信队列
(4)绑定死信队列到死信交换机上
(5)为业务队列配置死信交换机和路由键
注意:
并不是直接声明一个死信队列,然后所有的死信消息都跑到死信队列中去了。而是需要为每个业务队列配置一个死信交换机,同一个项目的死信交换机可以共用一个,然后为每个业务队列分配一个单独的路由key。
死信队列并不是什么特殊的队列,只不过是绑定在死信交换机上的队列,死信交换机也不是什么特殊的交换机,只不过是用来接收普通业务队列中的死信消息的交换机。
特殊点在于为普通业务队列配置死信交换机时,第(5)步,是需要在创建普通队列时指定参数x-dead-letter-exchange和x-dead-letter-routing-key,如下:
//声明业务队列
@Bean("businessQueueB")
public Queue businessQueueB(){
Map<String,Object> args = new HashMap<>(2);
args.put("x-dead-letter-exchange", DEAD_LETTER_EXCHANGE);//DEAD_LETTER_EXCHANGE为声明的死信交换机,与普通交换机声明一样
args.put("x-dead-letter-routing-key", DEAD_LETTER_ROUTING_KEY);//到死信交换机的路由键
return QueueBuilder.durable("BUSINESS_QUEUE_NAME").withArguments(agrs).build();
}
TTL(time to live),在rabbitmq中,队列和消息本身都有这个属性,当消息过期后就成了死信,会被清除,但是他们却有着区别。
代表消息多久过期,但是判断过期是在消息出队列的时候判断的,所以这种方式没有实时做到清除过期的消息,而且当队列存在积压时,过期消息也没有得到及时删除。
给队列加上TTL属性后,代表消息在队列中存活的最大时间,消息一旦过期就会被删除,存在积压时能有效的及时清除过期的消息。
TTL属性和死信队列配合使用,就可以实现延迟队列,如下:
(1)首先实现死信队列
,但是需要给业务队列加上TTL属性
(2)当业务队列的消息过期后就成了死信,会被丢到死信队列
(3)消费者监听死信队列消费就可以了
问题描述:发送者丢失是指发送者到交换机,交换机将消息投递到对应的队列这一链路出了问题。
解决机制:开启发送确认模式
,开启confirm模式后,发送者发送的消息会被指派一个唯一的id,一旦消息被正确投递到队列,就会返回一个正确投递的应答(包含消息的唯一id)给发送者;如果某一环节出错,则会返回一个错误应答给发送者,发送者拿到这个应答可以重试,也可以记录日志或者记录表,用定时任务进行轮询。
问题描述:队列丢失是指消息在队列中丢失,一般造成原因是rabbitmq服务挂掉或者宕机了。
解决机制:可以开启持久化机制
,当消息到达队列后会持久化到磁盘上。可以和发送确认模式
配合使用,当消息投递到对应队列后,进行持久化,持久化完成过后再将正确应答返回给发送者,持久化失败则返回错误应答,发送采取重发等机制。
消费确认机制: 消息消费确认机制分为自动确认机制和手动确认机制。
自动确认机制是默认的,当消息到达消费者的时候,消费者就会自动向rabbitMQ确认消息已到达,rabbitMQ就会删除队列中已经确认的消息。
存在消息丢失问题
:当消费者收到消息后立即确认消息,但是在处理业务的过程中却报错了,导致业务没有被正确处理完,但这个时候消息已经被删除了,也不会重发了,所以此时就造成消息丢失了,解决此问题就需要开启手动确认机制。
开启手动确认机制,当消息到达消费者后,先执行业务逻辑再进行确认,如果执行业务失败,就否定确认,让rabbitmq重新发送消息。
重复消费问题是指消费者在成功消费消息后,队列没有删除消息,队列又将该消息投递给下一个消费者,导致重复消费。那关键点在于为什么队列没有删除消息,正常情况是队列接收到消费者的应答后才会删除消息,但是在应答的途中,由于网络原因导致消费者和rabbitmq之间的连接中断,导致rabbitmq没有收到消费者的应答,所以又选择下一个消费者投递消息。
这里需要注意的是,rabbitmq和消费者之间并没有超时机制,为了保证消息只投递一次,是给足了时间让消费者消费,除非连接断开导致没有收到应答。
参考文档
队列只存在于被创建的节点上,而其它节点只拥有队列的元数据(队列名和属性),其它节点只做转发,将请求转发到队列所在的队列上。优点在于
能够在一定程度上提高并发量。缺点在于
不能保障高可用,因为一旦队列所在的节点挂掉了,就丢失了,没有备份。
镜像节点在集群中的其他节点拥有从队列拷贝,一旦主节点不可用,最老的从队列将被选举为新的主队列。但镜像队列不能作为负载均衡使用,因为每个操作在所有节点都要做一遍。该模式带来的副作用也很明显,除了降低系统性能外,如果镜像队列数量过多,加之大量的消息进入,集群内部的网络带宽将会被这种同步通讯大大消耗掉。所以在对可靠性要求较高的场合中适用。