十二、消息队列-MQ

news2024/12/26 13:33:14

文章目录

  • 前言
  • 一、MQ介绍
    • 1. 背景
    • 2. 解决思路
    • 3. 解决方案
  • 二、应用场景
  • 三、常见的MQ产品
  • 四、MQ选型总结
  • 五、相关知识
    • 1. AMQP
    • 2. JMS
  • 五、如何设计实现一个消息队列
    • 1. 设计消息队列的思路
    • 2. 实现队列基本功能
      • 1. RPC通信协议
      • 2. 高可用
      • 3. 服务端承载消息堆积的能力
      • 4. 存储子系统的选择
      • 5. 消费关系解析
    • 3. 队列高级特性设计
      • 1. 可靠投递(最终一致性)
      • 2. 事务
      • 3. 性能相关
      • 4. push/pull 推拉模型简要分析


前言

消息队列

  传统应用系统出现同步耗时、系统耦合、高并发等痛点问题,支撑业务逐渐吃力。

  通过引入MQ(Message Queue)消息队列机制,实现异步调用,系统解耦,流量削峰的方式,解决传统应用系统的痛点问题。

  作为分布式系统中重要的组件,MQ本质是创建和维护应用程序间消息传输的通道。实现高性能,高可用,可伸缩和最终一致性架构。


一、MQ介绍

1. 背景

  传统应用系统出现以下痛点问题,支撑业务逐渐吃力。

  1. 痛点1-同步耗时

有些复杂的业务系统,一次用户请求可能会同步调用N个系统的接口,需要等待所有的接口都返回了,才能真正的获取执行结果。

同步耗时
这种同步接口调用的方式总耗时比较长,非常影响用户的体验,特别是在网络不稳定的情况下,极容易出现接口超时问题。

  1. 痛点2-系统耦合

很多复杂的业务系统,一般都会拆分成多个子系统。以用户下单为例,请求会先通过订单系统,然后分别调用:支付系统、库存系统、积分系统 和 物流系统。

系统耦合
系统之间耦合性太高,如果调用的任何一个子系统出现异常,整个请求都会异常,对系统的稳定性非常不利。

  1. 痛点3-高并发

有时候为了吸引用户,会搞一些促销活动,比如秒杀。
如果用户操作突增,一时间所有的请求都到数据库,可能会导致数据库无法承受这么大的压力,响应变慢或者直接挂掉。

高并发
对于这种突然出现的请求峰值,无法保证系统的稳定性。

2. 解决思路

  通过引入MQ(Message Queue)消息队列机制,实现异步调用,系统解耦,流量削峰的方式,解决传统应用系统的痛点问题。

  1. 方法1-异步调用

主业务执行结束后从属业务通过MQ,异步执行,减低业务的响应时间,提高用户体验。

异步
2. 方法2-系统解耦

主业务完成以后,发送一条MQ,其余模块异步消费MQ消息,既可实现业务,又降低模块之间的耦合。

系统解耦
3. 方法3-流量削峰

高并发情况下,业务异步处理,提供高峰期业务处理能力,避免系统瘫痪。

流量削峰

3. 解决方案

  MQ(Message Queue)是一种跨进程的通信机制,用于上下游传递消息。作为分布式系统中重要的组件,MQ本质是创建和维护应用程序间消息传输的通道,实现高性能,高可用,可伸缩和最终一致性架构。

  MQ早已成为企业IT系统内部通信的核心手段。它具有低耦合、可靠投递、广播、流量控制、最终一致性等一系列功能,成为异步RPC的主要手段之一。

  当今市面上有很多主流的MQ,如老牌的ActiveMQ、RabbitMQ,炙手可热的Kafka,阿里巴巴自主开发RocketMQ等。

MQ概念

二、应用场景

  1. 异步处理

  不用MQ,那么我们的代码必然耦合在一起,下单成功后,依次要通过RPC远程调用这几个系统,然后同步等到他们的响应才能返回给用户是否成功的结果。假设每个系统耗时200ms,那么就得花费600ms。

异步处理
2. 应用解耦

  我购买车票成功后,会收到信息提醒,但是如果短信系统故障了,客户就有可能收到不短信了,这就是各个系统之间的耦合太高了,我们应该解耦。传统的做法如下:

应用解耦
我们在订单系统产生数据后,将订单这条数据发送给MQ,就返回成功,然后让短信、邮件等系统都订阅MQ,一旦发现MQ有消息,他们主动拉取消息,然后解析,进行业务处理。
这样一来,就算你短信系统挂了,丝毫不会影响其他系统,而且如果后来想加一个新的系统,你也不用改订单系统的代码了,你只要订阅我们的MQ提供的消息就行了。

应用解耦是消息队列 MQ 的主要特点,主要目的是减少请求响应时间和解耦。主要的使用场景就是将比较耗时而且不需要即时(同步)返回结果的操作作为消息放入消息队列。同时,由于使用了消息队列MQ,只要保证消息格式不变,消息的发送方和接收方并不需要彼此联系,也不需要受对方的影响,即解耦合

  1. 流量削峰

  流量削峰也是消息队列 MQ 的常用场景,一般在秒杀或团队抢购(高并发)活动中使用广泛。

流量削峰以12306为例,假设平时可能买票的人不多,所以订单系统的QPS( 每秒查询率 )也不是很高,每秒也就处理1000个请求,但是一到节假日、春运期间可能抢票的人就非常多,并发量远远大于平时,这个时候,订单系统明显扛不住了。

为解决这些问题,可以设计高可用的MQ,让所有的请求都到MQ,缓存起来。这样一来高峰期的流量和数据都将积压在MQ中,流量高峰就被削弱了(削峰),然后我们的订单系统就避免了高并发的请求,它可以慢慢的从MQ中拉取自己能力范围内的消息就行处理。这样一来,高峰期积压的消息也终将被消费完,可以叫做填谷

三、常见的MQ产品

目前业界有很多MQ产品,比较出名的有下面这些:

  • ActiveMQ
    历史悠久的Apache开源项目。已经在很多产品中得到应用,实现了JMS1.1规范,可以和springjms轻松融合,实现了多种协议,支持持久化到数据库,对队列数较多的情况支持不好。

  • RabbitMQ
    使用erlang语言开发,性能较好,适合于企业级的开发。但是不利于做二次开发和维护。

  • RocketMQ
    阿里巴巴的MQ中间件,由java语言开发,性能非常好,能够撑住双十一的大流量,而且使用起来很简单。

  • Kafka
    Kafka是Apache下的一个子项目,是一个高性能跨语言分布式Publish/Subscribe消息队列系统,相对于ActiveMQ是一个非常轻量级的消息系统,除了性能非常好之外,还是一个工作良好的分布式系统。

  • ZeroMQ
    号称最快的消息队列系统,尤其针对大吞吐量的需求场景。扩展性好,开发比较灵活,采用C语言实现,实际上只是一个socket库的重新封装,如果做为消息队列使用,需要开发大量的代码。
    ZeroMQ仅提供非持久性的队列,也就是说如果down机,数据将会丢失。

四、MQ选型总结

需要根据具体的应用场景和需求的多维度来决定:
性能:吞吐量、并发时效性
可靠性:可用性、集群支持、持久化、性能稳定性、安全性
可维护性:管理界面等运维能力
易用性:平台熟悉度
兼容可扩展性:易于扩展
服务支持:社区活跃度

  • ActiveMQ
    作为老牌的消息队列,吞吐量比较低,也缺少大规模吞吐量场景的验证、社区活跃度也很低,数据持久化的支持一般,目前渐渐被淘汰,已经不是主流了,不太建议选择了。

  • RabbitMQ和RocketMQ
    社区比较活跃,吞吐量比较高,支持AMQP,稳定性也比较好,如果你的场景是应用需要可靠性消息传递和较高的并发,那么这两者是比较好的选择。
    要注意,rabbitMQ是使用Erlang语言开发的,而RocketMQ则使用Java语言开发,所以如果是需要深度研究掌握的话,要考虑团队中是否有Erlang工程师,如果不具备相关的人才储备的话,更建议选择RocketMQ。当然,如果只是小团队简单使用,则rabbitMQ是一个挺好的选择。

  • Kafka和Pulsar
    如果是大数据领域的实时计算、日志采集等场景,那么这两者是比较好的选择。
    Kafka经历了超大规模应用的验证,社区活跃度很高,性能也非常高,几乎是全世界这个领域的事实性的标准。
    Pulsar作为新兴的分布式消息传递系统,可扩展性强、性能高、社区活跃度也很高,最重要的是支持存储和计算分离,这在云原生下是非常出色的一项能力,并且天然支持跨数据中心的容灾,目前的应用也越来越广泛,如果集群对于持久化要求高,数据级别是超大规模,对于机器成本敏感,且支持多数据中心容灾,则建议选择Pulsar。

  • RabbitMQ:当需要性能稳定、低延时、功能强大且易于管理的方案,建议使用 RabbitMQ。

  • RocketMQ:当需要低延迟和金融级别的稳定性,且吞吐量需求较大的消息队列,系统主要场景是处理在线业务,比如在交易系统中传递订单,那 RocketMQ 是最适合的方案。

  • Kafka:对消息吞吐量需求很大,且不在乎消息偶尔丢失的情况,像收集日志、监控信息或是前端埋点这类海量数据,或是应用场景大量使用了大数据、流计算相关的开源产品,那 Kafka 是最适合的消息队列。

比较项ActiveMQRabbitMQRocketMQKafkaPulsar
单机吞吐量较低(万级)一般(万级)高(十万级)高(十万级)高(十万级)
时效性ms级us级ms级ms级以内-
可用性高(主从架构)高(主从架构)非常高(分布式架构)非常高(分布式架构)-
持久化支持(小)支持(小)支持(大)支持(大)支持(大)
顺序消息不支持不支持支持支持支持
性能稳定性一般较差一般
集群支持主备模式镜像模式(复制)主备模式Leader-Slave每台既是master也是slave,集群可扩展性强集群模式,broker无状态,易迁移,支持跨数据中心
消费模式P2P、Pub-Subdirect、fanout、topic、Headers基于Topic和MessageTag的的Pub-Sub基于Topic的Pub-Sub基于Topic的Pub-Sub
管理界面一般较好一般
计算和存储分离不支持不支持不支持不支持支持
AMQP支持支持支持支持不完全支持不完全支持
开发语言JavaErlangJavaJava/ScalaJava
维护者ApacheSpringApache(Alibaba)Apache(Confluent)Apache(StreamNative)
Star数量2.1K10.4K18.8K24.3K12.4K
Contributor126246438991600
社区活跃度较高
功能特性成熟的产品,有较多的文档;各种协议支持较好并发能力很强,性能很好,延时很低;管理界面丰富MQ功能比较完毕,扩展性佳支持主要的MQ功能,在大数据领域应用广泛可扩展性强、性能高、云原生下非常出色,支持存储和计算分离,跨数据中心的容灾

五、相关知识

1. AMQP

**高级消息队列协议(Advanced Message Queuing Protocol)**是一个网络协议。它支持符合要求的客户端应用(application)和消息中间件代理(messaging middleware broker)之间进行通信。主要特征是面向消息、队列、路由(包括点对点和发布/订阅)、可靠性、安全。AMQP是协议,类比HTTP。

2. JMS

Java消息服务(JavaMessage Service)应用程序接口,是一个Java平台中关于面向消息中间件(MOM)的 API,用于在两个应用程序之间,或分布式系统中发送消息,进行异步通信。JMS是API接口规范,类比JDBC。

五、如何设计实现一个消息队列

MQ设计思路

1. 设计消息队列的思路

  1. 一次RPC变成两次RPC

基于消息的系统模型,不一定需要broker(消息队列服务端)。
而之所以要设计一个消息队列,并且配备broker,无外乎要做两件事情:

  • 消息的转储,在更合适的时间点投递,或者通过一系列手段辅助消息最终能送达消费机。

  • 规范一种范式和通用的模式,以满足解耦、最终一致性、错峰等需求。

掰开了揉碎了看,最简单的消息队列可以做成一个消息转发器,把一次RPC做成两次RPC。发送者把消息投递到服务端(简称broker),服务端再将消息转发一手到接收端,就是这么简单。

  1. 整体设计思路如下

两次RPC加一次转储,外加消费确认的第三次RPC

(1)build一个整体的数据流,例如producer发送给broker,broker发送给consumer,consumer回复消费确认,broker删除/备份消息等。

(2)利用RPC将数据流串起来。考虑RPC的高可用性,尽量做到无状态,方便水平扩展。

(3)考虑如何承载消息堆积,在合适的时机投递消息。而处理堆积的最佳方式,就是存储,存储的选型需要综合考虑性能/可靠性和开发维护成本等诸多因素。

(4)而为了实现广播等功能,必须要维护消费关系,可以利用zk/config server等保存消费关系。

  1. 在完成了上述几个功能后,消息队列基本就实现了。然后我们可以考虑一些高级特性,如可靠投递,事务特性,性能优化等。

2. 实现队列基本功能

下面以设计消息队列时重点考虑的模块为主线,穿插灌输一些消息队列的特性实现方法,来具体分析设计实现一个消息队列时的方方面面。

1. RPC通信协议

所谓消息队列,无外乎两次RPC加一次转储,当然需要消费端最终做消费确认的情况是三次RPC。既然是RPC,需要考虑,负载均衡、服务发现、通信协议、序列化协议等等。

因为消息队列的RPC,和普通的RPC没有本质区别。利用公司现有的RPC框架:Thrift也好,Dubbo也好,或者是其他自定义的框架也好。

简单来讲,服务端提供两个RPC服务,一个用来接收消息,一个用来确认消息收到。并且做到不管哪个server收到消息和确认消息,结果一致即可。

2. 高可用

所有的高可用,是依赖于RPC和存储的高可用来做的。

而消息队列的高可用,只要保证broker接受消息和确认消息的接口是幂等的,并且consumer的几台机器处理消息是幂等的,这样就把消息队列的可用性,转交给RPC框架来处理了。

保证幂等最简单的方式莫过于共享存储。broker多机器共享一个DB或者一个分布式文件/kv系统,则处理消息自然是幂等的。另外failover可以依赖定时任务的补偿,这是消息队列本身天然就可以支持的功能。

对于不共享存储的队列,如Kafka使用分区加主备模式,就略微麻烦一些。需要保证每一个分区内的高可用性,也就是每一个分区至少要有一个主备且需要做数据的同步。

3. 服务端承载消息堆积的能力

为了满足我们错峰/流控/最终可达等一系列需求,把消息存储下来,然后选择时机投递就显得是顺理成章的了。

存储可以做成很多方式。比如存储在内存里,存储在分布式KV里,存储在磁盘里,存储在数据库里等等。但归结起来,主要有持久化和非持久化两种。

持久化的形式能更大程度地保证消息的可靠性(如断电等不可抗外力),并且理论上能承载更大限度的消息堆积(外存的空间远大于内存)。

但并不是每种消息都需要持久化存储。很多消息对于投递性能的要求大于可靠性的要求,且数量极大(如日志)。这时候,消息不落地直接暂存内存,尝试几次failover,最终投递出去。

4. 存储子系统的选择

理论上,从速度来看,文件系统 > 分布式KV(持久化) > 分布式文件系统 > 数据库,而可靠性却截然相反。

还是要从支持的业务场景出发作出最合理的选择,如果你们的消息队列是用来支持支付/交易等对可靠性要求非常高,但对性能和量的要求没有这么高,而且没有时间精力专门做文件存储系统的研究,DB是最好的选择。

分布式KV(如MongoDB,HBase)等,或者持久化的Redis,由于其编程接口较友好,性能也比较可观,如果在可靠性要求不是那么高的场景,也不失为一个不错的选择。

5. 消费关系解析

消息队列定义了一堆名词,如JMS 规范中的Topic/Queue,Kafka里面的Topic/Partition/ConsumerGroup,RabbitMQ里面的Exchange等等。

抛开现象看本质,无外乎是单播与广播的区别。所谓单播,就是点到点;而广播,是一点对多点。当然,对于互联网的大部分应用来说,组间广播、组内单播是最常见的情形。

至于广播关系的维护,一般由于消息队列本身都是集群,所以都维护在公共存储上,如config server、zookeeper等。维护广播关系所要做的事情基本是一致的:

  • 发送关系的维护。

  • 发送关系变更时的通知。

3. 队列高级特性设计

对于可靠投递(消息丢失与重复),事务,性能,不是每类消息队列都会兼顾到,所以要依照业务的需求,来仔细衡量各种特性实现的成本,利弊,最终做出最为合理的设计。

1. 可靠投递(最终一致性)

每当要发生不可靠的事情(RPC等)之前,先将消息落地,然后发送。当失败或者不知道成功失败(比如超时)时,消息状态是待发送,定时任务不停轮询所有待发送消息,最终一定可以送达。

  1. 具体来说:

    1. producer往broker发送消息之前,需要做一次落地。
    2. 请求到server后,server确保数据落地后再告诉客户端发送成功。
    3. 支持广播的消息队列需要对每个待发送的endpoint,持久化一个发送状态,直到所有endpoint状态都OK才可删除消息。

不是所有的系统都要求最终一致性或者可靠投递,比如一个论坛系统、一个招聘系统。一个重复的简历或话题被发布,可能比丢失了一个发布显得更让用户无法接受。不断重复一句话,任何基础组件要服务于业务场景。

  1. 消费确认

把消息的送达和消息的处理分开,这样才真正的实现了消息队列的本质-解耦。所以,允许消费者主动进行消费确认是必要的。当然,对于没有特殊逻辑的消息,默认Auto Ack也是可以的,但一定要允许消费方主动ack。

  1. 重复消息和顺序消息

重复消息是不可能100%避免的,除非可以允许丢失,所以绝对的顺序消息基本上是不能实现的,当然在METAQ/Kafka等pull模型的消息队列中,单线程生产/消费,排除消息丢失,也是一种顺序消息的解决方案。

一般来讲,一个主流消息队列的设计范式里,应该是不丢消息的前提下,尽量减少重复消息,不保证消息的投递顺序

重复消息,主要是两个话题:

1. 如何鉴别消息重复,并幂等的处理重复消息。
2. 一个消息队列如何尽量减少重复消息的投递。

每一个消息应该有它的唯一身份MessageId,消息到来时能够进行比对就能完成重复的鉴定。

幂等的处理消息是一门艺术,因为种种原因重复消息或者错乱的消息还是来到了,说两种通用的解决方案版本号状态机

  • 版本号

每次只接受比当前版本号大的消息。

参考TCP/IP协议,如果想让乱序的消息最后能够正确的被组织,那么就应该只接收比当前版本号大一的消息。并且在一个session周期内要一直保存各个消息的版本号。

比如应该的顺序是12,到来的顺序是21,则先把2存起来,待1到来后,再处理2,这样重复性和顺序性要求就都达到了。

  • 状态机

使用版本号的最大问题是成本太高。

对发送方必须要求消息带业务版本号。
下游必须存储消息的版本号,对于要严格保证顺序的。

该怎么解决呢?业务方只需要自己维护一个状态机,定义各种状态的流转关系。

举例子说明,假设产品本身状态是下线,1是上线消息,2是下线消息,3是上线消息,正常情况下,消息应该的到来顺序是123,但实际情况下收到的消息状态变成了3123。

那么下游收到3消息的时候,判断状态机流转是下线->上线,可以接收消息。然后收到消息1,发现是上线->上线,拒绝接收,要求重发。然后收到消息2,状态是上线->下线,于是接收这个消息。

此时无论重发的消息1或者3到来,还是可以接收。另外的重发,在一定次数拒绝后停止重发,业务正确。

  1. 中间件对于重复消息的处理

上述通用的版本号/状态机/ID判重解决方案里,哪些是消息队列该做的、哪些是消息队列不该做业务方处理的呢?其实这里没有一个完全严格的定义,但回到出发点,保证不丢失消息的情况下尽量少重复消息,消费顺序不保证

减少重复消息的关键步骤:

  • broker记录MessageId,直到投递成功后清除,重复的ID到来不做处理,这样只要发送者在清除周期内能够感知到消息投递成功,就基本不会在server端产生重复消息。

  • 对于server投递到consumer的消息,由于不确定对端是在处理过程中还是消息发送丢失的情况下,有必要记录下投递的IP地址。决定重发之前询问这个IP,消息处理成功了吗?如果询问无果,再重发。

2. 事务

满足事务的一致性特征,则必须要么都不进行,要么都能成功。解决方案从大方向上有两种:

  • 两阶段提交,分布式事务。
  • 本地事务,本地落地,补偿发送。

分布式事务存在的最大问题是成本太高,两阶段提交协议,出现仲裁down机或者单点故障,几乎是一个无解的黑洞。对于交易密集型或者I/O密集型的应用,没有办法承受这么高的网络延迟,系统复杂性。并且成熟的分布式事务构建成本太高。

如何使用本地事务解决分布式事务的问题呢?以本地和业务在一个数据库实例中建表为例子,与其它业务操作在同一个事务里,将消息插入本地数据库。如果消息入库失败,则业务回滚;如果消息入库成功,事务提交,然后发送消息。为了最终一致性,只要消息没有发送成功,就一直靠定时任务重试。

本地事务做的,是业务落地和消息落地的事务,而不是业务落地和RPC成功的事务。如果是后者,是事务嵌套RPC,是大忌,会有长事务死锁等各种风险。

消息只要成功落地,很大程度上就没有丢失的风险(磁盘物理损坏除外)。而消息只要投递到服务端确认后本地才做删除,就完成了producer->broker的可靠投递,并且当消息存储异常时,业务也是可以回滚的。

对不同的业务场景做不同的选择。另外事务的使用应该尽量低成本、透明化,可以依托于现有的成熟框架,如Spring的声明式事务做扩展。业务方只需要使用@Transactional标签即可。

3. 性能相关

  • 异步/同步

异步,归根结底你还是需要关心结果的,但可能不是当时的时间点关心,可以用轮询或者回调等方式处理结果;同步是需要当时关心的结果的。

RPC都是可以任意组合的:客户端同步对服务端异步,客户端异步对服务端异步,客户端同步对服务端同步,客户端异步对服务端同步。

对于客户端来说,同步与异步主要是拿到一个Result,还是Future(Listenable)的区别。实现方式可以是线程池,NIO或者其他事件机制。
服务端异步是需要RPC协议支持的。参考servlet 3.0规范,服务端可以吐一个future给客户端,并且在future done的时候通知客户端。

同步能够保证结果,异步能够保证效率,要合理的结合才是最好的方式。

  • 批量

生产者消费者模型。
最大的痛点是:消费者到底应该何时进行消费。消费动作都是事件驱动的。主要事件包括:

攒够了一定数量。
到达了一定时间。
队列里有新的数据到来。

为什么网络请求小包合并成大包会提高性能?主要原因有两个:

减少无谓的请求头,如果你每个请求只有几字节,而头却有几十字节,无疑效率非常低下。
减少回复的ack包个数。把请求合并后,ack包数量必然减少,确认和重发的成本就会降低。

4. push/pull 推拉模型简要分析

简要分析下 push 推和 pull 拉模型各自存在的利弊。

  • 慢消费

慢消费是push模型最大的致命伤消费者的速度比发送者的速度慢很多,势必造成消息在broker的堆积。最致命的是broker给consumer推送一堆consumer无法处理的消息,consumer不是reject就是error,然后来回踢皮球。

反观pull模式,consumer可以按需消费,无需记录每一个要发送消息的状态,只需要维护所有消息的队列和偏移量就可以了。所以对于建立索引等慢消费,消息量有限且到来的速度不均匀的情况,pull模式比较合适。

  • 消息延迟与忙等

这是pull模式最大的短板pull模的主动权在消费方,而有没有消息到来的决定权不在消费方

业界较成熟的做法是从短时间开始(不会对broker有太大负担),然后指数级增长等待。比如开始等5ms,然后10ms,然后20ms,然后40ms……直到有消息到来,然后再回到5ms。

在阿里的RocketMQ里,有一种优化的做法-长轮询,来平衡推拉模型各自的缺点。基本思路是,消费者如果尝试拉取失败,不是直接return,而是把连接挂在那里wait,服务端如果有新的消息到来,把连接notify起来,这也是不错的思路。但海量的长连接block对系统的开销还是不容小觑的,还是要合理的评估时间间隔,给wait加一个时间上限更稳妥。

  • 顺序消息

对于本身堆积能力和慢消费就是瓶颈的push模式的消息队列,实现分区下的顺序消息,成本太高了,尤其是必须每个消息消费确认后才能发下一条消息。

反观pull模式,如果想做到分区顺序消息,就相对容易很多:

producer对应partition,并且单线程。
consumer对应partition,消费确认(或批量确认),继续消费即可。

所以对于日志这种全局有序,但允许出现小误差的场景,pull模式非常合适。如果不想看到通篇乱套的日志。
需要顺序消息的场景还是比较有限的而且成本太高,请慎重考虑。


本文的引用仅限自我学习如有侵权,请联系作者删除。
参考知识
消息队列设计


本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/2254219.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新手如何做好一份技术文档

对于新手来说,编写技术文档可能是一项挑战,但这也是一个提升自己技术写作能力的绝佳机会。技术文档不仅仅是代码的补充说明,它更是团队协作和项目成功的基石。本文将为你提供一些实用的指导和建议,帮助你编写出一份高质量的技术文…

如何设置PPT以“只读方式”打开?3种简单方法!

在PPT中设置文件为“只读”模式,可以防止自己意外修改,也可以防止他人对文件内容进行修改,确保文件的安全性。根据需求不同,PPT可以设置3种不同的”只读方式“,一起来看看吧! 方式1:设置文件为只…

DICOM医学影象应用篇——多平面重建(MPR)在DICOM医学影像中的应用详解

目录 MPR(多平面重建)概述 基本原理 具体实现 代码详解 总结 MPR(多平面重建)概述 多平面重建(MPR, Multi-Planar Reconstruction)是一项用于从三维医学影像数据集中生成不同平面的二维切片的技术。通常应用于CT或MRI数据集,MPR可以帮助医…

Vue前端开发-多级路由配置

在Vue 路由数组中,允许配置多级的路由对象结构,可以是二级、三级或者更多级别,最大级别原则上没有限制,但通常最大的是三或四级,这种路由结构,称之为多级路由。 例如:一级路由地址/list&#x…

【二分查找】力扣 875. 爱吃香蕉的珂珂

一、题目 二、思路 速度 k(单位:根/小时)是存在一个取值范围的。 速度越大肯定在规定的时间之内一定会吃完全部的香蕉,但也是可以确定出一个上界的。由于只要保证一小时之内,可以吃完香蕉数目最多的那一堆的香蕉&…

C语言——指针基础

1 指针基础 怎么获得变量地址 1 如何产生一个指针变量——>类型* 标识符;int* p1;char* p2;double* p3;//不同类型的基本指针占用内存是一样的都是4个字节(32位)/8个字节(64位),都是存的地址2 数组名是数组首地址…

Leetcode day1.两数相加(2) 2.整数反转(7)

注意点:1.链表会出现其中一个已经为空,另一个缺还是有数据 2.相加时会出现进位操作 解法一、 利用队列的性质(基础不好 第一时间想到的) 很像队列的性质,先进先出,逐步计算。但是最后要换成链表样式。 …

在Ubuntu-22.04 [WSL2]中配置Docker

文章目录 0. 进入Ubuntu-22.041. 更新系统软件包2. 安装Docker相关依赖包3. 添加Docker官方GPG密钥4. 添加Docker软件源5. 安装Docker Engine5.1 更新软件包列表5.2 安装Docker相关软件包 6. 验证Docker安装是否成功6.1 查看Docker版本信息6.2 启动Docker6.3 配置镜像加速器6.4…

51单片机应用开发(进阶)---串口接收字符命令

实现目标 1、巩固UART知识; 2、掌握串口接收字符数据; 3、具体实现目标:(1)上位机串口助手发送多字符命令,单片机接收命令作相应的处理(如:openled1 即打开LED1;closeled1 即关…

【查询基础】.NET开源 ORM 框架 SqlSugar 系列

💥 .NET开源 ORM 框架 SqlSugar 系列 🎉🎉🎉 【开篇】.NET开源 ORM 框架 SqlSugar 系列【入门必看】.NET开源 ORM 框架 SqlSugar 系列【实体配置】.NET开源 ORM 框架 SqlSugar 系列【Db First】.NET开源 ORM 框架 SqlSugar 系列…

基于Matlab BP神经网络的电力负荷预测模型研究与实现

随着电力系统的复杂性和规模的不断增长,准确的电力负荷预测对于电网的稳定性和运行效率至关重要。传统的负荷预测方法依赖于历史数据和简单的统计模型,但这些方法在处理非线性和动态变化的负荷数据时,表现出较大的局限性。近年来,…

LeetCode - #150 逆波兰表达式求值

文章目录 前言1. 描述2. 示例3. 答案关于我们 前言 我们社区陆续会将顾毅(Netflix 增长黑客,《iOS 面试之道》作者,ACE 职业健身教练。)的 Swift 算法题题解整理为文字版以方便大家学习与阅读。 LeetCode 算法到目前我们已经更新…

mysql基础学习1

useradd -r -g mysql -s /bin/false mysql (-r)系统用户 不能登录 A temporary password is generated for rootlocalhost: d>#jT7rfoaz) 看是否启动 看进程 端口 直接连接 看日志 varchar (20) char(20)更耗空间 create table student_info(id int,name varchar(20),s…

PPT怎样做的更加精美

目录 PPT怎样做的更加精美 3D的GIF图片 3维空间图​编辑 结果有明显的对比 阅读高质量文献,采用他们的图 PPT怎样做的更加精美 3D的GIF图片 3维空间图 结果有明显的对比

Marvell第四季度营收预计超预期,定制芯片需求激增

芯片制造商Marvell Technology(美满电子科技)(MRVL)在周二发布了强劲的业绩预告,预计第四季度的营收将超过市场预期,得益于企业对其定制人工智能芯片的需求激增。随着人工智能技术的快速发展,特…

爬虫第四篇:Xpath 路径表达式全解析:从网页基础到爬取百度贴吧图片实战

简介:本文围绕 Xpath 路径表达式展开讲解,先是介绍了网页相关基础如 html、css、vue 以及前后端分离的概念与示例,包括各部分的结构、作用及简单代码展示,随后详细阐述了 xml 的节点关系、选取节点、谓语等理论知识,最…

使用lumerical脚本语言创建弯曲波导并进行数据分析(纯代码实现)

本文使用lumerical脚本语言创建弯曲波导、设置有限差分时域(FDTD)模拟、改变波导弯曲半径计算损耗、绘制图像展示电场强度分布情况及对具有不同弯曲半径的波导进行一系列模拟和分析操作(代码均有注释讲解)。 一、创建弯曲波导 1.1 基本结构讲解 (1)包层(Clad) 在波导结…

HarmonyOS4+NEXT星河版入门与项目实战(23)------实现手机游戏摇杆功能

文章目录 1、案例效果2、案例实现1、代码实现2、代码解释4、总结1、案例效果 2、案例实现 1、代码实现 代码如下(示例): import router from @ohos.router import {ResizeDirection } from @ohos.UiTest import curves

Redis面试专题-持久化

前言 开始Redis面试知识的复习和资料的收集(收集和参考了网上的优质文章),本篇文章会不断更新,本系列文章主要分为两部分,一部分是该专题所涉及的相关基础知识,另一部分是面试题与思考题,大部分…

Blender导入下载好的fbx模型像的骨骼像针戳/像刺猬

为什么我下载下来的骨骼模型和我自己绑定的模型骨骼朝向完全不一样 左边是下载的模型 右边是我自己绑定的模型 左边的模型刚刚感觉都是像针一样往外戳的,像刺猬一样那种。 解决方法勾选自动骨骼坐标系