为什么要使用消息队列?
解耦、异步、削峰
mysql每秒写入2000就会出现瓶颈。
消息队列有什么优缺点?
系统可用性降低,如 mq挂了,整个系统就崩溃了;
系统复杂度提高,如怎么保障消息没有重复、顺序性、消息丢失问题;
一致问题,如a系统成功返回,但是bc 系统中 ,c处理失败。
如何保证消息队列的高可用性?
rabbitmq 分以下几种模式:
单机模式;
普通集群模式(无高可用性):
多台机器上启动多个实例,每个机器上启动一个。创建的queue,只会在一个rabbitmq实例上,但每台实例都会同步所有的queue的元数据(元数据就是queue的一些配置信息,通过元数据可以找到对应的queue实例)。当消费连到另外的B实例,那么B实例会从queue所在A实例上拉取数据过来,会导致数据拉取开销。当固定连接queue实例会导致单实例性能瓶颈。
镜像集群模式(高可用性):
每个实例上都有所有实例的全量的元数据和queue的消息。
好处--任何一台宕机都没影响,其他实例包含这个实例queue的完整实例。
坏处--性能开销太大,消息需要同步到所有实例上,导致带宽和消耗;没法线性扩展,不是分布式,新加机器也要包含所有的queue数据。
kafka高可用性
kafka基本组成:由多个broker组成,每个broker是一个节点;创建一个topic可以划分为多个partition,每个partition可以存在于不同的broker上,每个partition放一部分数据。
高可用机制:replica(复制品)副本机制。每个partition的数据都会同步到其他机器上,形成多个replica副本。所有replica会选举一个leader出来,生产和消费都跟这个leader打交道,然后其他replica是follower。写入时,leader会负责把数据同步到所有follower上去,读取时就直接读leader上数据。只读写leader,如果要随意读写每个follower,那么就得关心数据一致性的问题。kafka会均匀地将一个partition的所有replica分布在不同机器上,提供容错性。
kafka消费时,只会读leader,但是只有当一个消息被所有follower都同步成功返回ack的时候,这个消息才会被消费者读到。
如何保证消息不被重复消费?或者说,如何保证消息消费的幂等性?
rabbitmq、kafka都会出现消息重复的情况,这是正常的。我们应该在业务开发时保证消息不重复消费。
幂等性,指同一个请求给你重复多次,数据库得保证对应的数据不会改变,不能出错。解决此问题要结合业务来,比如:在生产者方给每条数据加一个全局变量id, 消费时在缓存里面检查一下是否已经消费过
如何保证消息的可靠性传输?或者说,如何处理消息丢失问题?
rabbitmq消息丢失:
生产者端:开启rabbitmq事务,(同步,不推荐);开启confirm模式(异步,推荐)
mq端:开启rabbitmq持久化;
消费者端:关闭rabbitmq自动ack;
kafka消息丢失:
消费者端:消费者自动提交了offset,让kafka以为已经消费好这个消息,但你才刚准备处理这个消息,但你还没来得及处理,自己就先挂掉。
kafka端:某个broker宕机,然后重选partition的leader,此时宕机,follower没同步完数据,然后再选举leader,会出现数据丢失。一般设置几个参数可以防止:
给topic设置 replication.factor参数:这个值大于1,且每个partition要有2个副本。
给kafka服务端设置min.insync.replicas参数:值大于1,这个值要求一个leader至少感应一个follower跟自己保持联系,这样才能在leader宕机时,还有一个follower数据是同步完成的。
在生产者端设置acks=all 参数: 要求每条数据,必须写入所有replica副本后,才可认为是写成功;
在生产者端设置retries=MAX参数:要求写失败后,进行无限重试。
如何保证消息的顺序性?
rabbitmq
拆分多个queue,每个queue一个consumer。
kafka
一个topic,一个partition,一个consumer,内部单线程消费,单线程吞吐低,一般不会用这个;
写N个内存queue,具体相同key的数据都到一个内存queue,然后对于N个线程,每个线程分别消费一个内存queue即可。
如何解决消息队列的延时以及过期失效问题?消息队列满了以后该怎么处理?有几百万消息持续积压几个小时,怎么解决?
大量消息积压,只能紧急扩容,如下思路:
先修复消费者问题,确保其恢复消费速度,然后将现有消费都停掉;
新建一个topic,partition是原来10倍,临时建立好原来10倍的queue数量;
然后写一个临时的分发数据的消费者程序,这个应用部署上去消费积压的数据,消费后不做耗时处理,直接均匀写入临时建好的10倍数量的queue。
接着临时征用10倍的机器来部署消费者,每批消费者消费一个临时queue的数据。相当于临时将queue和consumer资源扩大10倍,以正常10倍速度来消费数据。
等快速消费完积压数据之后,得恢复原先部署的架构,重新用原先的consumer机器来消费消息。
mq中消息过期失效
对于Rabbitmq可以设置过期时间TTL。消息过期大量的数据会被直接丢弃。可以采取批量重导,过了用户使用高峰,用新写的程序,将丢失的数据,一点点查找出来并重新灌入mq里,把丢失的补救回来。
mq快写满了
临时程序来消费,消费一个丢一个,快消费掉所有消息,走第二个方案,晚上补救数据。
如果让你写一个消息队列,该如何进行架构设计?谈下思路
消息队列系统,如下几个角度:
mq具备可伸缩性,支持快速扩容,随时增加吞吐量和容量。如kafka设计理念,broker->topic->partition,每个partition放一个机器,就存一部分数据。如果资源不够用就给topic增加partition,然后做数据迁移,增加机器,不就可以存放更多数据提高吞吐量了。
mq数据要落地磁盘,才能保证别的进程挂了数据不会就丢了。保证磁盘 顺序写 ,这样就没磁盘随机读写的寻址开销,磁盘顺序读写性能很高,这是kafka思路。
mq可用性,kafka的高可用性保障机制,多副本->leader&follower->broker挂了重新选举leader
mq支持0数据丢失。