消息队列(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与消息队列进行一个长链接,从而进行消息队列的行为。

消息队列(Message Query)的初学习的更多相关文章

  1. rabbitMQ消息队列 – Message方法解析

    消息的创建由AMQPMessage对象来创建$message = new AMQPMessage("消息内容");是不是很简单. 后边是一个数组.可以对消息进行一些特殊配置$mes ...

  2. Linux:进程通信之消息队列Message实例

    /*send.c*/ /*send.c*/ #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h&g ...

  3. .NET 云原生架构师训练营(模块二 基础巩固 消息队列 介绍与基础)--学习笔记

    2.6.1 消息队列 -- 介绍 主要使用场景 队列的三种形式 消息队列的优点 主要使用场景 典型的异步处理 流量削锋 应用解耦 队列的三种形式 点对点 工作队列 发布与订阅 消息队列的优点 1.屏蔽 ...

  4. 浅谈消息队列 Message Queue

    消息队列:在消息传递的过程中暂时保存消息的容器,充当发送者和接受者的中间人 消息队列的基本操作 using System; using System.Messaging; namespace MQ { ...

  5. 微软消息队列-MicroSoft Message Queue(MSMQ)队列的C#使用

    目录 定义的接口 接口实现 建立队列工厂 写入队列 获取消息 什么是MSMQ Message Queuing(MSMQ) 是微软开发的消息中间件,可应用于程序内部或程序之间的异步通信.主要的机制是:消 ...

  6. RabbitMQ消息队列(一)-RabbitMQ的优劣势及产生背景

    本篇并没有直接讲到技术,例如没有先写个Helloword.我想在选择了解或者学习一门技术之前先要明白为什么要现在这个技术而不是其他的,以免到最后发现自己学错了.同时如果已经确定就是他,最好先要了解下技 ...

  7. RabbitMQ 消息队列 二

    一:查看MQ的用户角色 rabbitmqctl list_users 二:添加新的角色,并授予权限 rabbitmqctl add_user xiaoyao 123456 rabbitmqctl se ...

  8. 消息队列ActiveMQ的使用详解

    通过上一篇文章 <消息队列深入解析>,我们已经消息队列是什么.使用消息队列的好处以及常见消息队列的简单介绍. 这一篇文章,主要带大家详细了解一下消息队列ActiveMQ的使用. 学习消息队 ...

  9. WCF分布式开发步步为赢(13):WCF服务离线操作与消息队列MSMQ

    之前曾经写过一个关于MSMQ消息队列的文章:WCF分布式开发必备知识(1):MSMQ消息队列 ,当时的目的也是用它来作为学习WCF 消息队列MSMQ编程的基础文章.在那篇文章里,我们详细介绍了MSMQ ...

随机推荐

  1. 一天五道Java面试题----第七天(mysql索引结构,各自的优劣--------->事务的基本特性和隔离级别)

    这里是参考B站上的大佬做的面试题笔记.大家也可以去看视频讲解!!! 文章目录 1 .mysql索引结构,各自的优劣 2 .索引的设计原则 3 .mysql锁的类型有哪些 4 .mysql执行计划怎么看 ...

  2. Java函数式编程:二、高阶函数,闭包,函数组合以及柯里化

    承接上文:Java函数式编程:一.函数式接口,lambda表达式和方法引用 这次来聊聊函数式编程中其他的几个比较重要的概念和技术,从而使得我们能更深刻的掌握Java中的函数式编程. 本篇博客主要聊聊以 ...

  3. jQuery+bootstrap实现有省略号的数据分页

    1.前言 在前端通过ajax请求数据后,可以通过bootstrap实现分页.由于bootstrap只提供分页的按钮的样式.数据分页我们需要实现页码跳转,上一页下一页,数据过多显示省略号,点击省略号能快 ...

  4. mysql网上知识

    MySQL学习笔记 登录和退出MySQL服务器 # 登录MySQL $ mysql -u root -p12345612 # 退出MySQL数据库服务器 exit; 基本语法 -- 显示所有数据库 s ...

  5. CSS布局秘籍(2)-6脉神剑

    HTML系列: 人人都懂的HTML基础知识-HTML教程(1) HTML元素大全(1) HTML元素大全(2)-表单 CSS系列: CSS基础知识筑基 常用CSS样式属性 CSS选择器大全48式 CS ...

  6. 记录一次Oracle导入数据库失败的解决办法,最终报错:UDI-04045、ORA-04045、ORA-01775

    费了很大的工夫,终于解决了.做个记录. ******************************************************************************** ...

  7. for in 和 for of 的区别和v-for指令的三种使用方法

    for...in 循环:只能获得对象的键名,不能获得键值 for...of 循环:允许遍历获得键值 var arr = ['red', 'green', 'blue'] for(let item in ...

  8. 【笔记】入门DP(Ⅱ)

    0X00 P1433 吃奶酪 状压 \(DP\),把经过的点压缩成01串.若第 \(i\) 位为 \(0\) 表示未到达,为 \(1\) 则表示已到达. 用 \(f[i][j]\) 表示以 \(i\) ...

  9. mindxdl--common--head_handler.go

    // Copyright (c) 2021. Huawei Technologies Co., Ltd. All rights reserved.// Package common the commo ...

  10. devexpress中dockManager保存布局后恢复不正常

    在使用dockManager保存布局后进行恢复发现不正常,与中间的gridcontorl接触的都不行.gridcontorl设置的填充是fill 所以在在界面上再添加一个PanelControl控件并 ...