1、消息队列的使用场景(作用、优点)
消峰
异步
解耦
2、消息队列会带来哪些缺点
系统可用性降低:
需要保证MQ不能挂掉系统复杂度提高:
引入MQ,怎么保证消息的重复消费?怎么处理消息丢失的情况?一致性问题:
A服务发送的消息被BCD消费,BC成功D失败,导致数据不一致。
Kafka、ActiveMQ、RabbitMQ、RocketMQ 有什么优缺点?
特性 | ActiveMQ | RabbitMQ | RocketMQ | Kafka |
---|---|---|---|---|
单机吞吐量 | 万级,比 RocketMQ、Kafka 低一个数量级 | 同 ActiveMQ | 10 万级,支撑高吞吐 | 10 万级,高吞吐,一般配合大数据类的系统来进行实时数据计算、日志采集等场景 |
topic 数量对吞吐量的影响 | topic 可以达到几百/几千的级别,吞吐量会有较小幅度的下降,这是 RocketMQ 的一大优势,在同等机器下,可以支撑大量的 topic | topic 从几十到几百个时候,吞吐量会大幅度下降,在同等机器下,Kafka 尽量保证 topic 数量不要过多,如果要支撑大规模的 topic,需要增加更多的机器资源 | ||
时效性 | ms 级 | 微秒级,这是 RabbitMQ 的一大特点,延迟最低 | ms 级 | 延迟在 ms 级以内 |
可用性 | 高,基于主从架构实现高可用 | 同 ActiveMQ | 非常高,分布式架构 | 非常高,分布式,一个数据多个副本,少数机器宕机,不会丢失数据,不会导致不可用 |
消息可靠性 | 有较低的概率丢失数据 | 基本不丢 | 经过参数优化配置,可以做到 0 丢失 | 同 RocketMQ |
功能支持 | MQ 领域的功能极其完备 | 基于 erlang 开发,并发能力很强,性能极好,延时很低 | MQ 功能较为完善,还是分布式的,扩展性好 | 功能较为简单,主要支持简单的 MQ 功能,在大数据领域的实时计算以及日志采集被大规模使用 |
3、kafka目前的使用场景:
应用日志收集分析、消息系统、用户行为分析、运行指标、流式处理(Spark、storm)
4、kafka的高吞吐原因:
1. 顺序读写
kafka的消息是不断追加到文件中的,这个特性使kafka可以充分利用磁盘的顺序读写性能,
顺序读写不需要硬盘磁头的寻道时间,只需很少的扇区旋转时间,所以速度远快于随机读写
2. 零拷贝
3. 批量发送
kafka允许进行批量发送消息,producter发送消息的时候,可以将消息缓存在本地,等到了固定条件发送到kafka
(等消息条数到固定条数,一段时间发送一次)
5、消息发送的三种确认方式(发送端可靠性)
生产者发送消息到broker,有三种确认方式:
- acks = 0:producer不会等待broker(leader)发送ack 。效率高,但可能丢失也可能会重发数据
- acks = 1:当leader收到消息后会发送ack确认收到(follower不管),如果丢失则会重发。
- acks = -1:需要等所有follower都同步消息成功后在返回ack,可靠性最高。
6、消息存储方式(存储端可靠性)
每一条消息被发送到broker中,会根据partition规则选择被存储到哪一个partition。如果partition规则设置的合理,所有消息可以均匀分布到不同的partition里,这样就实现了水平扩展。
在创建topic时可以指定这个topic对应的partition的数量。在发送一条消息时,可以指定这条消息的key,producer根据这个key和partition机制来判断这个消息发送到哪个partition。
如果Broker有多台负载,那么topic的partition会平均分配到每一台上,所以发送的消息也会平均分配到每一台上。
但是这样带来个问题:如果某个Broker单点故障了,这台broker上的partition就不可用了;
kafka的高可靠性的保障来自于另一个叫副本(replication)策略,通过设置副本的相关参数,可以使kafka在性能和可靠性之间做不同的切换。
7、副本机制
sh kafka-topics.sh --create --zookeeper 192.168.11.140:2181 --replication-factor 3 --partitions 3 --topic MyTopic
--replication-factor表示的副本数
ISR(副本同步队列)
维护的是有资格的follower节点
副本的所有节点都必须要和zookeeper保持连接状态
副本的最后一条消息的offset和leader副本的最后一条消息的offset之间的差值不能超过指定的阀值,这个阀值是可以设置的(replica.lag.max.messages)
10. 如何保证消息没有被重复消费
为什么会出现消息被重复消费
其中很大的一个原因就是网络不稳定,比如kafka在处理完消息准备commit时出现网络波动(或者重启了)提交失败,那么服务端是不知道这条消息已经被消费了.
怎么避免重复消费
没有根本的解决办法,只能从以下两个层面尽量规避:
消费端处理消息的业务逻辑保持幂等性
就是不管来多少重复消息,最终的处理结果都一样,比如:一个金额累加的消息,不能设计成每次只传一个增加的金额,这样同一个消息重复N次,那么总金额也会累加N次;应该在消息体里传入增加的金额和增加后的金额.保证每条消息都一个唯一编号并且消息的处理成功和去重表的记录同时出现
消费端每消费一条消息记录一次,可以通过数据库的唯一索引,如果这个编号是自增的可以通过redis等方式,同一条消息消费时,查询数据库发现已经消费则过滤掉;这种方式势必会对消息队列的吞吐量和性能带来影响,如果不是特别重要的业务可以不用处理,因为重复消息的概率很低.
11. 如何处理消息丢失的问题
为什么会出现消息丢失
消息丢失的问题,可能出现在生产者、MQ、消费者,以RabbitMQ为例:
消费端弄丢了数据
出现的情况可能是由于消费端获得了消息后自动提交了offset,让kafka以为你已经消费好了这条消息,但是你正准备处理这条消息时,挂掉了,消息就丢失了.
解决办法是:关闭自动提交offset,在出来完之后手动提交;
这可能带来重复消费的问题,所以必须要的话,需要自己保证幂等性.
kafka弄丢了数据
出现的情况可能是由于某个broker挂掉了,然后重新选举partition的leader,但是有可能follower数据还没同步好,这时候就会丢数据
解决办法是:生产端设置acks=all,即必须是写入所有 replica 之后,才能认为是写成功了;然后设置retries=MAX,要求一旦写入失败,就无限重试,卡在这里了;当然这会影响kafka写的性能.
生产端弄丢了数据
如果按照上述的思路设置了 acks=all,一定不会丢
12. 让你来设计一个消息队列,你会怎么做?
- 首先这个 mq 得支持可伸缩性吧,就是需要的时候快速扩容,就可以增加吞吐量和容量,那怎么搞?设计个分布式的系统呗,参照一下 kafka 的设计理念,broker -> topic -> partition,每个 partition 放一个机器,就存一部分数据。如果现在资源不够了,简单啊,给 topic 增加 partition,然后做数据迁移,增加机器,不就可以存放更多数据,提供更高的吞吐量了?
- 其次你得考虑一下这个 mq 的数据要不要落地磁盘吧?那肯定要了,落磁盘才能保证别进程挂了数据就丢了。那落磁盘的时候怎么落啊?顺序写,这样就没有磁盘随机读写的寻址开销,磁盘顺序读写的性能是很高的,这就是 kafka 的思路。
- 其次你考虑一下你的 mq 的可用性啊?这个事儿,具体参考之前可用性那个环节讲解的 kafka 的高可用保障机制。多副本 -> leader & follower -> broker 挂了重新选举 leader 即可对外服务。
- 能不能支持数据 0 丢失啊?可以的,参考我们之前说的那个 kafka 数据零丢失方案。
13. 事务消息
13.1 Rocketmq
RocketMQ 的事务消息也可以被认为是一个两阶段提交,简单的说就是在事务开始的时候会先发送一个半消息给 Broker。
半消息的意思就是这个消息此时对 Consumer 是不可见的,而且也不是存在真正要发送的队列中,而是一个特殊队列。
发送完半消息之后再执行本地事务,再根据本地事务的执行结果来决定是向 Broker 发送提交消息,还是发送回滚消息。
1、生产者发送事务消息给 broker时序
2、broker发送消息给生产者询问本地事务是否执行成功,生产者告知broker本地事务执行结果