RabbitMQ实战笔记收集整理

生产者、消费者以及队列初步认识

–队列是rabbitmq内部对象,用于存储消息;生产者生产消息投递队列中;消费者从队列中获取消息并消费。多个消费者可订阅同一队列,队列中的消息会被均分给多个消费者处理,不是每个消费者都收到所有消息处理。生产者将消息发送给exchange,通过binding将exchange与queue关联。

AMQP工作原理

–发布者发布消息,经由交换机。交换机根据路由规则将收到的消息分发给与该交换机绑定的队列。最后AMQP代理会将消息投递给订阅了此队列的消费者,或者消费者按需自行获取。

–在某些情况下,例如当一个消息无法被成功路由时(无法从交换机分发到队列),消息或许会被返回给发布者并被丢弃。或者,如果消息代理执行了延期操作,消息会被放入一个死信队列中。此时,消息发布者可以选择某些参数来处理这些特殊情况。

RabbitMQ的工作模式

1.工作队列模式
2.发布订阅模式
3.路由模式
4.通配符模式
5.headers模式
6.rpc模式
其中路由模式和通配符模式用的比较多

RabbitMQ中的几个比较重要的概念(虚拟主机,交换机,队列,绑定,路由键)

虚拟主机:一个虚拟主机持有一组交换机、队列和绑定。用户只能在虚拟主机的粒度进行权限控制。因此如果要禁止A组访问B组的交换机/队列/绑定,必须为A和B分别创建一个虚拟主机。每一个rabbitMQ服务器都有一个默认的虚拟主机“/”。
交换机:用户转发消息,但它不会做存储。如果没有Queue bind到Exchange的话,它会直接丢掉Producer发送过来的消息。
队列:用户存储着即将被消费的消息。队列在声明后才能被使用。持久化队列会被存储在磁盘上,当消息代理重启的时候,它依旧存在。
绑定:交换机和队列相互绑定。
路由键:消息到交换机的时候,交换机会转发到对应的队列中,路由键决定转发规则。

交换机详解之FanoutExchange

–将发送到所有与fanoutExchange绑定的消息队列中,忽略routingkey

应用场景:

大规模多用户在线游戏可以使用它来处理排行榜更新等全局事件
体育新闻网站可以用它来近乎实时的将比分更新分发给移动客户端
分发系统使用它来广播各种状态和配置更新
在群聊的时候,它可被用来分发消息给参与群聊的用户

交换机详解之DirectExchange+路由

–将消息发送到bandingkey与routingkey一一匹配的队列中—Routing Key==Binding Key

交换机详解之TopicExchange发布订阅模式匹配

–将消息发送到符合bindingkey与routingkey匹配模式规则的队列中,#与*

使用场景

分发有关于特定地理位置的数据,例如消息点
由多个工作者完成的后台任务,每个工作者负责处理某些特定任务
股票价格更新
涉及到分类或者标签的新闻更新
云端的不同种类服务的协调
分布式架构、基于系统的软件封装,其中每个构建者仅能处理一个特定的架构或者系统。

springboot整合RabbitMQ配置详解

– 配置rabbitMQ地址、端口、账户信息、配置队列、发送者、接收者

springboot整合RabbitMQ之事件驱动模型

–生产者->发送消息->队列->消费者监听处理消息

发送接收消息之创建队列交换机路由及绑定

–Binding binding=BindingBuilder.bind(queue).to(exchange)

发送接收消息之@RabbitMQListener接收消息

–@RabbitListener(queues=“queueName”,containerFactory=“containerFactory”)

发送接收消息之RabbitTemplate发送消息

–构造方法注入rabbitTemplate,使用rabbitTemplate.convertAndSend(exchaneName,routingKey,content,correlationId)

多线程消费同一队列

RabbitMQ默认采用轮询分发消息。
fair dispatch:做到按照每个消费者的能力分配消息。使用Qos和Acknowledge。消息从队列异步推送给消费者,消费者的ack也是异步发送给队列,从队列看,总会有一些消息已推送但尚未获得ack确认,Qos的prefetchCount参数就是用来限制这批未确认消息数量的。设为1时,队列只有在收到消费者发回的上一条消息ack确认后,才会向该消费者发送下一条消息。prefetchCount默认为0,即没有限制,队列会将所有消息尽快发给消费者。
basicQos中prefetchSize参数通过消息的总字节数来限制队列推送消息的速度。
prefetchSize与prefetchCount可以同时设置,达到任何一个限制,则队列暂停推送消息
global参数表示前两个参数的作用域,true表示限制是针对信道的,false表示限制是针对消费者的
可以对同一个信道同时设置global为true和false的Qos,表示队列要考虑每个消费者的限制,同时还要考虑整个信道的限制

如何保证发布的消息有没有正确的到达broker代理服务器呢?

– 如果不进行特殊配置,默认情况下发布操作不会返回任何信息给生产者的,也就是默认情况下生产者不知道消息有没有正确到达broker的

通过AMQP事务机制实现----降低RabbitMQ的性能
通过将channel设置成confirm模式来实现
producer端confirm模式实现:一旦信道进入confirm模式,所有在该信道上发布的消息都会被指派一个id,一旦消息被投递到所有匹配的队列后,broker就会发送一个确认给生产者,这样生产者就知道消息已经正确到达目的队列了。生产者得到确认后,便可通过毁掉方法来处理该确认消息。
consumer端confirm模式实现:factory.setAcknowledgeMode(AcknowledgeMode.MANUAL),noAck=false,RabbitMQ会等待消费者显式发回ack信号后才从内存中移出消息。这样消费者就有足够时间处理消息,不用担心消息丢失问题,因为RabbitMQ会一直持有消息知道消费者显式调用basicAck为止。

消息确认机制

发送者没法确认是否发送成功,消费者处理失败也无法反馈
官网介绍,唯一保证消息不丢失的是使用事务,但是性能太差,作为补偿,有了消息确认机制。此机制和事务模式不可共存。
确认消息不能路由时(exchange确认不能路由到任何queue),进行确认操作(确认失败)。如果发送方设置了mandatory模式,则会先调用basic.return方法,消息可以路由时,当需要发送的队列都发送成功后,进行消息确认。对于持久化队列,意味着已经写入磁盘,对于镜像队列,意味着所有镜像都接收成功。

mandatory与immediate

mandatory=true,如果exchange根据自身类型和消息routingkey无法找到queue,会调用basic.return将消息返回给生产者;mandatory=false,则broker会直接将消息丢掉。
immediate=true,mandatory标识告诉服务器至少将该消息route到一个队列中,否则将消息返还给生产者;告诉服务器如果该消息关联的queue上有消费者,则马上将消息投递给它,如果queue都没有消费者,直接把消息返还给生产者,不用将消息入队列等待消费者了。
rabbitMQ3.0以后的版本,去掉了immediate参数的支持,发送带immediate标记的publish会报错。

消息确认机制ChannelAwareMessageListener详解

basicAck: basicAck方法的第一个参数是Delivery Tag,它用来标识信道中投递的消息。rabbitMQ推送消息给Consumer时,会附带一个DeliveryTag,以便Consumer可以在消息确认时告诉RabbitMQ到底是哪条消息被确认了。rabbitMQ保证在每个信道中,每条消息的delivery Tag从1开始递增。
第二个参数multiple取值为false时,标识通知rabbitMQ当前消息被确认;如果为true,则额外将比第一个参数指定的delivery tag小的消息一并确认。(批量确认针对的是整个信道),对同一消息的重复确认,或者对不存在的消息的确认,会产生IO异常,导致信道关闭。
如果忘记确认消息:只要程序还在运行,则消息会一直处于unacked状态,无法被rabbitMQ重写投递。rabbitMQ消息消费并没有超时

队列优先级

– 具有更高优先级的队列就要较高的优先权,优先级高的消息具备优先被消费的特权。x-max-priority

队列长度限制

– x-max-length参数限制了一个队列的消息总数,当消息总数达到限定值时,队列头的消息会被抛弃。处于unacked状态的消息不纳入消息总数计算。但是当unacked消息被reject并重新入队时,就会受x-max-length参数限制,可能回不了队列。

队列的持久化

–queue的持久化是通过durable=true来实现的。queueDeclarePassive(String queue)可以用来检测一个queue是否已经存在。如果该队列存在,则返回true,如果不存在就会返回异常,但不会创建新的队列。

消息的持久化

–持久化的队列在服务重启后,依然存在,但是里面的消息是否持久化得看消息的持久化设置。这时必须设置消息的持久化了。 --设置了队列和消息的持久化之后,当broker服务重启之后,消息依旧存在。当只设置队列持久化,重启之后消息会丢失;当只设置消息的持久化,重启之后队列消失,继而消息也丢失。

exchange的持久化

–如果exchange不设置持久化,那么当broker服务重启后,exchange将不复存在,那么继而发送方rabbitmq producer就无法正常发送消息。

基于DirectExchange+RoutingKey异步写日志

基于DirectExchange+RoutingKey异步发送邮件

TTL

TTL(过期时间):rabbitMQ可以对消息和队列设置TTL。目前有两种方法可以设置。1、通过队列属性设置,队列中所有消息都有相同的过期时间。2、对消息进行单独设置,每条消息TTL可以不同。如果两种同时设置,则消息的过期时间以两者之间TTL较小的那个数值为准。消息在队列的生存时间一旦超过设置的TTL值,就称为dead message,消费者将无法再收到该消息。
整个queue设置TTL:queue.declare(),x-message-ttl参数设置,一旦消息过期,就会从队列中抹去。
如果不设置TTL,则表示此消息永不过期。如果设置为0,则表示除非此时可以直接将消息投递到消费者,否则该消息会被立即丢弃。
针对每条消息设置TTL:basic.publish()加入expiration属性参数。即使消息过期,也得等到消息在被消费时通过判断是否过期,如果过期再进行删除。RabbitMQ对队列头部消息的TTL扫描是自发的,即使没有Consumer连接在队列上,过期消息也会被正确的移出的。

死信队列DLX

– 当消息在一个队列中变成死信(dead message)之后,它能被重新publish到另一个exchange,这个exchange就是DLX。 消息变成死信有以下几种情况:

消息被拒绝(basic.reject/basic.nack)并且requeue=false
消息TTL过期
队列达到最大长度
RabbitMQ的Queue可以配置x-dead-letter-exchange和x-dead-letter-routing-key两个参数,如果队列内出现了dead letter,则按照这两个参数重新路由转发到指定的队列。

x-dead-letter-exchange:出现dead letter之后将dead letter重新发送到指定exchange
x-dead-letter-routing-key:出现dead letter之后将dead letter重新按照指定的routing-key发送
设置了TTL规则后当消息在一个队列中变成死信时,利用DLX特性它能重新转发到另一个Exchange或者RoutingKey,这时消息就可以重新被消费了

延迟队列

– 存储延迟消息-当消息被发送以后,并不想让消费者立即拿到消息,而是等待指定时间后,消费者才拿到这个消息进行消费。 结合TTL和DLX共同实现延迟队列

死信队列DLX,DLK,TTL认识

死信队列消息延迟分发

死信队列动态设置TTL

rabbitMQ消息顺序、消息幂等、消息重复、消息事务、集群、问题追踪

RabbitMQ的高可用性

– 基于主从(非分布式)做高可用性的,三种实现模式:单机模式、普通集群模式、镜像集群模式

– 单机模式:demo级别的

– 普通集群模式(无高可用性) 在多台机器上启动多个RabbitMQ实例,每个机器启动一个。而创建的Queue,只会放在一个RabbitMQ实例上,但是每个实例都同步queue的元数据(元数据可以认为是queue的一些配置信息,通过元数据,可以找到queue所在实例)。而消费的时候,实际上如果连接到了另外一个实例,那么这个实例会从queue所在实例上拉取数据过来。缺点:MQ集群内部可能产生大量的数据传输;可用性无保障,queue所在节点宕机,数据就丢失了。

– 镜像集群模式(高可用性) 在此模式下,创建的queue,无论元数据还是queue里的消息都会存在于多个实例上,每个RabbitMQ节点都有这个queue的一个完整镜像,包含queue的全部数据的意思,每次写消息到queue的时候,都会自动把消息同步到多个实例的queue上。如何开启:在控制后台新增一个策略,这个策略就是镜像集群模式的策略,指定的时候是可以要求数据同步到所有节点的,也可以要求同步到指定数量的节点,再次创建queue的时候,应用这个策略,就会自动将数据同步到其它节点上去了。这样子,任何一个机器宕机了,其它机器还包含了这个queue的完整数据,别的consumer都可以到其它节点上去消费数据。缺点:性能开销太大,消息需要同步到所有机器上,导致网络带宽压力和消耗很重。

如何保证消息队列消费的幂等性?

insert前先做判断,如果已存在数据则update一下
如果是写redis,每次set已经保证了幂等性
让生产者发送每条数据的时候,里面加一个全局唯一的ID,消费到了之后可以根据ID去库里查一下,之前是否有没消费过。
基于数据库唯一键约束。

如何保证消息的可靠性?

---- 数据的丢失问题,可能出现:

生产者产出消息在传入到rabbitMQ服务过程中丢失(1、开启rabbitMQ事务特性-》耗性能,2、开启confirm模式,在生产者那里设置开启confirm模式之后,每次写的消息都会分配一个唯一的ID,如果成功写入,rabbitMQ会回传一个ack消息,告诉你消息OK了;如果失败,则会回调一个nack接口,告诉你这个消息接收失败,你可以重试。)
RabbitMQ收到消息,暂存内存中,还没消费,自己挂掉了,内存中的数据搞丢(给消息设置持久化,1、创建queue的时候将其设置为持久化2、发送消息的时候将消息的deliveryMode设置为2)
消费者消费到了这个消息,但还没来得及处理,就挂了,RabbitMQ以为消息已经被处理了(消费端开启手动ack)

如何保证消息的顺序性?

拆分多个 queue,每个 queue 一个 consumer,就是多一些 queue 而已,确实是麻烦点;或者就一个 queue 但是对应一个 consumer,然后这个 consumer 内部用内存队列做排队,然后分发给底层不同的 worker 来处理。

如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几小时,说说怎么解决?

大量消息在mq积压几个小时的解决:

先修复consumer的问题,确保其恢复消费速度,然后将现有consumer都停掉
新建一个topic,partition是原来的10倍,临时建立好原先10倍的queue数量
然后写一个临时分发数据的consumer程序,这个程序部署上去消费积压的数据,消费之后不做耗时的处理,直接均匀轮询写入临时建立好的10倍数量的queue。
接着临时征用10倍的机器来部署consumer,每一批consumer消费一个临时queue的数据。这种做法相当于是临时将queue资源和consumer资源扩大10倍,以正常的10倍速度来消费数据。
等快消费完积压的数据之后,得恢复原先部署的架构,重新用原先的consumer机器来消费消息。

如果消息在queue中积压超过一定的时间就会被RabbitMQ给清理掉,这个数据就没了。批量重导----手工找到丢失的数据,手动发到mq里面去再补一次。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值