优点:
1:解耦
业务场景:A系统做数据修改,需要通知WEB端服务,APP服务,搜索服务,A系统向MQ中生产一条消息(sub/pub模式),多个系统对当前MQ队列订阅,后期不同系统按照自身业务,从MQ中消费消息,完成自身业务。这样对A系统完成解耦,不需要考虑调用其他多个系统,代码层面会简单很多。
2:异步
好处:异步化可以大幅度提升高延时系统的性能。
业务场景:A系统,因为业务需要,需要同时调用B、C、D三个系统,这样整个接口的调用时间为ABCD4个系统的耗时之和,使用异步化操作,只需要A系统把消息发送到对应系统的MQ队列即可,整个接口的调用时间只是A系统的耗时。
3:削峰填谷
业务场景:不使用MQ,系统访问高峰期,系统处于高并发阶段,系统会被打死。比如中午大量人开始点外卖,但是到了下午,请求又开始回到很低的请求。
使用MQ,大量用户请求100W,系统的处理请求为2000/s请求,远低于用户请求,大量的用户请求写入MQ中,系统从MQ中按照顺序拉取请求,保证系统不会挂掉。
缺点:
-
系统可用性降低,多了MQ环节,如果MQ所在机器宕机,A系统就无法发送消息到MQ中,即使BCD系统正常,也无法消费MQ中的消息,系统就不可用,崩溃。
-
系统要考虑的问题变多,进而导致系统复杂性变高。
(1)消息重复消费/幂等性
MQ出现消息重复属于正常情况。 以kafka为例: 由于消费者是定期提交offset,而不是每次提交offset(重点,原因),这就导致消费者机器重启,而此时,消息已经被消费,但是还没来得及提交offset,重启完成,kafka不知道已经消费了,会继续按照之前的offset开始,推送后面的消息给消费者,此时就存在了消息的重复消费。 解决方法,防止重复消费,保证幂等性:(必须按照实际业务思考如何保证幂等性) 1):musql插入的话,插入之前先查询是否存在,存在则更新,不存在则插入。 2):redis的话,直接set即可,redis的set具有天然幂等性。 3):复杂一点的业余,则可以基于唯一键约束,每次消费,先根据唯一键查询。
(2)消息丢失
rabbitMQ: 1)生产者弄丢了消息,比如网络原因 解决方案1:rabbitMQ支持消息的事务,消息会同步阻塞,造成MQ吞吐量降 低,影响性能。 2:生产者设置channel为confirm模式(常用模式),异步模式, 不会阻塞。 2)消息在MQ中,消息还没有来得及消费,MQ机器宕机,内存中的消息就弄丢了 解决方案: 消息持久化到磁盘中,即使MQ机器挂掉,重启服务,也可以从磁盘中恢复queue,恢复queue中的数据。 还有一点风险会丢失数据,那就是虽然开启了持久化,数据还没来得及持久化到磁盘,MQ所在的机器就挂掉了。 两步操作: 1)创建queue的时候,设置为持久化 2)发送消息的时候,deliveryMode设置为2 3)消费者消费到了消息,但是还没来得及消费,自己就挂掉了,MQ以为消费者已经消费了。消费者机器重启,MQ会继续提供后面的消息,消费者当时消费的那条消息已经丢失了。 这种情况,只存在消费者打开了autoAck模式,消费者接收到消息,就返回给MQ,表示自己已经消费正常,但是此时消费者所在机器宕机了,那消息就丢失了。 解决方案: 消费者关闭autoAck模式 kafka: 1)消费端弄丢消息 消费者自动提交offset,kafka以为已经成功消费了消息,但是此时消费者宕机了,当前消息丢失,重启服务,kafka会从开始发送新的消息给消费者。和rabbitMQ类似 解决方案: 关闭消费者自动提交offset 2)kafka自己丢失数据 kafka的leader宕机,副本数据还没来得及同步数据到follower,剩下的follower重新选举leader,此时消息已经丢失,新的leader中没有当前消息。 解决方案: 1)kafka端,设置replication.factor > 1 多副本 2)kafka端,min.insync.replicas > 1 至少保证leader和一个follower保持联系 3)生产者端,acks=all,保证每条数据都写入成功 retries=MAX,写入失败,无限重试。 3)kafka的生产者 按照上述情况,不存在数量丢失,按照上述配置,kafka全链路都不会数据丢失。
(3)消息顺序乱序
// TODO
(4)消息积压
// TODO
-
一致性问题:
正常情况,是系统A向BCD系统,发送请求,BCD系统都会执行成功,结果,BC系统执行正常,D系统执行失败,这种情况,则当前请求就是失败的,如何操作?
MQ的技术选型:
中小型公司,使用rabbitMQ,吞吐量达到万级别,功能完备,开源提供的管理界面完备,社区活跃。
中大型公司,选择rocketMQ,吞吐量达到几十万级别,分布式,扩展方便,社区稳定,支撑大规模的topic数量,阿里出品,有保障。
大数据相关的,不管公司规模,全部使用kafka,用于大数据领域的实时计算,日志采集。
MQ如何保证高可用:
rabbitMQ:
1:单机模式
2:集群模式:
运行原理:MQ所在的queue数据是存放在集群中的某台机器上,其他机器上则存放queue的元数据,用户请求某个queue,访问任意集群中的机器,MQ会查找元数据,找到queue存放的机器,通过内部请求,拿到queue,转发给消费方,进而消费消息。
特点:只是提高了MQ的吞吐量,没有高可用,不是分布式的。
缺点:1)rabbitMQ内部产生大量的数据传输。
2)没有任何高可用而言,如果queue所在的节点宕机,那么当前的这个queue的数据就会丢失了,无法消费。
3:镜像集群模式:
运行原理:queue同步到所有机器上,好处是一台机器宕机,不影响系统使用,缺点是机器性能开销大,同步所有消息,导致网络带宽压力和消耗很重。还有就是没有扩展性可言,新增机器,也会同步所有的消息。针对这个,可以在rabbitMQ的管理界面,新增策略,同步指定数量的节点。
kafka:
生产者生产的消息,会被存放在kafka中,多台机器中的topic的partition中,并且在其他机器中有replica至少一个副本,类似于solr集群高可用的方案。在这些副本中,会通过选举,选举出一个leader,所有的消息的读写都是通过leader来进行的。此时,即使一台机器宕机,也不影响消息的读写,如果宕机机器正好是leader,则其他副本重新选举,产生一个leader,其他继续为保持副本身份。
leader写数据时,leader将数据写入本地磁盘,其他follower会主动从leader来pull(拉)数据,follower同步完成数据,会向leader发送ack(回复),leader接受到全部的follower的ack之后,才会向生产者返回写入成功。必须所有的follower同步成功,这个消息才是写入成功,否则无法被消费者读到。
目前该高可用方案的类似技术:solr,ES,zookeeper