MQ方面
1.mq的优势
1)解耦:系统A在代码中直接调用系统B和系统C的代码,如果将来D系统接入,系统A还需要修改代码,过于麻烦!
2)异步:将消息写入消息队列,非必要的业务逻辑以异步的方式运行,加快响应速度,提高性能。
3)削峰填谷:并发量大的时候放入mq中,保证系统不会挂掉,高峰期过后在按照系统能接受的请求数量处理。
2.mq的缺陷
系统可用性降低,系统复杂性变高,一致性存在问题。
3.如何保证mq高可用
1)普通集群模式:实际queue保存在一个节点上,其他节点保存该 queue的元数据,消费者连接某个节点,该节点去调用实际存放queue的节点,返回数据,说白了只是简单的提高了吞吐量而已。
缺陷在于:在集群内部产生大量的数据传输,高可用性没有,queue所在的节点宕机,服务就没法继续使用。
2)镜像集群模式:每个节点上都保存了实际的queue的全部数据,消费者无论连接哪个节点,都能直接获取到数据.任何一个节点宕机,其他节点还存在queue的全部数据,消费者换个节点继续消费,这是高可用的模式。
缺陷在于:性能消耗太大,所有机器都要进行消息的同步,导致网络压力和消耗很大。没有扩展性可言,如果有一个queue负载很重,就算加了机器,新增的机器还是包含了这个queue的所有数据,并没有办法扩展queue。
4.常用的交换器主要分为以下三种
1) direct:如果路由键完全匹配,消息就被投递到相应的队列。
2)fanout:如果交换器收到消息,将会广播到所有绑定的队列上。
3)topic:可以使来自不同源头的消息能够到达同一个队列。 使用topic交换器时,可以使用通配符,比如:“*” 匹配特定位置的任意文本, “.” 把路由键分为了几部分,“#” 匹配所有规则等。特别注意:发往topic交换器的消息不能随意的设置选择键(routing_key),必须是由"."隔开的一系列的标识符组成。
5.如何避免消息重复投递或重复消费
在消息生产时,MQ内部针对每条生产者发送的消息生成一个inner-msg-id,作为去重和幂等的依据(消息投递失败并重传),避免重复的消息进入队列;在消息消费时,要求消息体中必须要有一个对于同一业务全局唯一的id)作为去重和幂等的依据,避免同一条消息被重复消费。
业务场景来答分以下几点:
1)你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
2)你拿到这个消息做redis的set的操作,那就容易了,不用解决,因为你无论set几次结果都是一样的,set操作本来就算幂等操作。
3)准备一个第三方介质,来做消费记录。以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。
6.如何解决丢数据的问题
1)生产者丢数据
RabbitMQ提供transaction和confirm模式来确保生产者不丢消息。
transaction机制:发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。
confirm模式:一旦channel进入confirm模式,所有在该信道上面发布的消息都将会被指派一个唯一的ID(从1开始),一旦消息被投递到所有匹配的队列之后,rabbitMQ就会发送一个Ack给生产者(包含消息的唯一ID),这就使得生产者知道消息已经正确到达目的队列了.如果rabiitMQ没能处理该消息,则会发送一个Nack消息给你,你可以进行重试操作。
2)消息队列丢数据
处理消息队列丢数据的情况,一般是开启持久化磁盘的配置。这个持久化配置可以和confirm机制配合使用,你可以在消息持久化磁盘后,再给生产者发送一个Ack信号。这样,如果消息持久化磁盘之前,rabbitMQ阵亡了,那么生产者收不到Ack信号,生产者会自动重发。
3)消费者丢数据
自动确认模式:消费者挂掉,待ack的消息回归到队列中。消费者抛出异常,消息会不断的被重发,直到处理成功。不会丢失消息,即便服务挂掉,没有处理完成的消息会重回队列,但是异常会让消息不断重试。
手动确认模式:如果消费者来不及处理就死掉时,没有响应ack时会重复发送一条信息给其他消费者;如果监听程序处理异常了,且未对异常进行捕获,会一直重复接收消息,然后一直抛异常;如果对异常进行了捕获,但是没有在finally里ack,也会一直重复发送消息(重试机制)。
不确认模式:acknowledge=“none” 不使用确认机制,只要消息发送完成会立即在队列移除,无论客户端异常还是断开,只要发送完就移除,不会重发。
7.死信队列和延迟队列的使用
1)死信消息:
消息被拒绝(Basic.Reject或Basic.Nack)并且设置 requeue 参数的值为 false
消息过期了:在 rabbitmq 中存在2种方可设置消息的过期时间,第一种通过对队列进行设置,这种设置后,该队列中所有的消息都存在相同的过期时间,第二种通过对消息本身进行设置,那么每条消息的过期时间都不一样。如果同时使用这2种方法,那么以过期时间小的那个数值为准。当消息达到过期时间还没有被消费,那么那个消息就成为了一个死信消息。
队列达到最大的长度
2)延时队列:
设置消息的过期时间和死信队列来模拟出延时队列。消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。
整理借鉴了很多大佬写的,在此无法一一说明,这只是个人用来查漏补缺的文章,如果对你有帮助我很高兴。