【高并发】为什么高并发系统中都要使用消息队列?此次完全懂了!

写在前面

不少高并发系统中都会使用到消息队列中间件,那么,问题来了,为何在高并发系统中都会使用到消息队列中间件呢?立志成为资深架构师的你思考过这个问题吗?前端

本文集结了众多技术大牛的编程思想,由冰河汇聚并整理而成,在此,感谢那些在技术发展道理上默默付出的前辈们!数据库

场景分析

如今假设这样一个场景,用户下单成功须要给用户发短信,若是没有消息队列,咱们会选择同步调用发短信的接口并等待短信发送成功。如今假设短信接口实现出现了问题或者短信发送短期内达到了上限,这个时候是选择重试几回仍是放弃发送呢?这里的设计会很复杂。若是使用了消息队列,咱们选择将发短信的操做封装成一条消息发送到消息队列,消息队列通知一个服务去发送一条短信,即便出现了上述的问题,能够选择把消息从新放到消息队列里等待处理。编程

消息队列的好处

经过上述了例子,咱们看到消息队列完成了一个异步解耦的过程,短信发送时咱们只要保证短信发到消息队列成功就能够了,接下来就能够去作别的事情;其次,设计变得更简单,在下单的场景下,咱们不用过多考虑发送短信的问题,交给消息队列管理就好了,可能短信发送会有延迟,可是保证了最终的一致性。微信

消息队列特性

  • 业务无关,只作消息分发。
  • FIFO,先投递先到达。
  • 容灾:节点动态增删和消息持久化。
  • 性能:吞吐量提高,系统内部通讯效率提升。

高并发系统为什么使用消息队列?

(1)业务解耦网络

成功完成了一个异步解耦的过程。短信发送时只要保证放到消息队列中就能够了,接着作后面的事情就行。一个事务只关心本质的流程,须要依赖其余事情可是不那么重要的时候,有通知便可,无需等待结果。每一个成员没必要受其余成员影响,能够更独立自主,只经过一个简单的容器来联系。架构

对于咱们的订单系统,订单最终支付成功以后可能须要给用户发送短信积分什么的,但其实这已经不是咱们系统的核心流程了。若是外部系统速度偏慢(好比短信网关速度很差),那么主流程的时间会加长不少,用户确定不但愿点击支付过好几分钟才看到结果。那么咱们只须要通知短信系统“咱们支付成功了”,不必定非要等待它处理完成。并发

(2)最终一致性负载均衡

主要是用记录和补偿的方式来处理;在作全部的不肯定事情以前,先把事情记录下来,而后去作不肯定的事,它的结果一般分为三种:成功,失败或者不肯定;若是成功,咱们就能够把记录的东西清理掉,对于失败和不肯定,咱们能够采用定时任务的方式把全部失败的事情从新作一遍直到成功为止。异步

保证了最终一致性,经过在队列中存听任务保证它最终必定会执行。分布式

最终一致性指的是两个系统的状态保持一致,要么都成功,要么都失败。固然有个时间限制,理论上越快越好,但实际上在各类异常的状况下,可能会有必定延迟达到最终一致状态,但最后两个系统的状态是同样的。
业界有一些为“最终一致性”而生的消息队列,如Notify(阿里)、QMQ(去哪儿)等,其设计初衷,就是为了交易系统中的高可靠通知。

以一个银行的转帐过程来理解最终一致性,转帐的需求很简单,若是A系统扣钱成功,则B系统加钱必定成功。反之则一块儿回滚,像什么都没发生同样。

然而,这个过程当中存在不少可能的意外:

  • A扣钱成功,调用B加钱接口失败。
  • A扣钱成功,调用B加钱接口虽然成功,但获取最终结果时网络异常引发超时。
  • A扣钱成功,B加钱失败,A想回滚扣的钱,但A机器down机。

可见,想把这件看似简单的事真正作成,真的不那么容易。全部跨JVM的一致性问题,从技术的角度讲通用的解决方案是:

  • 强一致性,分布式事务,但落地太难且成本过高。
  • 最终一致性,主要是用“记录”和“补偿”的方式。在作全部的不肯定的事情以前,先把事情记录下来,而后去作不肯定的事情,结果多是:成功、失败或是不肯定,“不肯定”(例如超时等)能够等价为失败。成功就能够把记录的东西清理掉了,对于失败和不肯定,能够依靠定时任务等方式把全部失败的事情从新搞一遍,直到成功为止。

回到刚才的例子,系统在A扣钱成功的状况下,把要给B“通知”这件事记录在库里(为了保证最高的可靠性能够把通知B系统加钱和扣钱成功这两件事维护在一个本地事务里),通知成功则删除这条记录,通知失败或不肯定则依靠定时任务补偿性地通知咱们,直到咱们把状态更新成正确的为止。

消息可能重复,注意消息的重复和幂等。

(3)广播

若是没有消息队列,每当一个新的业务接入时,咱们都须要链接一个新接口;有了消息队列,咱们只须要关系消息是否送到到消息队列,新接入的接口订阅相关的消息,本身去作处理就好了。

(4)错峰与流控

利用消息队列,转储两个系统的通讯内容,并在下游系统有能力处理这些消息的时候再处理这些消息。试想上下游对于事情的处理能力是不一样的。好比,Web前端每秒承受上千万的请求,并非什么神奇的事情,只须要加多一点机器,再搭建一些LVS负载均衡设备和Nginx等便可。但数据库的处理能力却十分有限,即便使用SSD加分库分表,单机的处理能力仍然在万级。因为成本的考虑,咱们不能奢求数据库的机器数量追上前端。

这种问题一样存在于系统和系统之间,如短信系统可能因为短板效应,速度卡在网关上(每秒几百次请求),跟前端的并发量不是一个数量级。但用户晚上个半分钟左右收到短信,通常是不会有太大问题的。若是没有消息队列,两个系统之间经过协商、滑动窗口等复杂的方案也不是说不能实现。但系统复杂性指数级增加,势必在上游或者下游作存储,而且要处理定时、拥塞等一系列问题。并且每当有处理能力有差距的时候,都须要单独开发一套逻辑来维护这套逻辑。因此,利用中间系统转储两个系统的通讯内容,并在下游系统有能力处理这些消息的时候,再处理这些消息,是一套相对较通用的方式。

总结

总而言之,消息队列不是万能的。对于须要强事务保证并且延迟敏感的,RPC是优于消息队列的。

对于一些无关痛痒,或者对于别人很是重要可是对于本身不是那么关心的事情,能够利用消息队列去作。

支持最终一致性的消息队列,可以用来处理延迟不那么敏感的“分布式事务”场景,并且相对于笨重的分布式事务,多是更优的处理方式。

当上下游系统处理能力存在差距的时候,利用消息队列作一个通用的“漏斗”。在下游有能力处理的时候,再进行分发。
若是下游有不少系统关心你的系统发出的通知的时候,果断地使用消息队列吧。

写在最后

若是以为文章对你有点帮助,请微信搜索并关注「 冰河技术 」微信公众号,跟冰河学习高并发编程技术。

最后,附上并发编程须要掌握的核心技能知识图,祝你们在学习并发编程时,少走弯路。

sandahexin_20200322

相关文章
相关标签/搜索