Part1?为什么?1什么是消息队列说到Java中的队列,大家应该都不陌生了。具有先进先出或双端进出的数据管理功能;通过阻塞实现自动负载均衡。消息队列之所以以队列命名,最初是因为它的作用和操作,类似于java的本地队列。因此,我们可以简单地认为消息队列是分布式服务之间进行数据传输、管理和消费的中间服务。2为什么使用消息队列Q:为什么要在系统中引入消息队列?我们总是需要知道消息队列的使用价值和自身业务场景中的实际痛点,才能回答我们为什么要使用消息队列以及引入消息队列到系统中的价值的问题。系统间解耦前几天和一个关注公众号的大佬讨论了后台更新广告流的操作:广告检索系统需要感知广告帖的信息变化来更新自己的广告流自己的索引,但实际上检索系统和投放、物资、资产等系统之间的感知行为不需要依赖接口来强关联,接口的方式在维护和系统压力方面并不友好。那么,消息队列的作用就很重要了。每个系统发布自己的消息,谁需要订阅,就达到了目的,不需要增加额外的系统调用压力。(注:构建器的接口调用是获取最新信息,可以通过压缩等方式优化)因此,当系统之间没有实时数据交互需求,但又需要业务信息时,可以使用消息队列到达系统只要发布者定义了消息队列格式,消费者的任何操作都可以与发布者无关,减少不必要的联调和发布冲突。服务异步最典型的例子之一就是支付场景中的结果通知功能。我们知道,在正常情况下,无论是app推送还是短信通知,都是一个耗时的操作。因此,无需因为这些非核心功能的耗时操作而影响支付的核心操作。只要我们在支付操作完成后将支付结果发送到短信中心指定的消息主题,短信中心自然会收到这条消息并确保通知用户。图片来自知乎的回答。因此,利用消息队列让非核心操作异步化,提高整个业务环节的效率和稳定性是非常有效的。削峰填谷的功能是我们本文关注的重点。面对秒杀、春晚红包等万亿级流量的脉动压力等特殊场景,保护我们系统服务不崩溃的有效手段就是消息队列。.通过消息中心高性能的存储和处理能力,将超出系统处理能力的超额流量暂时存储起来,在系统处理能力范围内缓缓释放,达到调峰的效果。比如我们的广告计费系统,面对几万并发的商帖检索量和几千并发的点击操作,实时接口的方式肯定是不合适的。广告行为毕竟不同于支付行为,支付失败的用户还可以Retry,但是用户商业帖的点击行为无法回放,当前流量就结束了。因此,需要使用消息队列来缓存扣款请求,以保证计费系统的稳定性。广播、事务、最终一致性等其他特性也是消息队列经常使用的功能。3消息队列存在哪些问题增加业务的响应延迟前面提到,消息队列让非核心业务流程异步化,可以提高整个业务运行的及时性和流畅性,提升用户体验。但是,也正是因为数据进入了队列,消费速度必然会有所延迟。导致业务不能及时生效。比如在之前遇到的商品推荐中,满减、闪购的商品就不要出现在商品推荐列表中,这样可以消除特价商品对推荐效果的影响。除了秒杀,我们还需要感知商品的上架下架、黑名单、库存等。因此,redis中的位的多个偏移量用于维护一个产品的多个状态。然后收到推广组的消息改变推荐缓存集群中商品的状态,但是由于消息的延迟,可能会造成商品状态没有及时改变的情况。但是只要在trade-off下在业务和技术上是可以接受的,就OK。架构不稳定因素的引入消息队列的引入,相当于在原有的分布式服务链路上增加了一个新的系统,系统的复杂度也随之增加。同时,消息队列的作用要求其具有高性能和高可用性。因此,面对如何部署一个高可用稳定的集群,消息发送不成功如何重试,broker数据同步策略如何设置,broker异常如何重发消息,如何重发消息,消费不成功如何重试等,需要中间件团队。努力使用业务系统。Part2怎么样?4RocketMQ,连续7年支持双11零故障,2020年双11交易量峰值58.3W笔/秒,RocketMQ针对阿里交易生态做了很多深度定制。这里只介绍高可用的优化。个人认为推送消费模式只适用于消费速度远大于生产速度的场景。如果是大流量并发场景,Pull消费基本是主打。在拉取之前,broker和client会进行负载均衡,建立连接。那么一旦client挂了,(没有宕机就不会rebalance,即使宕机也是默认20s会rebalance),broker就会关联到client。队列消息不能及时消费,导致积压。Whattodo:POP,新的消费模式POP消费不需要rebalance分配消费队列,而是请求所有broker获取消息进行消费。broker内部会按照一定的算法将自己三个队列的消息分发给等待中的POPClient。即使PopClient2挂了,内部队列中的消息也会被PopClient1和PopClient2消费。这避免了消费积累。[1]5快手万亿级Kafka集群的平滑扩容[2]要想做到平滑,需要生产者实现无感分区迁移。总的原则是将待迁移分区的数据与新分区数据同步,并持续一段时间,直到所有消费者赶上同步起始节点,然后改变路由,删除原分区,完成移民。同样的数据同步思想也应用在facebook的分布式队列容灾方案中。6快手/美团对Kafka缓存污染的优化[3]Kafka的高性能来源于对顺序文件读写和操作系统缓存pagecache的支持。在单分区单消费者的场景下,Kafka表现非常出色。但是,如果同一台机器上有不同的分区,或者即使消费模式有实时和延迟消费的混合场景,也会存在PageCache资源竞争,造成缓存污染,影响broker服务的处理效率。美团响应实时/延迟消费缓存污染,将数据按时间维度分布在不同设备,将近实时数据缓存在SSD中,当PageCache竞争时,实时消费作业从SSD读取数据以确保实时作业不会受到延迟消费作业的影响。当消费请求到达Broker时,Broker根据消息偏移量和自己维护的设备的关系,直接从对应的设备上获取数据并返回,并不在读请求中从HDD中读取数据。检索到的数据被闪回SSD以防止缓存污染。同时访问路径清晰,不会因为CacheMiss而产生额外的访问开销。快手应对follower数据同步造成的缓存污染broker中引入了两个对象:一个是blockcache;另一个是刷新队列。producer的写请求首先会在broker端以原始消息的形式写入flushqueue,然后数据会写入blockcache的一个block,然后整个请求结束。flush队列中的数据会被其他线程异步写入磁盘(会经过pagecache过程)。为了保证队列不受follower的影响,consumer会先从blockcache中取数据,如果命中则直接返回。否则,数据从磁盘读取。这种读取方式保证了消费者的cachemissread不会填满blockcache,从而避免了污染。综上所述,我们可以看出,解决缓存污染的基本出发点是将不同消费速度或不同数据生产源的任务进行拆解,分而治之,避免相互缓存的影响。7CMQ在红包支付场景中的应用[4]红包操作??背后的流程简化为:从账户A中读取余额,然后进行减法,然后将结果写回账户A;然后打开红包,对B账户进行操作,将结果写入B账户。但是由于会计系统能够承载的压力有限(与会计相关的系统一般会因为锁、交易等影响处理效率)。),可能会导致入账失败。如果是基于实时业务逻辑,需要对红包的拆解进行实时回滚。(回滚需要另外添加A的账户),引入CMQ后,业务环节变为将失败的请求写入CMQ,利用CMQ的高可用保证数据的一致性,直到记账系统最终记账成功。简化了账务系统因系统压力导致入账失败导致红包账户回滚导致的额外系统操作。Part3总结本文从消息队列的作用出发,从阿里巴巴双11、快手、美团、微信红包等案例出发,阐述了消息队列本身的优化方案以及消息队列的高效使用以业务为中心,阐述了消息队列在高端并发优化场景中的作用。
