消息队列定义 清疚 2023-06-13 15:24 9阅读 0赞 ### 什么是消息队列 ### 消息队列,一般我们会简称它为MQ(Message Queue),先不管消息(Message)这个词,来看看队列(Queue)。队列是一种**先进先出**的数据结构: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70][] 在Java里边,已经实现了不少的队列了(Java的队列实现类): ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 1][] 消息队列可以简单理解为:**把要传输的数据放在队列中**。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 2][] 科普: * 把数据放到消息队列叫做**生产者** * 从消息队列里边取数据叫做**消费者** ### 为什么要用消息队列 ### **原因:解耦、异步、削峰/限流** **1、解耦** 现在我有一个系统A,系统A可以产生一个`userId` ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 3][] 系统A可以产生一个UserId,然后,现在有系统B和系统C都需要这个`userId`去做相关的操作 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 4][] 系统A给系统B和系统C传入userId这个值,写成伪代码可能是这样的: public class SystemA { // 系统B和系统C的依赖 SystemB systemB = new SystemB(); SystemC systemC = new SystemC(); // 系统A独有的数据userId private String userId = "Java3y"; public void doSomething() { // 系统B和系统C都需要拿着系统A的userId去操作其他的事 systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); } } 结构图如下: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 5][] ok一切平安无事度过几天。某天系统B的负责人告诉系统A的负责人,现在系统B的`SystemBNeed2do(String userId)`这个接口不再使用了,**让系统A别去调它了**。于是系统A的负责人说"好的,那我就不调用你了。",于是就**把调用系统B接口的代码给删掉了**: public void doSomething() { // 系统A不再调用系统B的接口了 //systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); } 又过几天,系统D的负责人接了个需求,也需要用到系统A的userId,于是就跑去跟系统A的负责人说:"老哥,我要用到你的userId,你调一下我的接口吧",于是系统A说:"没问题的,这就搞" ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 6][] 系统A需要调用系统D的接口,然后,系统A的代码如下: public class SystemA { // 已经不再需要系统B的依赖了 // SystemB systemB = new SystemB(); // 系统C和系统D的依赖 SystemC systemC = new SystemC(); SystemD systemD = new SystemD(); // 系统A独有的数据 private String userId = "Java3y"; public void doSomething() { // 已经不再需要系统B的依赖了 //systemB.SystemBNeed2do(userId); // 系统C和系统D都需要拿着系统A的userId去操作其他的事 systemC.SystemCNeed2do(userId); systemD.SystemDNeed2do(userId); } } 时间飞逝: * 又过了几天,系统E的负责人过来了,告诉系统A,需要userId。 * 又过了几天,系统B的负责人过来了,告诉系统A,还是重新掉那个接口吧。 * 又过了几天,系统F的负责人过来了,告诉系统A,需要userId。 * …… 于是系统A的负责人,每天都被这给骚扰着,改来改去,改来改去……. 还有另外一个问题,调用系统C的时候,如果系统C挂了,系统A还得想办法处理。如果调用系统D时,由于网络延迟,请求超时了,那系统A是反馈`fail`还是重试?最后,系统A的负责人,觉得**隔一段时间就改来改去**,没意思,于是就跑路了。然后公司招来一个大佬,大佬经过几天熟悉,上来就说:**将系统A的userId写到消息队列中,这样系统A就不用经常改动了**。我们来一起看看: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 7][] 系统A将userId写到消息队列中,系统C和系统D从消息队列中拿数据。**这样有什么好处**? * 系统A**只负责**把数据写到队列中,谁想要或不想要这个数据(消息),**系统A一点都不关心**。 * 即便现在系统D不想要userId这个数据了,系统B又突然想要userId这个数据了,都跟系统A无关,系统A一点代码都不用改。 * 系统D拿userId不再经过系统A,而是从消息队列里边拿。**系统D即便挂了或者请求超时,都跟系统A无关,只跟消息队列有关**。 这样一来,系统A与系统B、C、D都**解耦**了。 **2、异步** 系统A还是**直接调用**系统B、C、D ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 8][] 代码如下: public class SystemA { SystemB systemB = new SystemB(); SystemC systemC = new SystemC(); SystemD systemD = new SystemD(); // 系统A独有的数据 private String userId ; public void doOrder() { // 下订单 userId = this.order(); // 如果下单成功,则安排其他系统做一些事 systemB.SystemBNeed2do(userId); systemC.SystemCNeed2do(userId); systemD.SystemDNeed2do(userId); } } **设**系统A运算出userId具体的值需50ms,调用系统B的接口需300ms,调用系统C的接口需300ms,调用系统D的接口需300ms。那这次请求就需`50+300+300+300=950ms`,并且我们得知系统A做的是**主要的业务**,而系统B、C、D是**非主要**的业务。比如系统A处理的是**订单下单**,而系统B是订单下单成功了,那发送一条短信告诉具体的用户此订单已成功,而系统C和系统D也是处理一些小事而已。那么此时,为了**提高用户体验和吞吐量**,其实可以**异步地**调用系统B、C、D的接口。所以,我们可以弄成是这样的: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 9][] 此时才用了100ms,系统A执行完了以后,将userId写到消息队列中,然后就直接返回了(至于其他的操作,则异步处理)。 * 本来整个请求需要用950ms(同步) * 现在将调用其他系统接口异步化,从请求到返回只需要100ms(异步) **3、削峰/限流** 现在我们每个月要搞一次大促,大促期间的并发可能会很高的,比如每秒3000个请求。假设我们现在有两台机器处理请求,并且每台机器只能每次处理1000个请求。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 10][] 那多出来的1000个请求,可能就把我们**整个系统给搞崩了**…所以,有一种办法,我们可以写到消息队列中: ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 11][] 写到消息队列中,系统从消息队列中拿到请求。系统B和系统C**根据自己的能够处理的请求数去消息队列中拿数据**,这样即便有每秒有8000个请求,那只是把请求放在消息队列中,去拿消息队列的消息**由系统自己去控制**,这样就不会把整个系统给搞崩。 ### 使用消息队列有什么问题 ### **问题:高可用、数据丢失、消费者怎么得到消息队列的数据、提高系统复杂性……** 经过上面的场景已经可以发现消息队列能做的事其实还是蛮多的。说到这里先回到文章的开头,"明明JDK已经有不少的队列实现了,我们还需要消息队列中间件呢?"其实很简单,JDK实现的队列种类虽然有很多种,但是都是**简单的内存队列**。为什么我说JDK是简单的内存队列呢?下面我们来看看要实现消息队列(中间件)**可能要考虑什么问题**。 **1、高可用** 无论是使用消息队列来做解耦、异步还是削峰,消息队列**肯定不能是单机**的。试着想一下,如果是单机的消息队列,万一这台机器挂了,那我们整个系统几乎就是不可用了。所以,当我们项目中使用消息队列,都是得`集群/分布式`的。要做`集群/分布式`就必然希望该消息队列能够提供**现成**的支持,而不是自己写代码手动去实现。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 12][] **2、数据丢失问题** 将数据写到消息队列上,系统B和C还没来得及取消息队列的数据就挂掉了。**如果没有做任何的措施,我们的数据就丢了**。 ![watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 13][] 学过Redis的都知道,Redis可以将数据持久化磁盘上,万一Redis挂了,还能从磁盘从将数据恢复过来。同样地,消息队列中的数据也需要存在别的地方,这样才尽可能减少数据的丢失。 那存在哪呢? * 磁盘? * 数据库? * Redis? * 分布式文件系统? 同步存储还是异步存储? **3、消费者怎么得到消息队列的数据?** 有两种办法: * 生产者将数据放到消息队列中,消息队列有数据了,**主动叫**消费者去拿(俗称push) * 消费者不断去**轮训**消息队列,看看有没有新的数据,如果有就消费(俗称pull) **4、其他** 在**使用的时候**还得考虑各种的问题: * 消息重复消费了怎么办啊? * 我想保证消息是**绝对**有顺序的怎么做? * …….. 虽然消息队列给我们带来了那么多的好处,但同时我们发现引入消息队列也会**提高系统的复杂性**。市面上现在已经有不少消息队列轮子了,每种消息队列都有自己的特点,**选取哪种MQ还得好好斟酌**。 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70]: https://img-blog.csdnimg.cn/20191117212500409.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 1]: https://img-blog.csdnimg.cn/20191117212548570.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 2]: https://img-blog.csdnimg.cn/20191117212735489.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 3]: https://img-blog.csdnimg.cn/20191117212955327.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 4]: https://img-blog.csdnimg.cn/20191117213039800.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 5]: https://img-blog.csdnimg.cn/20191117213152230.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 6]: https://img-blog.csdnimg.cn/20191117213356468.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 7]: https://img-blog.csdnimg.cn/20191117213453315.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 8]: https://img-blog.csdnimg.cn/20191117213743917.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 9]: https://img-blog.csdnimg.cn/2019111721392111.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 10]: https://img-blog.csdnimg.cn/20191117214106866.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 11]: https://img-blog.csdnimg.cn/20191117214137748.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 12]: https://img-blog.csdnimg.cn/20191117214333851.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70 [watermark_type_ZmFuZ3poZW5naGVpdGk_shadow_10_text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx_size_16_color_FFFFFF_t_70 13]: https://img-blog.csdnimg.cn/20191117214426913.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM2NzYxODMx,size_16,color_FFFFFF,t_70
相关 消息队列 一、消息队列MQ(Message Queue): 1)消息队列是一种先进先出的数据结构; 2)消息队列使用的“协议”不是具体的通讯协议,而是更高层次通讯模型。它定义 「爱情、让人受尽委屈。」/ 2023年10月11日 11:11/ 0 赞/ 55 阅读
相关 消息队列? 对于 MQ 来说,其实不管是 RocketMQ、Kafka 还是其他消息队列,它们的本质都是:一发一存一消费。下面我们以这个本质作为根,一起由浅入深地聊聊 MQ。 01 从 Bertha 。/ 2023年10月10日 12:49/ 0 赞/ 31 阅读
相关 消息队列 一、什么是消息队列 以下为虚构的小故事: 有一天,产品跑来跟小王说:“我们要做一个用户实名的功能,需要在用户实名成功后给用户发一条短信。” 小王(攻城狮leve 缺乏、安全感/ 2023年10月06日 17:00/ 0 赞/ 56 阅读
相关 消息队列定义 什么是消息队列 消息队列,一般我们会简称它为MQ(Message Queue),先不管消息(Message)这个词,来看看队列(Queue)。队列是一种先进先出的数据结构 清疚/ 2023年06月13日 15:24/ 0 赞/ 10 阅读
相关 消息队列 1. 消息队列在项目中的使用 背景:在分布式系统中是如何处理高并发的。 由于在高并发的环境下,来不及同步处理用户发送的请求,则会导致请求发生阻塞。比如说,大量的ins ﹏ヽ暗。殇╰゛Y/ 2022年12月15日 03:23/ 0 赞/ 277 阅读
相关 消息队列 https://www.cnblogs.com/457248499-qq-com/p/7392678.html 来源 消息队列:在消息的传输过程中保存消息的容器。 迈不过友情╰/ 2022年05月30日 07:42/ 0 赞/ 292 阅读
相关 消息队列 消息队列是啥?我觉得大家都心知肚明,已经众所周知到不用解释的程度。不过,但凡学习、解释一样东西,都应该遵循 “它是什么?”、 “做什么用?”、 “为啥要用它” ╰半橙微兮°/ 2022年04月23日 08:42/ 0 赞/ 364 阅读
相关 消息队列 消息队列介绍 维基百科上的描述:在计算机科学中,消息队列(Message queue)是一种进程间通信或同一进程的不同线程间的通信方式,软件的贮列用来处理一系列的输入,通 红太狼/ 2022年01月26日 10:51/ 0 赞/ 353 阅读
相关 消息队列 为什么写这篇文章? 博主有两位朋友分别是小A和小B: 1. 小A,工作于传统软件行业(某社保局的软件外包公司),每天工作内容就是和产品聊聊需求,改改业务逻辑。再不然就 小咪咪/ 2021年12月20日 06:51/ 0 赞/ 445 阅读
还没有评论,来说两句吧...