1、如何保证消息不会给重复消费
幂等:一个数据或一个请求,重复来多次,确保对应的数据是不会改变的,不能出错。
- 如果写redis,就没问题,反正每次都是set,天然的幂等性
- 生产者发送消息的时候带上全局唯一的id,消费者拿到消息后,先根据这个id去redis中查找,之前没有消费过的话就处理,并且写入这个id到redis中。如果消费过了,则不处理
- 基于数据库的唯一键
2、消息队列的优缺点,使用场景
优点
- 解耦,降低系统之间的依赖
- 异步处理,不需要等待
- 削峰填谷,将流量从高峰期引导低谷期进行处理
缺点:
- 增加了系统的复杂型,幂等,重复消费,消息丢失等问题。
- 系统可用性降低,mq的故障会影响系统可用
- 一致性,消费端可能失败
场景:日志采集,发布订阅
3、死信队列是什么?延时队列是什么?
- 死信队列也是一个消息队列,它是用来存放那些没有成功消费的消息,通常可以用来处理作为消息重试
- 延时队列是用来存放需要在指定时间被处理的元素的队列,通常用来处理一些具有过期性操作的业务,比如十分钟内未支付就取消订单。
4、RabbitMQ事物消息机制
通过对信道设置实现
- channel.txSelect():通知服务器开启是事务模式;服务端会返回TX.select-OK
- channel.basicPublish:发送消息,可以是多条消息,可以是消费消息提交ack
- channel.txCommit:提交事务
- channel.txRollback:回滚事务
消费者使用事物:
- autoAck=false,手动提交ack,以事物提交或回滚为准
- autoAck=true,不支持事务,也就是说你即使在收到消息之后再回滚事务也于事无补,队列已经把消息一起移除了
如果其中任意一个环节出了问题,就会抛出IOException异常,用户可以拦截异常进行回滚事务,或决定要不要重复消息。事务消息会降低rabbit的性能。
5、RabbimqMq的交换机类型
- fanout:扇形交换机,不再判断routekey,直接将消息分发给所绑定的队列(发布订阅)
- direct:判断routekey的规则是完全匹配的模式,即发送消息指定的routekey要等于绑定的routekey
- topic:判断routekey的规则是模糊匹配的
- header:绑定队列与交换机的时候指定一个键值对,当交换机在分发消息的时候会先解开消息体里的headers数据,然后判断里面时候有设置的键值对,如果发现匹配成功,才将消息分发到队列中;这种交换机类型在性能上交差,在实际工作中很少用到。
6、RabbitMq的持久化机制
- 交换机持久化:exchange_declare创建交换机时通过参数绑定
- 队列持久化:queue_declare创建队列时通过参数绑定
- 消息持久化:new AMQPMessage创建消息时通过参数绑定
append的方式写文件,会根据大小自动消息生成新的文件,rabbitmq启动时会创建两个进程,一个负责持久化消息的存储,另一个负责非持久化的存储(内存不够时)
消息存储的时候在ets表中记录消息在文件中的映射以及消息信息(包括id,偏移量,有效数据,左边文件,右边文件),消息读取时根据该信息在文件读取,同时更新信息
消息删除智慧虫ets删除,变为垃圾数据,当垃圾数据超出比例(默认50%),并且文件数达到了3个,触发垃圾回收,锁定左右两个文件,整理左边文件有效数据,将右边文件有效数据写入左边,更新文件信息,删除左边,完成合并。当一个文件的有用数据等于0时,删除该文件
写入文件钱先写入buffer缓冲区,如果buffer已满,则写入文件(此时只是操作系统的页存)
每隔25ms刷新一次磁盘,不管buffer满没满,都会将buffer和页存中的数据落盘
每次消息写入后,如果没有后续写入请求,则直接刷盘
7、Rabbitmq如何保证消息的可靠性传输
1、使用事物消息
2、使用消息确认机制
发送方确认:
- channel设置为comfirm模式,则每条消息会被分配一个唯一id
- 消息投递成功,信道会发送ack给生产者,包含了id,回调了comfirmCallback接口
- 如果发送错误导致消息丢失,发送nack给生产者,回调ReturnCallback接口
- ack和nack只要一次触发,且只有一次,异步触发。可以技术发送消息。
接收方确认:
- 声明队列时,指定noack=false,broker会等待消费者手动返回ack,才会删除消息,否则立刻删除
- booker的ack没有超时机制,只会判断链接是否断开,如果断开,消息会被重写发送
8、rabbitmq的死信队列,延迟队列的原理
死信队列:
- 消息被消费方否定确认,使用了
channle.basicNack
或channel.basicReject
,并且此时requeue属性被设置了false - 消息在队列中存活时间超过了设置的TTL时间
- 消息队列的数量已经超过了最大队列长度
那么该消息成为死信队列。如果配置了死信队列信息,那么该消息会被丢进死信队列中,如果没有配置,则该消息将会被丢弃
为每个需要死信业务队列配置一个死信交换机,同一个项目的死信交换机可以共用一个,然后为每个业务队列分配一个单独的routekey,死信队列只不过是绑定在死信交换机上的队列,死信交换机也不是什么特殊的交换机,只不过是用来接受死信队列,那么可以为任何类型的交换机
TTL:一个消息或改队列中所有消息的最大存活时间
如果一个消息设置了TTL属性或者进入了设置TTL属性的队列中,那么这条消息在TTL设置的时间内没有被消费,则会成为“死信”。如果同时配置了队列的TTL和消息的TTL,那么较小的那个值将会被使用
只需要消费者一直消费私信队列里面的消息