目录
二、Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
一、使用rabbitmq优缺点
优点|
解耦:系统A在代码中直接调用系统B和系统C的代码,如果将D系统接入,系统A还需要修改代码,过于麻烦。
异步:将消息写入消息队列,非必要的业务逻辑以异步的方式进行运行,加快响应速度。
削峰:并发量大的时候,所有请求直接怼到数据库,造成数据库连接异常,可以采用延时队列的方式进行削峰。
缺点|
系统可用性降低,系统复杂度升高
二、Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
ActiveMq:没有经过大规模吞吐量场景的验证,社区也不是很活跃;
RabbitMq:基于erlang语言开发,研究源码很困难,但是确实人家是开源的,比较稳定的支持,活跃度也高;
RocketMq:RocketMQ是基于Java语言开发的,适合深入阅读源码,有需要可以站在源码层面解决线上生产问题,包括源码的二次开发和改造;
Kafka:Kafka的优势在于专为超高吞吐量的实时日志采集、实时数据同步、实时数据计算等场景来设计;
三、介绍一下rabbitmq的几种工作模式
(1)simple工作模式
生产者生产消息,将消息放到队列中,消费者消费消息(设置成手动的ack,但如果设置成手动ack,处理完后要及时发送ack消息给队列,否则会造成内存溢出)。
(2)work工作模式
消息生产者将消息放到队列中,消费者有多个同时监听同一个队列,消息消费者谁先拿到消息谁负责消费。隐患:高并发情况下,默认会产生某一个消息被多个消费者共同使用,可以设置一个开关(syncronize) 保证一条消息只能被一个消费者使用。
(3)发布订阅模式
每个消费者监听这自己的队列,生产者将消息发送到broker,由交换机将消息发送到各自绑定的队列中。
(4)路由模式
消息生产者按照routing key 发送消息到交换机,只有匹配上路由key对应的消息队列,对应的消费者才能消费消息
(5)主题模式
星号代表多个单词,井号代表一个单词;路由功能添加模糊匹配;
四、交换器类型有哪些
direct:如果路由键完全匹配,消息就被投递到相应的队列;
fanout:如果交换器收到消息,将会广播到所有绑定的队列上;
topic:可以使来自不同源头的消息能够到达同一个队列。 使用topic交换器时,可以使用通配符,比如:“*” 匹配特定位置的任意文本, “.” 把路由键分为了几部分,“#” 匹配所有规则等;
五、如何确保消息正确发送至RabbitMq
使用发送方确认模式,确保消息正确的发送到RabbitMq。发送方确认模式:将信道设置成confirm模式,则在信道上发布的消息都会被指派一个唯一Id。一旦消息正确写入到队列中或消息已被持久化,信道会发送一个确认给生产者(包含消息唯一Id);如果失败则,则发送一条nack信息。发送方确认模式是异步的,生产者在等待返回信息的同时,可以继续发送消息。
六、如何确保消息消费者消费了消息
接收方消息确认机制:消息接收每一条消息后都必须去确认。只有消费者确认了消息,RabbitMq才能安全的把消息从队列中删除;这里并没有用到超时机制,RabbitMQ仅通过Consumer的连接中断来确认是否需要重新发送消息。
(1)如果消费者接收到消息,在确认之前断开连接或取消订阅,RabbitMq会认为消息没有被分发,然后重新发送个给下一个订阅者。
(2)如果消费者接收到消息却没有确认消息,连接也为断开,则RabbitMq认为该消费者繁忙,不会给消费者分发更多的消息。
七、如何避免消息重复投递或重复消费
在消息生产时,Mq针对每个消息生成一个inner_msg_id,作为去重和幂等的依据,避免重复的消息进入队列;在消息消费时,必须要求消息体中必须有一个bizId(对于同一业务全局唯一,如支付ID、订单ID、帖子ID等)作为去重和幂等的依据,避免同一条消息被重复消费。
(1).比如,你拿到这个消息做数据库的insert操作。那就容易了,给这个消息做一个唯一主键,那么就算出现重复消费的情况,就会导致主键冲突,避免数据库出现脏数据。
(2)以redis为例,给消息分配一个全局id,只要消费过该消息,将<id,message>以K-V形式写入redis。那消费者开始消费前,先去redis中查询有没消费记录即可。
八、如何解决丢数据的问题
(1)生产者丢失数据:
1)发送方确认机制,将信道设置confirm模式,一旦信道进入confirm模式,所有信道上面的发布的消息都会被指派唯一的Id,且成功投递,rabbitMq会发送一个确认Basic.ACK(包含唯一的Id),丢失就发送nack。
2)事务机制:发送消息前,开启事物(channel.txSelect()),然后发送消息,如果发送过程中出现什么异常,事物就会回滚(channel.txRollback()),如果发送成功则提交事物(channel.txCommit())。
(2)消息队列丢失数据:开启持久化磁盘配置+confirm机制配合使用
可以在消息持久化到磁盘后,再给生产者发送一个ACK信号,如果消息持久化磁盘之前,rabbitmq阵亡那么生产者收不到ACK信号,生产者会自动重发。
设置:将queue持久化标识durable=true/发送消息将deliveryMode=2/消息还没持久化到磁盘时,可能服务已经死掉了,引入mirrored-queue镜像队列
(3)消费者丢失数据
手动确认模式:如果消费者来不及处理就死掉时,没有响应ack时会重复发送一条信息给其他消费者进行消费;如果监听程序处理异常了,且未对异常进行捕获,会一直重复接收消息,然后一直抛异常;如果对异常进行了捕获,但是没有在finally里ack,也会一直重复发送消息(重试机制);每次你确定处理完一条消息,在发送ack消息给rabbitmq。
九、死信队列和延迟队列的使用
死信队列产生的条件:(1)消息被拒绝,并且required=false;(2)消息ttl过期;(3)队列达到最大长度
解决办法:通过死信队列,将负责监听死信的应用程序进行处理,将产生死信通过程序的配置路由到指定的死信队列,然后监听死信队列,将接收到的死信做后续处理。
延时队列:消费者监听死信交换器绑定的队列,而不要监听消息发送的队列。