消息队列
这是我参与「第五届青训营 」伴学笔记创作活动的第 17 天
引入
相信大家或多或少都听过消息队列这个组件,对于消息队列来说,在互联网的各种业务中发挥着至关重要的作用,但是在大学的课程中却少有提及,因此希望通过此次分享帮助大家去了解工业界比较成熟消息队列的底层原理、架构设计、以及它们的一些高级特性,让同学在以后的学习中或者工作中更加熟练地去使用消息队列或者进行技术选型。
案例一:系统崩溃
首先大家跟着我想象一下下面的这个的场景,有一天晚上晚上我们上完课回到宿舍,想着新出的游戏机,但又摸了摸钱包,太贵了买不起,这个时候你突然想到,今天抖音直播搞活动,瞬间你掏出了手机打开抖音搜索,找到直播间以后,你点开了心心念念的游戏机详情页,看到价格只要500。这个时候我们分析一下,就我们上面这几步操作,在我们的程序背后,做了什么事情。
首先,请求会先到搜索商品这个服务上,并记录下你的搜索行为,然后点击商品的时候,又记录了我们的点击商品,这些数据最终都会通过计算分析,目的是为了下一次给你更准确的信息,这个时候问题来了,如果这个时候,负责记录存储的数据库被一个小哥删库跑路了。我们的所有操作都动不了了,这个时候我们应该怎么办,带着这个问题,我们继续往下看。
案例二:服务能力有限
可以看到,一堆人都发起了订单请求,可是公司给的预算不够,服务器的配置太弱,订单服务只能同事处理10个订单请求。这个时候我们又该怎么办呢。
案例三:链路耗时长尾
提交订单后发现一直转圈圈,卡在这个页面。
案例四:日志存储
在大家都抢到了自己心仪商品准备睡去的时候,在字节跳动的会议室里传出了悲伤的声音,因为刚刚有服务器坏掉了,我们的本地日志都丢掉了,没有了日志,我们还怎么去修复那些刚刚出现的那些问题,周围一片寂静,突然小张站出来缓缓的说了一句话,众人才露出了微笑准备下班离开,大家能猜到小张到底说的什么吗。
案例一:解决方案—解耦
案例二:解决方案—削峰
案例三:解决方案—异步
案例四:解决方案—日志处理
什么是消息队列?
消息队列(MQ),指保存消息的一个容器,本质是个队列。但这个队列呢,需要支持高吞吐,高并发,并且高可用。
1 前世今生
1.1 消息队列发展历程
消息中间件其实诞生的很早, 早在1983年互联网应用还是一片荒芜的年代, 有个在美国的印度小哥Vivek就设想了一种通用软件总线,世界上第一个现代消息队列软件The Information Bus(TIB),他受到了企业的欢迎, 这家公司的业务发展引起了当时最牛气的IT公司IBM的注意, 于是他们开始研发了自己消息队列软件, 于是才有了后来的wesphere mq, 再后来微软也加入了战团。 接近2000年的时候, 互联网时代已经初见曙光, 全球的应用程序得到了 极大地丰富, 对于程序之间互联互通的需求越来越强烈, 但是各大IT公司之间还是牢牢建立着各种技术壁垒, 以此来保证自己的商业利益,所以消息中间件在那个时候是大型企业才能够用的起的高级玩意。
但是时代的洪流不可逆转,有壁垒就有打破壁垒的后来者,2001年sun发布了jms技术,试图在各大厂商的层面上再包装一层统一的 java规范。java程序只需要针对jms api编程就可以了,不需要再关注使用了什么样的消息中间件,但是jms仅仅适用于java。2004年AMQP(高级消息队列协议)诞生了,才是真正促进了消息队列的繁荣发展,任何人都可以针对AMQP的标准进行编码。有好的协议指导,再加上互联网分布式应用的迅猛发展成为了消息中间件一飞冲天的最大动力,程序应用的互联互通,发布订阅,最大契合了消息中间件的最初的设计初衷。除了刚才介绍过的收费中间件,后来开源消息中间件开始层出不穷,常见比较流行的有ActiveMQ、RabbitMQ、Kafak、阿里的RocketMQ,以及目前存算分离的Pulsar,在目前互联网应用中消息队列中间件基本上成为标配。
1.2 业界消息队列对比
Kafka:分布式的、分区的、 多副本的日志提交服务, 在高吞吐场景下发挥较为出色
RocketMQ:低延迟、强一致、高性能、高可靠、 万亿级容量和灵活的可扩展性,在一些 实时场景中运用较广
Pulsar:是下一代云原生分布式消息流平台, 集消息、存储、轻量化函数式计算为一体、 采用存算分离的架构设计
BMQ:和Pulsar架构类似,存算分离, 初期定位是承接高吞吐的离线业务场景, 逐步替换掉对应的Kafka集群
2 消息队列—Kafka
2.1 使用场景
2.2 如何使用Kafka
第一步:首先需要创建一个Kafka集群,但如果你是在字节工作,恭喜你这一步消息团队的小伙伴已经帮你完成了
第二步:需要在这个集群中创建一个Topic, 并且设置好分片数量
第三步:引入对应语言的SDK,配置好集群和Topic等参数,初始化一个生产者,调用Send方法,将你的Hello World发送出去
第四步:引入对应语言的SDK,配置好集群和Topic等参数,初始化一个消费者,调用Poll方法,你将收到你刚刚发送的Hello World
2.3 基本概念
Topic:逻辑队列, 不同 Topic 可以建立不同的 Topic
Cluster: 物理集群,每个集群中可以建立多个不同的 Topic
Producer:生产者,负责将业务消息发送到 Topic 中
Consumer: 消费者,负责消费 Topic 中的消息
ConsumerGroup:消费者组,不同组 Consumer 消费进度互不干涉
Offset:消息在 partition 内的相对位置信息,可以理解为唯一ID,在 partition 内部严格递增。
Replica:分片的副本, 分布在不同的机器上,可用来容灾, Leader对外服务,FOllower异步去拉取leader的数据进行一个同步,如果 leader挂掉了, 可以将Follower提升成leader再堆外进行服务
ISR:意思是同步中的副本,对于Follower来说, 始终和leader是有一定差距的, 但当这个差距比较小的时候,我们就可以将这个follower副本加入到ISR中, 不在ISR中的副本是不允许提升成Leader的
2.4 Kafka架构
2.5 重启操作
举个例子来说, 如果我们对一个机器进行重启 首先, 我们会关闭一个Broker, 此时如果该Broker上存在副本的Leader, 那么该副本将发生leader切换, 切换到其他节点上面并且在SR中的Follower副本, 可以看到图中是切换到了第二个Broker上面。而此时,因为数据在不断的写入, 对于刚刚关闭重启的Broker来说, 和新Leader之间一定会存在数据的滞后, 此时这个Broker会追赶数据,重新加入到ISR当中。当数据追赶完成之后,我们需要回切leader, 这一步叫做prefer leader, 这一步的目的是为了避免, 在一个集群长期运行后,所有的leader都分布在少数节点上, 导致数据的不均衡,通过上面的一个流程分析, 我们可以发现对于一个Broker的重启来说, 需要进行数据复制,所以时间成本会比较大, 比如一个节点重启需要10分钟,一个集群有1000个节点, 如果该集群需要重启升级, 则需要10000分钟, 那差不多就是一个星期,这样的时间成本是非常大的。
2.6 问题总结
- 运维成本高
- 对于负载不均衡的场景,解决方案复杂
- 没有自己的缓存,完全依赖 Page Cache
- Controller 和 Coordinator和Broker 在同一进程中,大量IO会造成其性能下降