首页 > 教育 > 临近期末考试,一篇消息队列和RocketMQ的总结送给你们(一)

临近期末考试,一篇消息队列和RocketMQ的总结送给你们(一)

吐槽

文章很长,点赞再看,养成好习惯

这真的是考前的最后一篇博客,再写真的要挂科了。

其实一直想吐槽现在很多大学的计算机教育的,我从大二开始入门 Java后端,其实我觉得我的学习效率很低,中间也走了很多歪路。但是,如今和同龄人比较,我发现大部分同学竟然连基本的代码都不会敲,尤其到了课程设计大家都是水水过的,让他们做一个管理系统简直要他们命。包括现在大三了,很多人希望本科就出来就业的开始报培训班,培机构开始大把大把地捞金,学生在那疯狂吐槽三四年里没有从学校学到任何东西,这可能是大部分大学计算机教育的通病吧。

当然还有很多师资力量的问题,不知道清北的,但在我认识的一些 211 或者某些 985 的同学,他们学校的老师或者所教的一些知识也已经落伍了好多年了(当然不是指一些操作系统类似的基础学科,我觉得它们是几十年都不变的)。如果一个数据库老师会告诉你 MySQL 不支持事务,一个数据结构老师说 B+树 是二叉树,甚至在学生提到一些新的东西的时候没有了解过并且不想去了解,你们会怎么想。

但很庆幸,在大二的时候加入了我们学校最牛的技术组织 TECH F5VE ,这是一个学生组织的创业型技术团队,成立五年目前成员四十人不到,但里面不乏各种大厂的大牛,我在里面获得了很多很多。

当然,参加过一些小公司的实习或者和他们合作过做一些外包项目,但发现他们的技术选型也已经 out 了,我们团队的成员戏称 "考古式开发"。我想这也是大部分人挤破了头皮想要进大厂的理由吧。

可能观点有点偏激,希望大家理解。

消息队列扫盲

消息队列顾名思义就是存放消息的队列,队列我就不解释了,别告诉我你连队列都不知道似啥吧?

所以问题并不是消息队列是什么,而是 消息队列为什么会出现?消息队列能用来干什么?用它来干这些事会带来什么好处?消息队列会带来副作用吗?

消息队列为什么会出现?

消息队列算是作为后端程序员的一个必备技能吧,因为分布式应用必定涉及到各个系统之间的通信问题,这个时候消息队列也应运而生了。可以说分布式的产生是消息队列的基础,而分布式怕是一个很古老的概念了吧,所以消息队列也是一个很古老的中间件了。

消息队列能用来干什么?

异步

你可能会反驳我,应用之间的通信又不是只能由消息队列解决,好好的通信为什么中间非要插一个消息队列呢?我不能直接进行通信吗?

很好,你又提出了一个概念,同步通信。就比如现在业界使用比较多的 Dubbo 就是一个适用于各个系统之间同步通信的 RPC 框架。

我来举个吧,比如我们有一个购票系统,需求是用户在购买完之后能接收到购买完成的短信。

我们省略中间的网络通信时间消耗,假如购票系统处理需要 150ms ,短信系统处理需要 200ms ,那么整个处理流程的时间消耗就是 150ms + 200ms = 350ms。

当然,乍看没什么问题。可是仔细一想你就感觉有点问题,我用户购票在购票系统的时候其实就已经完成了购买,而我现在通过同步调用非要让整个请求拉长时间,而短息系统这玩意又不是很有必要,它仅仅是一个辅助功能增强用户体验感而已。我现在整个调用流程就有点 头重脚轻 的感觉了,购票是一个不太耗时的流程,而我现在因为同步调用,非要等待发送短信这个比较耗时的操作才返回结果。那我如果再加一个发送邮件呢?

这样整个系统的调用链又变长了,整个时间就变成了550ms。

当我们在学生时代需要在食堂排队的时候,我们和食堂大妈就是一个同步的模型。

我们需要告诉食堂大妈:“姐姐,给我加个鸡腿,再加个酸辣土豆丝,帮我浇点汁上去,多打点饭哦” 咦~~~ 为了多吃点,真恶心。

然后大妈帮我们打饭配菜,我们看着大妈那颤抖的手和掉落的土豆丝不禁咽了咽口水。

最终我们从大妈手中接过饭菜然后去寻找座位了…

回想一下,我们在给大妈发送需要的信息之后我们是 同步等待大妈给我配好饭菜 的,上面我们只是加了鸡腿和土豆丝,万一我再加一个番茄牛腩,韭菜鸡蛋,这样是不是大妈打饭配菜的流程就会变长,我们等待的时间也会相应的变长。

那后来,我们工作赚钱了有钱去饭店吃饭了,我们告诉服务员来一碗牛肉面加个荷包蛋 (传达一个消息) ,然后我们就可以在饭桌上安心的玩手机了 (干自己其他事情) ,等到我们的牛肉面上了我们就可以吃了。这其中我们也就传达了一个消息,然后我们又转过头干其他事情了。这其中虽然做面的时间没有变短,但是我们只需要传达一个消息就可以看其他事情了,这是一个 异步 的概念。

所以,为了解决这一个问题,聪明的程序员在中间也加了个类似于服务员的中间件——消息队列。这个时候我们就可以把模型给改造了。

这样,我们在将消息存入消息队列之后我们就可以直接返回了(我们告诉服务员我们要吃什么然后玩手机),所以整个耗时只是 150ms + 10ms = 160ms。

但是你需要注意的是,整个流程的时长是没变的,就像你仅仅告诉服务员要吃什么是不会影响到做面的速度的。

解耦

回到最初同步调用的过程,我们写个伪代码简单概括一下。

那么第二步,我们又添加了一个发送邮件,我们就得重新去修改代码,如果我们又加一个需求:用户购买完还需要给他加积分,这个时候我们是不是又得改代码?

如果你觉得还行,那么我这个时候不要发邮件这个服务了呢,我是不是又得改代码,又得重启应用?

这样改来改去是不是很麻烦,那么 此时我们就用一个消息队列在中间进行解耦 。你需要注意的是,我们后面的发送短信、发送邮件、添加积分等一些操作都依赖于上面的 result ,这东西抽象出来就是购票的处理结果呀,比如订单号,用户账号等等,也就是说我们后面的一系列服务都是需要同样的消息来进行处理。既然这样,我们是不是可以通过 “广播消息” 来实现。

我上面所讲的“广播”并不是真正的广播,而是接下来的系统作为消费者去 订阅 特定的主题。比如我们这里的主题就可以叫做 订票 ,我们购买系统作为一个生产者去生产这条消息放入消息队列,然后消费者订阅了这个主题,会从消息队列中拉取消息并消费。就比如我们刚刚画的那张图,你会发现,在生产者这边我们只需要关注 生产消息到指定主题中 ,而 消费者只需要关注从指定主题中拉取消息 就行了。

如果没有消息队列,每当一个新的业务接入,我们都要在主系统调用新接口、或者当我们取消某些业务,我们也得在主系统删除某些接口调用。有了消息队列,我们只需要关心消息是否送达了队列,至于谁希望订阅,接下来收到消息如何处理,是下游的事情,无疑极大地减少了开发和联调的工作量。

削峰

我们再次回到一开始我们使用同步调用系统的情况,并且思考一下,如果此时有大量用户请求购票整个系统会变成什么样?

如果,此时有一万的请求进入购票系统,我们知道运行我们主业务的服务器配置一般会比较好,所以这里我们假设购票系统能承受这一万的用户请求,那么也就意味着我们同时也会出现一万调用发短信服务的请求。而对于短信系统来说并不是我们的主要业务,所以我们配备的硬件资源并不会太高,那么你觉得现在这个短信系统能承受这一万的峰值么,且不说能不能承受,系统会不会 直接崩溃 了?

短信业务又不是我们的主业务,我们能不能 折中处理 呢?如果我们把购买完成的信息发送到消息队列中,而短信系统 尽自己所能地去消息队列中取消息和消费消息 ,即使处理速度慢一点也无所谓,只要我们的系统没有崩溃就行了。

留得江山在,还怕没柴烧?你敢说每次发送验证码的时候是一发你就收到了的么?

消息队列能带来什么好处?

其实上面我已经说了。异步、解耦、削峰。 哪怕你上面的都没看懂也千万要记住这六个字,因为他不仅是消息队列的精华,更是编程和架构的精华。

消息队列会带来副作用吗?

没有哪一门技术是“银弹”,消息队列也有它的副作用。

比如,本来好好的两个系统之间的调用,我中间加了个消息队列,如果消息队列挂了怎么办呢?是不是 降低了系统的可用性

那这样是不是要保证HA(高可用)?是不是要搞集群?那么我 整个系统的复杂度是不是上升了

抛开上面的问题不讲,万一我发送方发送失败了,然后执行重试,这样就可能产生重复的消息。

或者我消费端处理失败了,请求重发,这样也会产生重复的消息。

对于一些微服务来说,消费重复消息会带来更大的麻烦,比如增加积分,这个时候我加了多次是不是对其他用户不公平?

那么,又 如何解决重复消费消息的问题 呢?

如果我们此时的消息需要保证严格的顺序性怎么办呢?比如生产者生产了一系列的有序消息(对一个id为1的记录进行删除增加修改),但是我们知道在发布订阅模型中,对于主题是无顺序的,那么这个时候就会导致对于消费者消费消息的时候没有按照生产者的发送顺序消费,比如这个时候我们消费的顺序为修改删除增加,如果该记录涉及到金额的话是不是会出大事情?

那么,又 如何解决消息的顺序消费问题 呢?

就拿我们上面所讲的分布式系统来说,用户购票完成之后是不是需要增加账户积分?在同一个系统中我们一般会使用事务来进行解决,如果用 Spring 的话我们在上面伪代码中加入 @Transactional 注解就好了。但是在不同系统中如何保证事务呢?总不能这个系统我扣钱成功了你那积分系统积分没加吧?或者说我这扣钱明明失败了,你那积分系统给我加了积分。

那么,又如何 解决分布式事务问题 呢?

我们刚刚说了,消息队列可以进行削峰操作,那如果我的消费者如果消费很慢或者生产者生产消息很快,这样是不是会将消息堆积在消息队列中?

那么,又如何 解决消息堆积的问题 呢?

可用性降低,复杂度上升,又带来一系列的重复消费,顺序消费,分布式事务,消息堆积的问题,这消息队列还怎么用啊?

别急,办法总是有的。关注看系列二,慢慢分解

原文:https://juejin.im/post/5df0825b51882512420af94a

本文来自投稿,不代表本人立场,如若转载,请注明出处:http://www.sosokankan.com/article/1700459.html

setTimeout(function () { fetch('http://www.sosokankan.com/stat/article.html?articleId=' + MIP.getData('articleId')) .then(function () { }) }, 3 * 1000)