高并发系列:架构优化之从BAT实际案例看消息中间件的妙用
The following article is from Coder的技术之路 Author Coder的技术之路
本文内容预览:
是什么?为什么?
1.1 什么是消息队列
1.2 为什么要使用消息队列
1.3 引入消息队列的带来了哪些问题怎么样?
2.1 支撑七年双11零故障的RocketMQ
2.2 快手万亿级kafka集群的平滑扩容
2.3 快手/美团对kafka缓存污染的优化
2.4 CMQ在微信红包支付场景下的应用
Part1是什么?为什么?
1什么是消息队列
说到Java中的队列应该都不会陌生。其具有通过先进先出,或者双端进出的方式进行数据管理;通过阻塞以达到自动平衡负载的功能。
消息队列之所以以队列命名,起初也是因为其功能和操作,和java的本地队列有相似之处。所以,我们可以简单的认为消息队列就是为了满足分布式下各服务之间的数据传输、管理和消费的一种中间服务。
2为什么要使用消息队列
问:你们的系统中为什么要引入消息队列?
我们总归需要知晓消息队列的使用价值,以及自己的业务场景下的实际痛点才能回答为什么要用消息队列这个问题,才能回答系统引入消息队列的价值所在。
系统间解耦
以前几天在后台和关注公号的一个大佬讨论的广告流水更新的操作为例:
因此,当系统间无实时数据交互要求,但还需要其业务信息时,可以用消息队列来达到系统间解耦的作用,只要发布方定义好消息队列格式,消费方的任何操作均可和发布方无关,减少了不必要的联调和发布冲突等影响。
服务异步化
最典型的一个例子,就是支付场景下的结果通知功能。
我们知道,一般情况下不管是app push 还是短信通知,都是比较耗时的操作。所以,没有必要因为这些非核心功能的耗时操作而影响了支付的核心操作,只要我们在支付操作完成之后,将支付结果发到短信中心指定的消息topic下,短信中心自然会接收到此消息并保证通知给用户。
因此使用消息队列,让非核心的操作异步化,提高整个业务链路的高效和稳定,是很有效的。
削峰填谷
这个功能使我们本篇关注的重点,面对特殊场景如秒杀、春晚红包等万亿级流量的脉冲式压力下,一种保护我们系统的服务免于崩溃的有效手段就是消息队列。
通过消息中心高性能的存储和处理能力,将超过系统处理能力的多余流量暂时存储起来,并在系统处理能力内平缓释放出来,达到削峰的效果。
比如我们的广告计费系统,面对上万并发的商业贴检索量,数千并发的点击操作,实时接口的方式一定是不合适的,毕竟广告行为和支付行为不一样,支付失败用户还可以重试,但用户的商业贴点击行为是不可回放的,本次流量过去就过去了,因此,需要利用消息队列将扣费请求缓存下来,来保证计费系统的稳定。
其他
还如广播、事务型、最终一致性等特性,也是消息队列经常用到的功能。
3消息队列会存在哪些问题
业务上增加响应延迟
前面提到,消息队列使得业务非核心流程异步化,可以提高整个业务操作的时效性和流畅度,提升用户操作体验。但,也是因为数据进入队列的原因,不可避免的会耽搁消费速度。导致业务生效不及时。
比如,之前遇到的商品推荐,产品要求推荐列表中不能出现满减秒杀的商品,以消除特殊商品对推荐效果产生影响。除了秒杀,我们还需要感知商品的上下架、黑名单、库存等等,因次,用redis中的bit多个偏移量来维护一个商品的多个状态。然后接收促销组的消息来变更推荐缓存集群中的商品状态,但由于消息的延迟,就有可能导致商品状态变更不及时的情况发生。不过只要权衡之下业务和技术上是可接受的就OK了。
架构上引入不稳定因素
消息队列的引入,相当于在原有的分布式服务链路中新增了一个系统,系统复杂度也随之变大了。同时,消息队列的作用要求其具有高性能和高可用。
所以,面对怎样部署高可用稳定集群、消息发送不成功怎么重试、broker数据同步策略怎么设置、broker异常导致消息重发怎么幂等、消费不成功怎么重试等等问题,需要中间件团队和业务系统一起努力应对。
Part2怎么样?
4支撑七年双11零故障的RocketMQ
2020 年双十一交易峰值达到 58.3W 笔/秒。RocketMQ为了阿里的交易生态有很多深度定制,这里我们只介绍其中针对高可用的优化。
个人理解,push消费模式只适合于消费速度远大于生产速度的场景,如果是大流量并发场景,基本还是以Pull消费为主。
而pull前broker和client间会进行负载均衡建立连接,那么,一旦Client被Hang住,(没有宕就不会rebalance,即时宕机也是默认20s才会rebalance),就会让broker与该client关联的队列消息无法及时被消费,导致积压。怎么办:POP,新的消费模式
<<< 左右滑动见更多 >>>
POP 消费中并不需要rebalance去分配消费队列,取而代之的是请求所有的 broker 获取消息进行消费。broker 内部会把自身的三个队列的消息根据一定的算法分配给等待的 POPClient。即使 PopClient 2 出现 hang,但内部队列的消息也会让 Pop Client1 和 Pop Client2 进行消费。这样避免了消费堆积。[1]
5快手万亿级kafka集群的平滑扩容[2]
要实现平滑,则需要让producer无感的实现partition迁移。
大致原理是将待迁移partition的数据和新的partition数据进行同步并持续一段时间,直到消费者全部赶上同步的开始节点,然后再变更路由,删除原partition,完成迁移。
<<< 左右滑动见更多 >>>
相同的数据同步思路,在facebook的分布式队列灾备方案上也有应用。
6快手/美团对kafka缓存污染的优化[3]
kafka的高性能,来源于顺序文件读写和操作系统缓存pagecache的支持,在单partition,单consumer的场景下,kafka表现的非常优秀。但是,如果同一机器上,存在不同的partition,甚至,消费模式有实时和延迟消费的混合场景,将会出现PageCache资源竞争,导致缓存污染,影响broker的服务的处理效率。
美团应对实时/延迟消费缓存污染
将数据按照时间维度分布在不同的设备中,近实时部分的数据缓存在 SSD 中,这样当出现 PageCache 竞争时,实时消费作业从 SSD 中读取数据,保证实时作业不会受到延迟消费作业影响
快手应对follower数据同步引起的缓存污染
Producer 的写入请求在 broker 端首先会被以原 message 的形式写入 flush queue 中,之后再将数据写入到 block cache 的一个 block 中,之后整个请求就结束了。在 flush queue 中的数据会由其他线程异步地写入到磁盘中(会经历 page cache 过程)。保证queue不受follower的影响
consumer 首先会从 block cache 中检索数据,如果命中,则直接返回。否则,则从磁盘读取数据。这样的读取模式保障了 consumer 的 cache miss 读并不会填充 block cache,从而避免了产生污染。
总结
我们可以看出,解决缓存污染的基本出发点,还是要拆解不同消费速度的任务、或不同的数据生产来源,分而治之的思路避免相互间缓存的影响。
7CMQ在红包支付场景下的应用[4]
而由于账务系统能承载的压力有限(和账务相关的系统一般都会由于锁、事务等原因影响处理效率),可能导致入账失败,如果按实时业务逻辑,则需要对拆红包进行实时回滚(回滚需要对A的账户再进行一次加法),而引入CMQ后,业务链路变成将失败的请求写入CMQ,由CMQ的高可用来保证数据一致,直到账务系统最终入账成功。简化了账务系统由于系统压力而导致的入账失败而导致红包账务回滚带来的额外系统操作。
Part3总结
本篇从消息队列的作用出发,从阿里双11、快手、美团、微信红包等案例,就消息队列本身的优化方案和业务对消息队列的高效利用,阐述了消息队列在高并发的优化场景下的作用。如有问题,欢迎留言讨论,相互学习。
附:高并发系列历史文章目录
垂直性能提升
1.1. 架构优化:集群部署,负载均衡
1.2. 万亿流量下负载均衡的实现
1.3. 本文
参考资料
版权声明:本文为CSDN博主「阿里巴巴云原生」的原创文章,遵循CC 4.0 BY-SA版权协议: https://blog.csdn.net/alisystemsoftware/article/details/111314602
[2]快手万亿级别 Kafka 集群应用实践与技术演进之路: https://www.infoq.cn/article/Q0oQzLQiay31MWiOBJH*
[3]美团把 Kafka 作为应用层缓存的实践: https://www.infoq.cn/article/k6dqfqqihpjfepl3y3hs
[4]春晚微信红包案例: https://cloud.tencent.com/document/product/406/4789
负载均衡续:万亿流量场景下的负载均衡实践
那个割肾换iPhone的男生,现在怎么样了
阿里面试,拿到 P7 offe
哈哈哈,发年终奖啦,我要去斗鱼打赏最喜欢的妹子
加入字节跳动 455 天,谈一谈我的感受
HR 给 15 k,我如何谈到 25 k?
你伤害了我,却一笑而
内推字节 Linux C/C++ 开发的那位同学没通过面试
加入字节跳动 455 天,聊一聊我的感受
我卖掉了北京五百万的房子,在老家生活这两年......
为什么美国程序员不用加班,而中国程序员只能 996?
往期精华文章汇总
点赞、在看少个bug