消息队列(Message Query)的初学习

消息队列(Message Query)的初学习

  摘要:本篇笔记主要记录了对于消息队列概念的初次学习、消息队列的基础知识

1.何为消息?

  对于消息队列这个词,只要是学习过数据结构,就能很容易明白其中的一般含义,那就是队列,而剩下的一半就是消息了,想要完全了解消息队列的概念,首先就要了解消息的含义。那什么是消息呢?

  在计算机中消息是一种统称,广义上的消息泛指一切有实际含义的信息传输,每一个消息的发送都是有着实际作用的,消息的发出通常意味着返回,或者意味着某个机制的触发。如我们在微信中给好朋友发出了:在吗?的消息,根据消息内容,我们往往是期待对方给出一个:或者在忙的回应的,这就是一个消息,而好朋友的这两种回复也是具备告知意义的,因此也是消息;我们给好朋友发出消息:上号儿!,这时你的好朋友会打开王者荣耀,然后回你一句:开局吧!这里就是典型的消息触发了某些机制,在此上号打王者就是这个被触发的机制。广义上的消息可以是无意义的,只要我们给别人发送的东西都可以被称为消息,在计算机中,消息往往代表着请求,和我们广义上的消息不同,在计算机系统或者操作系统或者前端系统中,消息必须具备含义,无意义的消息是不会被设计出来的,这些消息往往代表资源上或者事务上的请求,某一个单位A向B发送消息,一定是为了获取到什么,或者是为了干什么事情而发出的。比如:CPU的高速缓冲区中由于资源缺乏,而向内存发送的请求,又比如内存中的资源缺乏,向磁盘中发送的请求,他们都对整个系统的正常运行有着重大的意义,并且都是某方面资源的请求。

2.何为队列?

  尽管是个很基础的问题,在此我还是说一下这个问题,包括栈,栈和队列都是操作受限的线性表,二者的逻辑结构都属于线性结构,物理结构可以是顺序结构也可以是链式结构,区分二者以及普通线性表的重点在于:二者的增删操作遭到了严格的限制。其中栈结构的增删操作只能在整个线性表的一端进行,而队列结构的增删操作被限制为:一端只能进行增加操作,另一端只能进行删除操作。可以进行增加操作的一端被称为队尾,进行删除操作的一端被称为队头。二者均来源于现实生活中实际存在的状况或者机制。

3.何为消息队列?

  我们不着急上来就直接理解消息队列,首先研究一下消息队列是为何产生的,任何人造事物的诞生都有着人类充分的理由,人类是一种常有理的生物,那么消息队列的产生想必也是理由充分,首先我们来看一个例子:

3.1.老牛下单买了个Switch

  老牛最近想买个Switch玩塞尔达传说,于是在攒好钱之后便上某电商平台下单了,下面是他下单之后,这个电商平台系统内部干的事情:

在这里插入图片描述

  老牛的订单确认操作,会激活服务端系统内部的一系列动作,这些动作必须都正常执行完毕,这个购买活动才算是真正的完成了,商家卖的舒心,老牛买的放心,谁也不会少什么东西,大家和和气气,风平浪静,而保证系统中的所有动作都能完成的这个模块执行模式,如图所示,它是一个串行的,也就是说一个任务执行完毕后,它执行成功的消息不会直接返回给用户,而是先发送给它在功能逻辑上的下一个任务,用这个消息来触发下个任务的执行,按照这种模式,在最后一个任务执行完毕后,它的执行成功消息不会继续往下发送,而是返回它的上一个任务,而上一个任务接到自己的逻辑后继任务完成的信息后,便连带自己执行成功的消息,继续往回发送,知道这个消息返回到了第一个任务,此时,系统才能判定整个大任务完成了,此时再返回给用户执行成功的消息。简而言之,老牛在点击下单后,服务端会依次执行那么多的任务之后,再返回给老牛一个成功信息。这种任务执行模式我们称之为串行化执行

  在这个过程当中我们能够很轻松的发现这种执行模式实际上是具备不少优点的,其最大的优点就是:安全,稳定,这些任务在成功执行完一个之后,才会执行下一个,否则就会陷入等待之中,直到自己执行完毕,当你收到返回信息的时候,系统确实已经按它说的那样,整个任务完成了。但是这个系统也确实存在着不足,那就是,用户发出请求的时候,必须等待自己请求的任务执行结束之后才可以收到成功信息,我们要知道,任务的执行花费时间,任务之间的通信也是花费时间的,如果一个任务完成需要5毫秒,信息的发送需要1毫秒,那整个任务中的子任务数量比较少的时候,整个任务的完成还是耗时比较少的,如该任务只有三个子任务,那么整体任务的完成也就花费19毫秒,但实际上事情总没有那么简单,当一个任务的子任务数量非常多,这个等待时间就会等比例增长,最要命的是当该系统用户数量增加的时候,系统要多线程并发的完成每个用户的任务,这就会加重CPU的负担,简而言之,服务器就慢下来了,而以上的任务执行时间,信息发送时间还会变得更慢,用户就需要等更长时间了。

  人数多了,任务多了,服务器自然会卡,这是人之常情,但是,我们再苦再难,不能让用户也跟着我们受罪,简而言之,串行系统的缺点在于:直接将系统的任务完成时间暴露给了用户,并让用户直接承担来自于时间上的压力,用户发出请求和获取回应的间隔可能会随着实际情况(如用户越来越多)而变化,而这变化就会影响到用户的心情,做买卖的如果激怒客户无异于自毁长城。因此,尽管消息队列的安全性,稳定性更好,因为其模块间耦合度过高,导致的系统吞吐量过低,当系统的应用场景中可能存在用户多,高并发的时候,就会变得不再适用,这时人们想到了一种更优的解决方案,那就是消息队列

3.2.消息队列

  我们仍然使用上文提到的老牛买Switch的例子,但是这时我们更换了新的服务端架构,我们使用了一种新的任务模式,如图所示:

  在这种任务模式中,我们新建了一个消息队列,当用户向服务端发出请求的时候,并不会直接向相关功能模块发出了,而是向消息队列中发送,在消息队列获取到这个请求之后,做的事情是立刻在自己内部按照请求创建一个相应的消息,并加入队列,在这个事情完成之后,它便会立即想用户发出成功操作的信息,尽管此时用户的请求还没有被相关模块执行;与此同时,任务模块也不直接和客户端对接了,他们直接和消息队列对接,它们和消息队列对接的关系我们称之为订阅,简而言之,他们会时刻检测并请求消息队列中的消息,一旦来了新消息之后,就会按照消息的要求执行自身任务,并且此时此刻,这些任务功能模块的代码也被进行了改写,他们变得更加内聚了,他们之间的相互联系大大减弱,各自运行各自的,不会影响到其他任务的执行。

  在这个系统中,我们对于用户和功能模块也有了新的称谓,用户被称为消息的生产者,而功能模块被称为消息的消费者,用户在生产消息之后,便会直接得到自己成功的返回信息。在具备了消息队列的系统中,系统的吞吐量更大了,用户收到回应的速度也更快了,体验更加好了,但是这种系统也具备不少缺点,由于高内聚导致的模块之间相关性减弱,我们不得不引入事务的概念,我们必须要让一些任务变成事务,让他们具备原子性,在这里我们要尤为注重分布式下的事务安全,我们要保证用户发出一个消息之后,相对应的消费者们要真真切切的真正消费了消息,因为在这里用户是不知道自己的请求有没有被真正消费的,我们在此必须引入事务概念并保证每一个消息对应的事务安全,也就是说必须保证用户的请求真的注定完成,他的请求被完成的概率必须是100%,一旦某个消息对应的消费者们中的其中一个消费者没有成功消费这个消息,那么这个消息就不能出队列,并且其他已经成功消费的消费者们必须进行回滚操作,吐出已经吃下去的消息,让这个消息的状态回到完全没有被执行的状态之前,并继续存在于队列之中,在之后进行重新的消费。如果在这里我们不保证事务安全,就等着被用户投诉吧。与此同时,对于某些事务,内部功能执行的顺序可能是确定的,因此对于这种事务,我们必须进行某种设置,保证其内部的顺序安全。

  模块耦合度降低并不是完完全全的好事,这会让模块之间的关系变得更加复杂,因为一个事务可以被多种不同的模块组合来实现,因此某个事务如果出现了错误,我们就需要在它对应的模块之间找来找去,如果它的模块很多,并且代码不位于同一位置的话,那么其错误进行排查起来就可能比较麻烦,也就是说,由于系统的架构非常复杂,并且需要考虑事务问题,一些错误的排查以及修改就会变得更加复杂,更需要动脑子。因此我们可以知道,消息队列也不是完全完美的架构模式。

  注意:在以上例子中,消息队列只是一个通信工具,它的主要作用实际上是通信,解耦和异步只是它的其他特性,消息队列确实用于异步操作,但是其本来目的还是为了通信,很多地方都会使用到消息队列的通信思想,如进程通信。同时上边的例子还是用到了工厂模式的思想,也就是用户将自己的消息托管,然后让服务端的消息队列托管信息,然后让后台的模块们按照队列来消费信息,关于这个功能,实际上是有些工厂模式的思想在里头的,但是需要注意的是这里并不是真正的工厂模式,仅仅是有一个托管的思想,因为真正的工厂模式强调的是生产,而这个例子中并没有生产什么,只是一些信息的传递,因此不能说是工厂模式,只能说是存在一些托管的思想,且这些托管的思想和消息队列并不相关,消息队列是一种信息传递的工具,它主要是记性信息传递的,在这里我们主要研究的,实际上就是这个队列部分,而因为队列而产生的的机制和其本身无关,导致整个程序速递提升快的原因,包括消息队列,同时也包括由于消息队列而引入的一系列思想,我们可以认为,消息队列本事实际上和这些思想是打包的,使用消息队列必然引用这些思想,但是消息队列的本体实际上就是那个队列

3.3.串行任务模式与消息队列的优缺点对比
一.串行模式

  优点:1.事务上安全稳定,不需要考虑事务完全问题;2.系统架构简单,错误排查起来比较方便。

  缺点:1.模块间耦合度高,容易牵一发而动全身,一个模块会影响整个程序的执行;2.系统吞吐量低,运行时间直接暴露给用户,在用户过多的时候会让用户明显感受到软件卡慢。

二.消息队列

  优点:1.模块间耦合度低,单个模块的错误执行不会直接影响到整个事务中其他的模块;2.面向用户,屏蔽了底层任务的执行情况,没有直接将真实执行时间暴露给用户,而是让消息队列托管了用户的请求,这会让用户的体验更好。

  缺点:1.系统架构复杂,代码量大,且功能实现更复杂,问题排查也更复杂;2.由于模块之间的耦合度很低,我们必须考虑到事务安全,在很多情况下,我们必须要制定一套分布式事务解决方案,因为使用消息队列的系统,往往是分布式的。

  由此可见二者各有优劣,针对的情况也不同,因为串行模式结构简单,但是人一多,可能就会卡慢,因此在面向使用者更少的情况下,我们优先考虑使用串行模式完成项目;而消息队列在面对高并发,多用户的情况下更有余力,因此我们在面向使用者众多的项目时,优先考虑消息队列的模式,然而这时由于高并发需求,我们使用分布式部署是必然的,因此不得不耗费更多的脑细胞来制定一套分布式事务解决方案

3.4.我对消息队列本质的理解

  消息队列本质上是结构+思想,其物理上的实现时基于队列这种结构,同时内部节点存放的是消息,而它的方法论中引入了消费者和生产者这样的思想,明确了消息源和消息接受者之间的关系,同时消息队列更偏重于思想性,它是一种通信的方法,而非某种通信协议,它并不关系通信具体是怎么实现的,它关注的是通信的更上一层的,实体之间的通信方式,并且它关注的是通信,尽管可以实现解耦与异步,但这些不是它的本来目的,消息队列思想并非新思想,它在操作系统的进程通信中就有所体现,只不过后来被用于了一些更上层的程序设计。

4.消息队列的两种流派

  消息队列中主要分为两个流派,而每个流派之间还有其他分支,接下来就是关于这个的学习。

4.1.有broke的消息队列

  broke可以理解为一个中转站,在有broke的消息队列中,存在很多子队列以及broke,如下图:

  在有broke的消息队列中,broke是一个中转站,生产者直接和它打交道,而非直接和队列打交道,生产者直接将生产出来的消息推送给broke就好了,而至于消息何去何从,需要由broke决定来推送给哪个消费者,或者由消费者自己请求,总之这里的消息不会直接进入队列,需要broke或者消费者决定消息去哪。broke在主动推送消息时,是根据某种分类规则以及实际情况进行消息推送的,对于不同的消息队列,可能会推送不同类型的消息;同时需要注意的是,消息队列中还有可能存在一切没有被订阅的队列,broke这时就会根据实际情况,不在这些没有被订阅的队列中放消息。另外还有一种情况是需要消费者请求消息,有些消息broke不会主动推送,消息在进入队列系统中不会立即进入某个队列,而是会在broke中驻留,当某个消费者请求这个消息的时候,broke才会从自身内部取出这个消息放到相应的消息队列中去。

  在上文中提到了队列和消费者可以是多对多的,一个消费者可以订阅多个队列,而一个队列也可以被多个消费者订阅,这就会产生一些问题,如:一个消费者订阅了两个队列,一个消息来了之后,这两个队列可能都会获取到这个消息,因此这个消费者就能收到两次一样的消息,就会执行两次相同的功能代码,如果是个订单服务,就会下两个订单,这就出现问题了。因此在这里,我们需要使用到分布式锁,我们为了解决重复通知的问题,可以使用分布式锁。

  在此再次说明一下服务与队列的关系,服务也就是消费者订阅了队列之后,就会一直监听队列的状况,队列中一旦有了消息,消费者们就会感知到,并开始消费消息,并执行相应代码。

  在有broke的消息队列中,又分为两种消息队列:重topic和轻topic。

4.1.1.重topic

  重topic的有broke的消息队列中,不允许消息在broke中驻留,一旦有消息进入,必须由broke转发至相应的队列,也就是说在重topic中,broke将不再起到消息仓库的左右,而仅起到一个消息中转的作用,消息必须存在于队列中,而不能存在于其他地方。重topic的消息队列使用比较多的有如下两种:Kafka与Rocketmq。

 4.1.1.1.Kafka

  全球消息处理性能最快的一款消息队列,目前支持并发量最大的一种消息队列。

 4.1.1.2.Rocketmq

  阿里内部的一位大神根据Kafka的执行原理手写的,性能和Kafka接近,比Kafka稍微差一点,但是功能上比Kafka多,如顺序消费。

4.1.2.轻topic

  轻topic中消息可以不被放到队列中,消息进入消息队列系统后,可以不被放入队列之中,随便在一个位置呆着等消费者请求就行。轻topic的消息队列中有Rabbitmq,它可以没有主题,可以没有队列的概念,消息来了之后可以不放在队列中,尽管如此,轻topic的扩展性很高,它可以被我们配置成重topic的消息队列,它的具体执行方式可以随着我们的配置而变化。

4.2.无broke的消息队列

  没有使用broke,直接使用socket进行通信。典型的软件为Zeromq。在无broke的消息队列中,没有broke,他们直接使用socket进行通信,消费者直接通过socket与消息队列进行一个长链接,从而进行消息队列的行为。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值