消息队列MQ

本文参考 https://github.com/doocs/advanced-java/blob/master/docs/high-concurrency/why-mq.md

https://www.yuque.com/liangxinjiang/powiyk/akyt35

一. 消息队列的组成

1. Broker 消息服务器,作为server提供消息核心服务

2. Producer 消息生产者,业务的发起方,负责生产消息传输给broker

3. Consumer 消息消费者,业务的处理方,负责从broker获取消息并进行业务逻辑处理

4. Topic 队列,PTP(Point-to-Point点对点)模式下,特定生产者向特定queue发送消息,消费者订阅特定的Queue完成指定消息的接收

5. Message 消息体,根据不同通信协议定义的固定格式进行编码的数据包,来封装业务数据,实现消息的传输

二:模式分类

1. 点对点 Point-to-Point     使用queue作为通信载体

 生产者发送消息到queue中,然后消费者从queue中拉取并进行消费。消息被消费后,queue中不再存储,所以消费者不可能消费到已经被消费的消息,Queue支持存在多个消费者,但是对一个消息而言,只有一个消费者可以消费。

2. 发布/订阅模式

Pub/Sub 发布订阅(广播):使用topic作为通信载体

消息生产者将消息发布到topic中,同时有多个消息消费者(订阅)消费该消息。和点对点方式不同,发布到topic的消息会被所有订阅者消费。 

queue实现了负载均衡,将producer生产的消息发送到消息队列中,由多个消费者进行消费,但一个消费者只能被一个消费者接受,当没有消费者可用时,这个消息会被保存知道有一个可用的消费者。

topic 实现了发布和订阅,当你发布一个消息,所有订阅这个topic的服务都能得到这个消息,所以从1到N个订阅者都能得到一个消息的拷贝。

二. 为什么使用消息队列

根据具体的业务场景来说。解耦,冗余,异步通信,扩展性,过载保护,可恢复性,顺序保证,缓冲,数据流处理,比较重要的有:解耦、异步、削峰(过载保护)

1. 解耦

场景1:ABCD四个系统(模块),A系统发送数据到BCD三个系统,系统之间通过接口调用发送,这时候C系统,不需要A系统发送数据了,而新增了一个E系统需要A系统发送数据。

这个场景下。A系统与其他系统严重耦合,A系统发送数据时,要时刻考虑其他系统是否正常、要不要进行重发、发送的数据是否要保存起来避免出错。而这些问题在使用MQ之后都可以进行更好的处理。

A系统发送一条数据,发送到MQ中去,哪个系统需要就自己去MQ里消费,如果添加新系统,新系统也去MQ消费即可,哪个系统不需要数据了,就取消该系统对MQ消息的消费。通过使用一个MQ,Pub/Sub发布订阅消息模型,A系统就不需要考虑给谁发送,失败超时等问题,与其他系统彻底解耦。

2. 异步

场景2:A 系统接受一个请求,需要在自己本地写库,还需要BCD三个系统写库,自己本地写库需要3ms,BCD三个系统写库分别需要300ms,450ms,200ms。最终请求总延时是 3 + 300 + 450 + 200 = 953ms,接近1s,这会导致用户体验很差,无法接受。

如果使用MQ,那么A系统连续发送3条消息到MQ队列中,假如耗时5ms,A系统从接受一个请求到返回响应给用户,总时长是 3+5=8ms,用户几乎感知不到,这就不会影响到用户体验。  

3. 削峰 过载保护

场景3:A系统每天1:00~2:00,每秒并发请求数量就会暴增到5k+条,其他时间正常,每秒并发请求数量就50~100条,A系统是直接基于MySQL的,大量的请求涌入MySQL。每秒钟需要对MySQL执行约5k+条SQL,但一般的MySQL无法实现每秒5k+的操作,如果每秒请求数量到5k的话,可能会直接把MySQL给干死,导致系统game over。而且高峰期一过,每秒钟的请求数量就降下来了,对整个系统无任何压力。

使用MQ,每秒5k个请求写入MQ,A系统每秒钟最多处理2k个请求,因为MySQL的性能限制它每秒钟只能处理这么多,那么A系统从MQ中拉取请求,每秒钟拉取2k个请求进行处理,不超过自己每秒能处理的最大请求数量就OK,这样下来,即便是高峰期的时候,A系统也不会挂掉,这样的结果是MQ中会积压大量的请求,这个短暂的高峰期是没问题的,因为高峰期过了之后,每秒钟就50~100个请求进入MQ,但A系统依旧会按照每秒2k个请求的速度处理,所以高峰期过后,MQ中积压的消息A系统会快速的给解决掉。

可恢复性:系统的一部分组件失效时,不会影响到整个系统。消息队列降低了进程间的耦合度,所以即使一个处理消息的进程挂掉,加入队列中的消息仍然可以在系统恢复后被处理。

数据流处理:分布式系统产生的海量数据流,如:业务日志、监控数据、用户行为等,针对这些数据流进行实时或批量数据采集,然后进行大数据分析是当前互联网的必备集数,通过消息队列完成此类数据收集是最好的选择。

三. 消息队列有什么优缺点 

消息队列MQ是一种非常复杂得架构,引入它有很多好处,但是也得针对它带来的坏处做各种额外的处理来进行规避,这样就使得系统复杂度直线上升,但有些情况下你还不得不用。

优点:特殊场景下的应用,解耦、异步(提高系统响应时间)、削峰、为大数据处理框架提供服务

缺点:

1. 系统可用性降低

系统引入的外部依赖越多,越容易出现问题,加入MQ后,MQ一旦出现问题,整套系统全部崩溃,只能gg,这时就要考虑MQ的高可用。

2. 系统复杂度提高

加入MQ后,你得考虑如何保证消息不被重复消费、如何处理消息丢失、如何保证消息传递的顺序性等一堆问题,头大。

3. 一致性问题(****)

A系统处理完了直接返回成功了,客户以为这个请求就成功了,但问题是,你A系统处理完了,还需要BCD系统进行写库操作,然后BC系统写库成功,D系统写库失败了,咋整,数据不一致了。

四. MQ高可用(RabbitMQ、Kafka):

RabbitMQ 是基于主从(非分布式)做高可用性的。有三种模式:单机模式,普通集群模式,镜像集群模式。

单机模式:自己玩玩儿。

普通集群模式:在多台机器上启动多个RabbitMQ实例,每个机器启动一个。你创建的queue,只会放在一个RabbitMQ实例上,但是每个实例都同步queue的元数据(元数据:queue的一些配置信息,通过元数据,可以找到queue所在实例)。你消费的时候,实际上如果连接到了另外一个实例,那么那个实例会从queue所在实例上拉取数据过来。这种方式很麻烦,也没做到所谓的分布式,就是个普通集群。这导致你要么消费者每次随机连接一个实例然后拉取数据,要么固定连接那个queue所在实例消费数据前者有数据拉取的开销,后者导致单例性能瓶颈。而且如果那个放queue的实例宕机了,会导致接下来其他实例就无法从那个实例拉取,如果你开启了消息持久化,让RabbitMQ落地存储消息的话,消息不一定会丢,得等到这个实例恢复了,才可以继续从这个实例拉取数据。所以这个方案没有高可用性,主要是用来提高吞吐量的,让集群中多个节点来服务某个queue的读写操作

 

镜像集群模式(HA 高可用性):该模式下,创建的queue会存放于多个实例上,即每个RabbitMQ节点都有这个queue的一个完整镜像,包含queue的全部数据。每次写消息到queue的时候,都会自动把消息同步到多个实例的queue上,读的时候任意一个节点都可以。

优点:任何一个机器宕机,不影响其他机器与业务。

缺点在于网络带宽压力 和消耗很重,并且扩展性差(某个queue负载很重,添加机器,新机器也要同步这个queue所包含的所有数据,无法线性扩展)。

 

Kafka 结构图:

Kafka:有多个broker组成,每个broker可以看成一个节点;创建一个topic,这个topic可以划分为多个partition(分区),每个partition存放一部分数据,然后每个partition存放在不同broker上。(分布式消息队列,一个topic的数据分散放在多个机器上,每个机器上放一部分数据)

kafka 0.8版本之后提供了HA(高可用)机制 即副本机制,每个partition的数据会同步到其他机器上,形成自己的多个replica副本。然后所有的副本选举出一个leader出来,其他的副本就是follower,写的时候 leader 负责把数据同步到所有的follower上,读的时候直接读取 leader 上的数据即可,kafka会均匀的将一个partition的所有副本分布在不同的机器上,提高容错性。(高可用)

某个broker宕机了,这个broker上的partition数据在其他机器上都有副本,如果这个broker上有某个partition的 leader ,那么此时会从follower中重新选举出一个 leader ,继续读取这个新 leader 就行。

写数据的时候,生产者就写 leader,然后 leader 将数据落地写本地磁盘,接着其他 follower 自己主动从 leader 来 pull 数据。一旦所有 follower 同步好数据了,就会发送 ack 给 leader,leader 收到所有 follower 的 ack 之后,就会返回写成功的消息给生产者。(当然,这只是其中一种模式,还可以适当调整这个行为)

消费的时候,只会从 leader 去读,但是只有当一个消息已经被所有 follower 都同步成功返回 ack 的时候,这个消息才会被消费者读到。

生产者如何保证数据的完整性?

设置发送数据是否需要服务端的反馈,有三个值0,1,-1

0: producer不会等待broker发送ack

1: 当leader接收到消息之后发送ack

-1: 当所有的follower都同步消息成功后发送ack    request.required.acks=0

kafka只能保证一个partition中的消息被某个consumer消费时是顺序的;事实上,从Topic角度来说,当有多个partitions时,消息仍不是全局有序的。

分数阶傅里叶变换(Fractional Fourier Transform, FRFT)是对传统傅里叶变换的拓展,它通过非整数阶的变换方式,能够更有效地处理非线性信号以及涉及时频局部化的问题。在信号处理领域,FRFT尤其适用于分析非平稳信号,例如在雷达、声纳和通信系统中,对线性调频(Linear Frequency Modulation, LFM)信号的分析具有显著优势。LFM信号是一种频率随时间线性变化的信号,因其具有宽频带和良好的时频分辨率,被广泛应用于雷达和通信系统。FRFT能够更精准地捕捉LFM信号的时间和频率信息,相比普通傅里叶变换,其性能更为出色。 MATLAB是一种强大的数值计算和科学计算工具,拥有丰富的函数库和用户友好的界面。在MATLAB中实现FRFT,通常需要编写自定义函数或利用信号处理工具箱中的相关函数。例如,一个名为“frft”的文件可能是用于执行分数阶傅里叶变换的MATLAB脚本或函数,并展示其在信号处理中的应用。FRFT的正确性验证通常通过对比变换前后信号的特性来完成,比如评估信号的重构质量、信噪比等。具体而言,可以通过计算原始信号与经过FRFT处理后的信号之间的相似度,或者对比LFM信号的关键参数(如初始频率、扫频率和持续时间)是否在变换后得到准确恢复。 在MATLAB代码实现中,通常包含以下步骤:首先,生成LFM信号模型,设定其初始频率、扫频率、持续时间和采样率等参数;其次,利用自定义的frft函数对LFM信号进行分数阶傅里叶变换;接着,使用MATLAB的可视化工具(如plot或imagesc)展示原始信号的时域和频域表示,以及FRFT后的结果,以便直观对比;最后,通过计算均方误差、峰值信噪比等指标来评估FRFT的性能。深入理解FRFT的数学原理并结合MATLAB编程技巧,可以实现对LFM信号的有效分析和处理。这个代码示例不仅展示了理论知识在
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值