消息队列入门

60 阅读11分钟

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动

​前言

消息队列,相信大家在工作中肯定用过或者听说过吧,如果你连消息队列都没听过,我甚至怀疑你是个假的程序猿。一个东西的存在必有它存在的价值,存在即合理。那她到底是什么呢?用于解决哪些痛点呢?是如何工作的呢?我们来揭开她的面纱

即使大家对消息队列不熟,但是大家应该对队列这一结构应该都很熟悉了吧。队列就是一种先进先出的数据结构,举几个简单的例子:排队买冰激凌,排队上茅房~

在Java中有各种各样的队列供大家选择,那为什么还要引入消息队列这个中间件呢?大家可以思考一下,待会给大家解答

正文

消息队列是什么?

消息队列就是把我们业务中的消息按照一定的顺序或者一定的规则放入到相应的队列中,为什么要这么搞呢?哪有为什么,公司项目就是这样设计的,我哪知道,这可能是很多人的心理活动,哈哈,话虽如此,我们还是要搞懂为何引入这个中间件的,这样我们就能在面试的时候吹一波了

举个例子,我们现在做一个电商系统包括用户模块、拼团模块、支付模块,初期我们可能用户较少,业务简单,所以直接一把梭哈全部搞定,但是随着业务量的增加,需要根据微服务设计思想拆分出更多的模块,对系统越来越不友好,老板觉得该对系统加大优化力度了。于是,我们的消息队列登场了

消息队列的三大优点:解耦、异步、削峰,这应该是大家应该熟记于心的那种了,应该是提起消息队列就会条件反射的想起来的东西了,下面我从这三方面帮助大家理解下

解耦

一个下单的流程,包含增加用户积分、扣优惠券、扣库存、扣钱,甚至还有一些贵重物品会发邮件、发短信通知,试想一下,如果这些代码都写在一个接口中,这会是一种灾难。并发问题暂且不说,如果接口中的一部分无关紧要的业务出现问题,可能会导致整个系统崩溃,这是我们不想看到的结果,线上出个这问题,你可以收拾办公桌了。

梭哈老大爷直呼内行!

甚至线上出现问题排查起来也会是一种灾难,你可能会说可以加异常处理和日志来解决,那要是几十个几百个这样的逻辑业务呢?线上还是尽量不要这么做,这样的代码就像是定时炸弹,随时可能会出现问题影响整个系统

这个问题,用了消息队列就可以解决,怎么回事?

用户下单了,系统把这个下单的消息放入到消息队列中,其余的多个系统监听这一个消息队列,它们监听到消息每个系统去分别处理就可以了,大家互不影响,后面如果再临时接入什么系统,直接监听这个消息队列消息就可以了啊

有的小伙伴可能会问,我把消息发送了,如果消息丢失了怎么办?或者说我消息发送了,别的系统处理消息失败这种情况如何处理呢?

很好,都会抢答了。这些问题其实是消息队列中的经典问题,也是面试官最爱问的问题,这里我先简单回答下,因为这些问题很重点,需要单独拎出来来说。消息丢失,每个不同的消息处理中间件都有自己的保护措施,通过合适的参数配置加上一些适当的重试机制基本都可以解决消息丢失问题。

至于消息处理失败的情况,也需要重试或者回调的配合来完成,其中涉及多个模块还会有分布式事务的问题,比如用户的钱扣除成功了,如何保证库存一定扣减成功,积分增加成功,在接受范围内进行合理的措施调整。

异步:就上面刚说的直接一把梭哈解决问题的方式,对用户也是相当不友好的。

你想啊,你这整个下单逻辑中包含多个处理子模块,这些都是串行的啊,如果有的服务处理很慢,或者说部分的服务可能对于时间很敏感,需要接口快速的响应,那如果此时有个被调用的接口很慢,那岂不是很糟糕。

就比如一个电商系统包含多个子模块:积分系统、优惠券系统、扣款系统、下单系统、日志系统等等,如果用户下一个订单,需要等待这些系统每一个都执行完并且给出响应的结果,那会很糟糕,一个接口200ms,用户下单就需要一秒多。

系统少的时候暂时还可以顶得住,但是产品经理笑眯眯的又来了,又来了两个新需求,需要搞在这里面,这样可能会搞得用户下单变得很慢,用户买个东西都这么慢,买一个东西花几十秒,那这个系统再不优化,你就可以准备收拾东西say byebye了。

嗯,如何解决呢?

这个时候其实我们的消息队列就有大作用了,就像上图所示的那样子,上面的很多流程其实是可以很多同时做的,并且有些需求可能不是要求很高的实时性。

其实就是一个词,异步。同时进行,不需要同步等待上一个逻辑的响应再进行下一个逻辑。

等下,我的理解是你说的这个异步,多个逻辑同时进行,我用多线程也可以来实现啊,我初始化一个线程池,用里面的线程来完成相应的业务处理,也可以做到啊。

没错,确实可以做到,但是如果一个订单流程,一个接口里面要调用很多的处理,积分要一个线程来处理,优惠券要一个线程来处理,这么多的处理的接口耦合在一个系统里面,每次加一个接口或者修改其中的一部分,都要重新发布系统,耦合也是很糟糕的处理。消息队列,这个问题可以轻松解决

用户下单了,就把支付成功的消息发送到相应的队列中,别的系统去监听这些队列,别的系统监听到就去处理就可以了,主流程主需要走完自己的支付流程,并且把自己的消息发送出去就可以了,加上如果后面再有一些新需求进来,一样的道理,也是直接监听相应的消息队列就可以了

消息队列还有一个很重要的作用:削峰,比如淘宝双十一,平时流量没有那么高,但是到了双十一买买买的时候,流量就会变得很高,这个时候就要考虑服务器啊,Redis啊,数据库啊这些的承受能力,如果流量直接全部直接打到数据库肯定是有问题的,怎么办呢?消息队列,削峰~

啥原理呢?

其实就是把请求放到消息队列中,至于消费者能处理多块,就要看消费者服务器的处理能力了,你服务器几万块一台和几十万一台,肯定能顶的QPS是不一样的,慢的服务器就处理慢一点咯,但是不至于把服务器打挂咯,就比如淘宝双十一凌晨的时候,瞬时流量那么大,它能够顶得住消息队列是功不可没的,有时候可能会出现一些友好型的页面,其实就是降级处理,服务器暂时处理不过来了,你的请求的QPS直接拦在了消息队列的外面了

消息队列存在的问题

消息队列这么好用的话,那岂不是所有公司都要引入这个中间件了?其实不是的,技术是把双刃剑,在使用的过程中,其实也会有很多的问题,我从两个方面来介绍下它的缺点

1、系统可用性

就比如要开发一个很简单的系统,本来同步调用就可以轻轻松松解决,现在如果要接入这么一个中间件,我需要去维护它,考虑它的可用性问题,以及消息的重试机制、重复消费问题、消息丢失,以及一些消息的顺序性的问题,总之,引入了这个技术中间件,会有更多的技术问题随之而来,所以比较小的系统可以考虑不引入这个中间件,可能会适得其反。当一个系统引入这个中间件的维护成本远远低于维护老系统造成的成本的时候,如果你的领导再不引入这个中间件,可能你需要提辞职了,因为跟着这个领导,会限制你的发展

**那这些问题,如何解决呢?重复消费、消息丢失、顺序消费?**这些问题我会单独拎出来来说,因为这些问题比较典型,而且解决的方式也有很多,其实重点就是根据相应的业务场景来解决,达到业务场景的需求最为重要,毕竟技术是为业务而生,业务是技术的载体,就像灵魂和肉体的关系~

我们还需要考虑MQ的可用性,一旦MQ挂了怎么办,如果我们的消息很重要,一条消息也不允许丢失,或者允许丢失短暂时间的丢失,我们又该怎么办?

带着各种问题来学习MQ,你的学习效果绝对是比别人强很多的

2、数据一致性

其实这个问题也比较大,这不只是MQ存在的问题,这是分布式系统本身存在的一个问题。比如系统A发送了消息,如何保证系统B成接收到消息,并且处理成功了呢?可以加一个系统B的回调,成功消费完消息回调通知系统A该消息已经消费完成了。但是还是会有问题,如果消息中间件把消息丢失了,消费者压根收不到这个消息呢,其实这个问题可以用生产者的一个重试发送消息的机制来解决。问题又来了,多条一样的消息发送到消费者的时候,消费者需要如何保证业务方向的幂等性(多次相同的处理会得到相同的结果)

常见的消息队列

我们来看比较常见的一些消息队列,ActiveMQ、RabbitMQ、RocketMQ、Kafka,这些是比较常见的消息队列,ActiveMQ没有经过大规模吞吐量的验证,社区也不是很活跃,暂时不推荐;RabbitMQ是用erlang语言开发的,这其实就变相的增加了开发成本,不方便深入研究和掌握它并且进行二次开发,但是这个消息队列社区还是比较活跃的;Kafka由于其内部的零拷贝和顺序写入等特性支持10万级别的高吞吐量,一般用于大数据领域;现在越来越多的公司开始使用RocketMQ了,采用Java开发便于维护,经过了多次双十一的洗礼,是阿里参考了Kafka特点研发的一个高性能、高可靠、高实时、分布式的消息中间件。

其实这个图应该是很明显了,吞吐量差距,在现在这样数据为王的时代,吞吐量相当重要,而且RocketMQ和Kafka天然支持分布式架构,而且数据也可以通过多备份做到消息零丢失。

求赞

好了,以上就是全部内容了,简单的把消息队列基础知识过了一遍,大家也对其有所了解了,能看到这里的肯定能学到一些东西

我希望有一天能够靠写字养活自己,现在还在磨练,这个时间可能会有很多年,感谢你们做我最初的读者和传播者。请大家相信,只要给我一份爱,我终究会还你们一页情的。

再次感谢大家能够读到这里,我后面会持续的更新技术文章以及一些记录生活的灵魂文章,如果觉得不错的,觉得【小仙】有点东西的话,求点赞、关注、分享三连

哦,对了!后续的更新文章我都会及时放到这里,欢迎大家点击观看,都是干货文章啊,建议收藏,以后随时翻阅查看

github.com/DayuMM2021/…