当前位置: 首页 > news >正文

从入门到精通:RabbitMQ的深度探索与实战应用

目录

一、RabbitMQ 初相识

 二、基础概念速览

(一)消息队列是什么

(二)RabbitMQ 核心组件

 三、RabbitMQ 基本使用

(一)安装与环境搭建

(二)简单示例

(三)工作队列模式(Work Queue)

(四)交换机类型详解

 四、RabbitMQ 高级用法

(一)消息可靠性投递

(二)死信队列(Dead Letter Queue)

(三)延迟队列(Delay Queue)

(四)优先级队列(Priority Queue)


一、RabbitMQ 初相识

在当今分布式系统大行其道的技术领域中,RabbitMQ 宛如一颗璀璨的明星,占据着举足轻重的地位。它是一款开源的消息代理软件,犹如一座桥梁,在不同的应用程序之间搭建起高效通信的通道。

RabbitMQ 基于高级消息队列协议(AMQP),实现了生产者与消费者之间的解耦,让应用程序能够更加专注于自身的业务逻辑,而无需过多担忧消息传递的复杂细节。它能够高效地处理大量的消息,无论是高并发的互联网应用,还是对数据一致性要求极高的金融系统,RabbitMQ 都能凭借其出色的性能和可靠性,为系统的稳定运行提供坚实保障。

对于咱们程序员来说,掌握 RabbitMQ 的使用方法,无疑是为自己的技术栈增添了一件强大的武器。它不仅能够帮助我们解决分布式系统中的消息传递难题,还能极大地提升系统的性能和可扩展性。接下来,就让我们一起深入探索 RabbitMQ 的奥秘,从基础用法到高级技巧,逐步揭开它神秘的面纱。

 二、基础概念速览

 

 

(一)消息队列是什么

消息队列,从字面意义理解,就是一个存放消息的队列。在计算机系统中,它是一种进程间通信或同一进程的不同线程间通信的方式,用于在不同应用程序、服务或组件之间传递消息。其核心原理基于先进先出(FIFO)的顺序,即先进入队列的消息会先被处理。

消息队列在系统中扮演着至关重要的角色,有着多方面的作用。在应用间异步通信场景下,比如电商系统中,用户下单后,订单信息可通过消息队列异步发送给库存系统、物流系统等进行后续处理,此时生产者(下单系统)无需等待消费者(库存、物流系统)处理完成,就能立即响应用户,极大地提升了系统的响应速度 。

解耦方面,以大型微服务架构为例,各个微服务之间通过消息队列进行通信。当某个微服务进行升级或修改时,只要消息格式不变,就不会影响其他依赖它的微服务正常运行,从而降低了系统间的耦合度,提高了系统的可维护性和可扩展性。

在削峰填谷场景中,在电商促销活动时,短时间内会产生大量的订单请求。消息队列可以将这些请求暂存起来,按照系统能够处理的速度逐步发送给后端服务进行处理,避免因瞬间高并发流量压垮系统,同时在低峰期,又能处理之前积压的请求,充分利用系统资源。

(二)RabbitMQ 核心组件

  1. 生产者(Producer):消息的发送方,负责产生消息并将其发送到 RabbitMQ 服务器。在实际应用中,比如一个订单生成系统,当用户完成下单操作后,该系统就作为生产者,将订单相关的消息发送到 RabbitMQ,这些消息可能包含订单编号、商品信息、用户信息等。
  1. 消费者(Consumer):消息的接收方,从 RabbitMQ 服务器获取消息并进行相应的处理。接着上面订单的例子,库存管理系统可以作为消费者,从 RabbitMQ 中接收订单消息,然后根据消息内容进行库存扣减等操作。
  1. 队列(Queue):消息的存储地,它类似于一个缓冲区,生产者发送的消息会被放入队列中等待处理。队列可以存储大量的消息,并且支持持久化,即使 RabbitMQ 服务器重启,持久化队列中的消息也不会丢失。多个生产者可以向同一个队列发送消息,同时多个消费者也可以从同一个队列中获取消息,实现了消息的多对多传递。
  1. 交换机(Exchange):接收来自生产者的消息,并根据路由规则将消息发送到一个或多个队列。RabbitMQ 提供了多种类型的交换机,如直接交换机(Direct Exchange)、主题交换机(Topic Exchange)、扇出交换机(Fanout Exchange)和头交换机(Headers Exchange) 。
  1. 路由键(Routing Key):在消息发送过程中,生产者会为每条消息指定一个路由键,交换机根据这个路由键和自身的类型及绑定规则,决定将消息发送到哪些队列。例如在直接交换机中,如果路由键与队列绑定的键完全匹配,消息就会被发送到对应的队列。

 三、RabbitMQ 基本使用

 

 

(一)安装与环境搭建

RabbitMQ 的安装步骤会因操作系统的不同而有所差异。在 Windows 系统中 ,首先需前往 RabbitMQ 官网下载适合 Windows 的安装程序(.exe 文件)。鉴于 RabbitMQ 是基于 Erlang 开发的,在安装 RabbitMQ 之前,必须先安装对应的 Erlang 环境。安装完成后,可通过开始菜单中的快捷方式启动 RabbitMQ 服务,也能使用命令行工具(在安装目录下的 sbin 文件夹中)来启动和管理 RabbitMQ。

在 Linux 系统(如 Ubuntu)中,打开终端,执行 “sudo apt - get update” 命令更新系统软件包列表,以确保系统的软件包索引是最新的,进而正确安装 RabbitMQ 相关的软件包。接着执行 “sudo apt - get install erlang - base erlang - asn1 erlang - crypto erlang - ssl erlang - inets erlang - public - key erlang - syntax - tools” 命令安装 Erlang 环境。完成后,执行 “sudo apt - get install rabbitmq - server” 命令安装 RabbitMQ Server,安装过程中,系统会自动下载并配置 RabbitMQ 服务 。

安装过程中,有诸多注意事项。要特别留意 RabbitMQ 与 Erlang 版本的兼容性,不同版本的 RabbitMQ 对 Erlang 版本有特定要求,可在 RabbitMQ 官网查看版本对应关系。安装完成后,建议修改默认的用户密码,增强安全性,并根据实际需求进行虚拟主机、用户权限等的配置。

(二)简单示例

以下通过代码示例,展示生产者向队列发送消息,消费者从队列接收消息的过程。以 Python 语言为例,使用 pika 库来操作 RabbitMQ。

在生产者代码中,首先建立到 RabbitMQ 服务器的连接,代码如下:

 import pika# 建立到RabbitMQ服务器的连接connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))channel = connection.channel()

 接着声明一个队列,若队列不存在则创建它。这里将队列命名为 'hello',并通过设置 durable = True 来实现队列持久化,这样在 RabbitMQ 服务器重启后,队列和其中的消息不会丢失。代码如下:

# 声明一个队列以便发送消息,如果队列不存在则创建。这里的队列名是 'hello'。# 可以通过在queue_declare方法中设置durable=True来实现队列持久化。这样在RabbitMQ服务器重启后,队列和其中的消息不会丢失。channel.queue_declare(queue='hello', durable=True)

 随后发布一条消息到名为 'hello' 的队列中,代码如下:

# 发布一条消息到名为 'hello' 的队列中。channel.basic_publish(exchange='', routing_key='hello', body='Hello World!')

最后关闭连接,代码如下: 

# 关闭连接connection.close()

 在消费者代码中,同样先建立到 RabbitMQ 服务器的连接,代码如下:

# 关闭连接connection.close()

 在消费者代码中,同样先建立到 RabbitMQ 服务器的连接,代码如下:

import pika# 建立到RabbitMQ服务器的连接connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))channel = connection.channel()

 然后声明一个队列以便从中接收消息,这里的队列名同样为 'hello',代码如下:

# 声明一个队列以便从中接收消息。channel.queue_declare(queue='hello', durable=True)

 接着定义一个回调函数来处理接收到的消息,在回调函数中,将接收到的消息内容打印出来,代码如下:

# 定义一个回调函数来处理接收到的消息def callback(ch, method, properties, body):print(f"Received {body}")

 最后,告诉 RabbitMQ 使用上面定义的回调函数来接收来自 'hello' 队列的消息,并开始接收消息,进入永久循环,等待消息并在需要时运行回调函数,代码如下:

# 告诉RabbitMQ使用上面定义的回调函数来接收来自 'hello' 队列的消息。# callback的参数都是由RabbitMQ自动提供的,不需要手动传递它们。# 当消息到达队列并且basic_consume方法已经注册了回调函数时,RabbitMQ会负责调用回调函数,并传递相应的参数。channel.basic_consume(queue='hello', on_message_callback=callback, auto_ack=True)# 开始接收消息,并进入永久循环,等待消息并在需要时运行回调函数。print('Waiting for messages. To exit press CTRL+C')channel.start_consuming()

 在上述代码中,生产者和消费者都声明了名为 'hello' 的队列,当生产者发送消息到该队列后,消费者便能从队列中获取并处理消息。

 

(三)工作队列模式(Work Queue)

工作队列模式(Work Queue)是 RabbitMQ 中一种常用的模式。在这种模式下,存在一个生产者和多个消费者,生产者将消息发送到队列中,多个消费者可以同时从队列中获取消息进行处理。但需要注意的是,每条消息只会被一个消费者获取并处理 。

它的适用场景非常广泛。以电商订单处理系统为例,在促销活动期间,会产生大量的订单。此时,可以将订单消息发送到工作队列中,由多个订单处理服务实例(消费者)同时从队列中获取订单消息进行处理,这样能大大提高订单处理的速度,避免单个服务实例因处理大量订单而出现性能瓶颈。

在工作队列模式中,RabbitMQ 默认采用轮询分发(Round - robin)的方式将消息分配给消费者。也就是说,它会按照顺序依次将消息发送给每个消费者,而不考虑消费者的处理能力。这种方式在某些情况下可能不太合理,比如当某个消费者的处理速度较慢时,会导致它积压大量的消息,而其他处理速度快的消费者却处于空闲状态。

为了解决这个问题,可以采用公平分发(Fair dispatch)的方式。实现公平分发,需要在消费者端进行一些配置。在 Python 中,使用 pika 库时,可以通过设置 basic_qos 方法的参数来实现。例如,设置 basic_qos (prefetch_count = 1),表示每个消费者在处理完当前消息之前,RabbitMQ 不会再给它发送新的消息,从而确保每个消费者都能合理地获取消息,避免出现消息分配不均的情况 。实现公平分发的示例代码如下:

 import pika# 建立到RabbitMQ服务器的连接connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))channel = connection.channel()# 声明一个队列以便从中接收消息。channel.queue_declare(queue='hello', durable=True)# 设置每个消费者在处理完当前消息之前,RabbitMQ不会再给它发送新的消息channel.basic_qos(prefetch_count = 1)# 定义一个回调函数来处理接收到的消息def callback(ch, method, properties, body):print(f"Received {body}")# 手动确认消息已被处理ch.basic_ack(delivery_tag = method.delivery_tag)# 告诉RabbitMQ使用上面定义的回调函数来接收来自 'hello' 队列的消息。# 这里设置auto_ack=False,需要手动确认消息channel.basic_consume(queue='hello', on_message_callback=callback, auto_ack=False)# 开始接收消息,并进入永久循环,等待消息并在需要时运行回调函数。print('Waiting for messages. To exit press CTRL+C')channel.start_consuming()

 在上述代码中,通过设置 basic_qos (prefetch_count = 1) 实现了公平分发,并且将 basic_consume 的 auto_ack 参数设置为 False,改为手动确认消息,即消费者在处理完消息后,通过调用 basic_ack 方法来告诉 RabbitMQ 消息已被处理,这样 RabbitMQ 才会将该消息从队列中移除。

 

(四)交换机类型详解

  1. 直连交换机(Direct Exchange):直连交换机是 RabbitMQ 中最基础的交换机类型之一。它的工作机制相对简单,会根据消息的路由键(Routing Key)将消息精确地发送到与之绑定的队列中。当一个队列与直连交换机绑定时,会指定一个绑定键(Binding Key)。只有当消息的路由键与绑定键完全匹配时,该消息才会被路由到对应的队列 。假设我们有一个订单处理系统,其中有一个队列专门用于处理紧急订单,我们可以将这个队列与直连交换机进行绑定,并设置绑定键为 “urgent_order”。当生产者发送一条消息,并且消息的路由键也设置为 “urgent_order” 时,这条消息就会被直连交换机准确地发送到处理紧急订单的队列中。直连交换机适用于需要精确匹配路由键的场景,能够确保消息被准确无误地投递到目标队列 。
  1. 扇形交换机(Fanout Exchange):扇形交换机的特点是不关心消息的路由键,它会将接收到的所有消息广播到所有与它绑定的队列中。在实际应用中,这种交换机常用于广播通知的场景。以一个新闻发布系统为例,当有新的新闻发布时,我们希望将这条新闻同时推送给多个不同的订阅者队列,如手机端订阅者队列、PC 端订阅者队列、邮件订阅者队列等。此时,我们可以使用扇形交换机,将这些队列都与扇形交换机进行绑定。当生产者向扇形交换机发送新闻消息时,无论消息的路由键是什么,扇形交换机都会将该消息广播到所有绑定的队列中,从而实现新闻的广泛传播 。
  1. 主题交换机(Topic Exchange):主题交换机是一种功能非常强大且灵活的交换机类型,它支持通配符的路由规则。在主题交换机中,路由键和绑定键都是由多个单词组成,单词之间用点号(.)分隔。它支持两种通配符:星号()和井号(#)。星号()表示匹配一个单词,井号(#)表示匹配零个或多个单词。例如,有一个日志处理系统,我们可以创建多个队列,分别用于处理不同级别的日志,如 error 日志队列、warning 日志队列、info 日志队列等。然后,将这些队列与主题交换机进行绑定,并设置相应的绑定键。将 error 日志队列的绑定键设置为 “logs.error.*”,表示匹配所有以 “logs.error.” 开头的路由键;将 warning 日志队列的绑定键设置为 “logs.warning”;将 info 日志队列的绑定键设置为 “logs.#”,表示匹配所有以 “logs.” 开头的路由键。当生产者发送一条消息,路由键为 “logs.error.database_connection_failed” 时,根据主题交换机的通配符规则,这条消息会被发送到 error 日志队列中;若路由键为 “logs.warning.memory_low”,则会被发送到 warning 日志队列中;若路由键为 “logs.info.system_started”,会被发送到 info 日志队列中 。主题交换机适用于需要根据消息的类别或特征进行灵活路由的复杂场景,通过合理设置通配符,可以实现非常精细的消息路由控制 。

 四、RabbitMQ 高级用法

一)消息可靠性投递

在分布式系统中,消息的可靠投递至关重要。RabbitMQ 提供了多种机制来确保消息从生产者到消费者的可靠传输 。

  1. 确认模式(Confirm 模式):生产者可以通过开启 Confirm 模式,来确认消息是否成功发送到交换机。在使用 RabbitMQ 的 Java 客户端时,通过调用 channel.confirmSelect() 方法开启 Confirm 模式。当消息成功到达交换机后,RabbitMQ 会发送一个确认信号给生产者。生产者可以通过 channel.waitForConfirms() 方法来同步等待确认,也可以通过添加确认监听器 channel.addConfirmListener(ConfirmListener listener) 来异步处理确认消息。以异步处理为例,示例代码如下:
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Producer {public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();// 开启Confirm模式channel.confirmSelect();// 添加确认监听器channel.addConfirmListener(new ConfirmListener() {@Overridepublic void handleAck(long deliveryTag, boolean multiple) throws IOException {System.out.println("消息发送成功,deliveryTag: " + deliveryTag + ", multiple: " + multiple);}@Overridepublic void handleNack(long deliveryTag, boolean multiple) throws IOException {System.out.println("消息发送失败,deliveryTag: " + deliveryTag + ", multiple: " + multiple);}});String exchangeName = "";String routingKey = "test";String message = "Hello, RabbitMQ!";channel.basicPublish(exchangeName, routingKey, null, message.getBytes());// 关闭资源channel.close();connection.close();}
}

在上述代码中,通过 channel.addConfirmListener 添加了确认监听器,在 handleAck 方法中处理消息成功发送到交换机的情况,在 handleNack 方法中处理消息发送失败的情况 。

  1. 退回模式(Return 模式):当交换机无法将消息路由到队列时,默认情况下消息会被丢弃。但通过开启 Return 模式,可以让交换机将无法路由的消息退回给生产者。在 Java 客户端中,需要设置 channel.basicPublish 方法的 mandatory 参数为 true,并添加 ReturnListener 监听器来处理退回的消息。示例代码如下:

 

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.ConfirmListener;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Producer {public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();// 开启Confirm模式channel.confirmSelect();// 添加确认监听器channel.addConfirmListener(new ConfirmListener() {@Overridepublic void handleAck(long deliveryTag, boolean multiple) throws IOException {System.out.println("消息发送成功,deliveryTag: " + deliveryTag + ", multiple: " + multiple);}@Overridepublic void handleNack(long deliveryTag, boolean multiple) throws IOException {System.out.println("消息发送失败,deliveryTag: " + deliveryTag + ", multiple: " + multiple);}});String exchangeName = "";String routingKey = "test";String message = "Hello, RabbitMQ!";channel.basicPublish(exchangeName, routingKey, null, message.getBytes());// 关闭资源channel.close();connection.close();}
}
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.ReturnListener;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Producer {public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();// 添加Return监听器channel.addReturnListener(new ReturnListener() {@Overridepublic void handleReturn(int replyCode, String replyText, String exchange, String routingKey, byte[] body) throws IOException {System.out.println("消息被退回,replyCode: " + replyCode + ", replyText: " + replyText + ", exchange: " + exchange + ", routingKey: " + routingKey);System.out.println("退回的消息内容: " + new String(body));}});String exchangeName = "testExchange";String routingKey = "nonexistentQueue"; // 不存在的队列String message = "This message will be returned";// 设置mandatory为true,开启Return模式channel.basicPublish(exchangeName, routingKey, true, null, message.getBytes());// 关闭资源channel.close();connection.close();}
}

 

在上述代码中,channel.addReturnListener 添加了 ReturnListener 监听器,在 handleReturn 方法中处理被退回的消息,包括打印错误码、错误信息、交换机、路由键以及消息内容 。

  1. 消费者确认机制:消费者确认机制用于确保消费者正确处理消息。RabbitMQ 提供了三种消费者确认消息的方式。
  • 自动确认(Auto Ack):在这种方式下,当消费者接收到消息后,RabbitMQ 会立即将该消息从队列中删除,无论消费者是否成功处理了消息。在 Java 客户端中,通过 channel.basicConsume(queueName, true, consumer) 方法开启自动确认,其中第二个参数 true 表示开启自动确认 。
  • 手动确认(Manual Ack):消费者在处理完消息后,需要手动调用 channel.basicAck(deliveryTag, multiple) 方法来确认消息。deliveryTag 是消息的唯一标识,multiple 表示是否批量确认。如果 multiple 为 true,则表示确认所有小于等于 deliveryTag 的消息。例如,在处理完一条消息后,调用 channel.basicAck(deliveryTag, false) 来确认该条消息 。
  • 根据异常情况确认:消费者可以在处理消息的过程中,根据是否发生异常来决定如何确认消息。在 try - catch 块中处理消息,若处理成功,则调用 basicAck 确认消息;若发生异常,则调用 channel.basicNack(deliveryTag, multiple, requeue) 或 channel.basicReject(deliveryTag, requeue) 方法。basicNack 方法可以批量拒绝消息,requeue 参数表示是否将消息重新放回队列;basicReject 方法只能拒绝单条消息,同样通过 requeue 参数决定是否将消息重新放回队列 。示例代码如下:

 

import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.rabbitmq.client.DefaultConsumer;
import com.rabbitmq.client.Envelope;
import com.rabbitmq.client.AMQP;import java.io.IOException;
import java.util.concurrent.TimeoutException;public class Consumer {public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();String queueName = "testQueue";// 手动确认模式channel.basicConsume(queueName, false, "myConsumerTag", new DefaultConsumer(channel) {@Overridepublic void handleDelivery(String consumerTag, Envelope envelope, AMQP.BasicProperties properties, byte[] body) throws IOException {try {String message = new String(body, "UTF - 8");System.out.println("Received message: " + message);// 处理消息// 处理成功后手动确认channel.basicAck(envelope.getDeliveryTag(), false);} catch (Exception e) {// 处理失败,拒绝消息并重新放回队列channel.basicReject(envelope.getDeliveryTag(), true);}}});}
}

 

在上述代码中,通过 channel.basicConsume 方法设置为手动确认模式(第二个参数为 false),在 handleDelivery 方法中处理消息,根据处理结果调用 basicAck 或 basicReject 方法 。

(二)死信队列(Dead Letter Queue)

死信队列,顾名思义,是用于存放那些无法被正常消费的 “死信” 的队列。在实际应用中,了解死信的产生原因以及如何配置死信队列,对于保障系统的稳定性和可靠性至关重要 。

  1. 死信产生原因
  • 消息被拒绝:当消费者调用 basic.reject 或 basic.nack 方法拒绝消息,并且设置 requeue 参数为 false 时,该消息会成为死信。例如,在处理订单消息时,如果订单数据格式错误,消费者无法处理,就可以拒绝该消息并设置不重新入队 。
  • 消息过期:RabbitMQ 支持为消息或队列设置过期时间(TTL,Time To Live)。当消息在队列中停留的时间超过了设置的过期时间,该消息就会变成死信。比如,在电商系统中,设置订单支付消息的过期时间为 30 分钟,如果 30 分钟内未支付,该订单消息就会过期成为死信 。
  • 队列达到最大长度:如果队列设置了最大长度(x - max - length),当队列中的消息数量达到这个最大值后,再添加的消息会被丢弃或成为死信,具体取决于队列的配置 。
  1. 死信队列的作用与配置:死信队列的主要作用是对无法正常消费的消息进行统一管理和后续处理,避免消息丢失导致的数据不一致或业务异常。配置死信队列需要经过以下步骤:
  • 定义死信交换机和队列:首先需要定义一个死信交换机(一般为 Direct 或 Topic 类型)和对应的死信队列。例如,在使用 Spring Boot 和 Spring AMQP 进行配置时,可以通过配置类来定义:

 

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DeadLetterQueueConfig {@Beanpublic DirectExchange deadLetterExchange() {return new DirectExchange("dlx.exchange");}@Beanpublic Queue deadLetterQueue() {return new Queue("dlx.queue");}@Beanpublic Binding deadLetterBinding() {return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with("dlx.routing.key");}
}

 

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DeadLetterQueueConfig {@Beanpublic DirectExchange deadLetterExchange() {return new DirectExchange("dlx.exchange");}@Beanpublic Queue deadLetterQueue() {return new Queue("dlx.queue");}@Beanpublic Binding deadLetterBinding() {return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with("dlx.routing.key");}
}

在上述代码中,定义了名为 dlx.exchange 的死信交换机和名为 dlx.queue 的死信队列,并通过 Binding 将它们绑定在一起,路由键为 dlx.routing.key 。

  • 关联正常队列与死信队列:将正常队列与死信队列进行关联,当正常队列中的消息成为死信时,会被发送到死信队列。可以通过在正常队列的声明中设置 x - dead - letter - exchange 和 x - dead - letter - routing - key 参数来实现。例如:
@Bean
public Queue normalQueue() {return QueueBuilder.durable("normal.queue").withArgument("x - dead - letter - exchange", "dlx.exchange").withArgument("x - dead - letter - routing - key", "dlx.routing.key").build();
}

 在上述代码中,normal.queue 正常队列通过设置 x - dead - letter - exchange 和 x - dead - letter - routing - key 参数,将死信交换机和路由键与之前定义的死信队列相关联 。

 

(三)延迟队列(Delay Queue)

延迟队列在实际业务中有着广泛的应用,它能够让消息在指定的延迟时间后才被处理。RabbitMQ 本身并没有直接提供延迟队列的功能,但可以通过一些方式来实现 。

  1. 实现方式
  • 使用插件:可以通过安装 rabbitmq_delayed_message_exchange 插件来实现延迟队列。安装插件后,在 RabbitMQ 管理界面中可以创建延迟交换机(类型为 x - delayed - message)。在发送消息时,通过设置消息的 x - delay 属性来指定延迟时间。例如,在使用 Java 客户端发送消息时:
import com.rabbitmq.client.AMQP;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeoutException;public class Producer {public static void main(String[] args) throws IOException, TimeoutException {ConnectionFactory factory = new ConnectionFactory();factory.setHost("localhost");Connection connection = factory.newConnection();Channel channel = connection.createChannel();String exchangeName = "delayed.exchange";String routingKey = "delayed.routing.key";String message = "Delayed message";// 设置延迟时间为5000毫秒Map<String, Object> headers = new HashMap<>();headers.put("x - delay", 5000);AMQP.BasicProperties properties = new AMQP.BasicProperties.Builder().headers(headers).build();channel.basicPublish(exchangeName, routingKey, properties, message.getBytes());// 关闭资源channel.close();connection.close();}
}

在上述代码中,通过设置消息的 headers 中的 x - delay 属性为 5000,表示该消息将延迟 5000 毫秒后才被处理 。

  • 利用死信队列实现:利用死信队列和消息过期时间(TTL)来模拟延迟队列的功能。通过设置正常队列的消息过期时间,当消息过期后,会被发送到死信队列,从而实现延迟处理的效果。例如,在配置类中定义正常队列和死信队列:

 

import org.springframework.amqp.core.Binding;
import org.springframework.amqp.core.BindingBuilder;
import org.springframework.amqp.core.DirectExchange;
import org.springframework.amqp.core.Queue;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;@Configuration
public class DelayQueueConfig {@Beanpublic DirectExchange normalExchange() {return new DirectExchange("normal.exchange");}@Beanpublic Queue normalQueue() {return QueueBuilder.durable("normal.queue").withArgument("x - message - ttl", 5000) // 设置消息过期时间为5000毫秒.withArgument("x - dead - letter - exchange", "dlx.exchange").withArgument("x - dead - letter - routing - key", "dlx.routing.key").build();}@Beanpublic Binding normalBinding() {return BindingBuilder.bind(normalQueue()).to(normalExchange()).with("normal.routing.key");}@Beanpublic DirectExchange deadLetterExchange() {return new DirectExchange("dlx.exchange");}@Beanpublic Queue deadLetterQueue() {return new Queue("dlx.queue");}@Beanpublic Binding deadLetterBinding() {return BindingBuilder.bind(deadLetterQueue()).to(deadLetterExchange()).with("dlx.routing.key");}
}

 

在上述代码中,normal.queue 正常队列设置了消息过期时间为 5000 毫秒,当消息在该队列中过期后,会根据 x - dead - letter - exchange 和 x - dead - letter - routing - key 参数被发送到死信队列 dlx.queue,从而实现了延迟 5000 毫秒处理消息的效果 。

  1. 应用场景
  • 订单超时处理:在电商系统中,当用户下单后,如果在规定时间内未支付,系统需要自动取消订单。可以将订单消息发送到延迟队列,设置延迟时间为支付超时时间,当延迟时间到达后,消息从延迟队列中被消费,触发订单取消的业务逻辑 。
  • 任务定时执行:例如在一个任务调度系统中,需要在某个特定时间点执行某项任务。可以将任务消息发送到延迟队列,设置延迟时间为距离任务执行时间的间隔,当延迟时间结束,任务消息被消费,执行相应的任务 。

(四)优先级队列(Priority Queue)

优先级队列允许为消息设置不同的优先级,使得高优先级的消息能够优先被处理。在实际应用场景中,这种特性能够有效地优化资源分配和任务调度,提升系统的整体性能和响应速度 。

在 RabbitMQ 中设置消息优先级,首先需要在队列声明时启用优先级功能。以 Python 语言使用 pika 库为例,代码如下:

import pika# 建立到RabbitMQ服务器的连接
connection = pika.BlockingConnection(pika.ConnectionParameters('localhost'))
channel = connection.channel()# 声明一个支持优先级的队列,这里设置最大优先级为10
channel.queue_declare(queue='priority_queue', arguments={'x - max - priority': 10})

在上述代码中,通过在 queue_declare 方法的 arguments 参数中设置 x - max - priority 为 10,声明了一个名为 priority_queue 的队列,并支持 0 - 10 共 11 个优先级 。

发送消息时,可以指定消息的优先级。示例代码如下:

 

# 发送高优先级消息

channel.basic_publish(exchange='', routing_key='priority_queue', body='High priority message', properties=pika.BasicProperties(priority = 10))

# 发送低优先级消息

channel.basic_publish(exchange='', routing_key='priority_queue', body='Low priority message', properties=pika.BasicProperties(priority =

## 五、实战案例剖析

![need_search_image_by_title]()

### (一)电商场景下单流程优化

在电商系统中,下单流程涉及多个系统的协同工作,如订单系统、库存系统、支付系统、物流系统等。传统的下单流程中,这些系统之间往往是紧密耦合的同步调用,这会导致系统的响应速度慢、可扩展性差,且一旦某个系统出现故障,整个下单流程就会受到影响 。

引入RabbitMQ后,下单流程得到了极大的优化。当用户下单时,订单系统作为生产者,将订单消息发送到RabbitMQ的订单队列中。库存系统、支付系统、物流系统等作为消费者,从订单队列中获取订单消息进行异步处理 。

这样做带来了诸多好处。系统的响应速度大幅提升,因为订单系统在发送消息后无需等待其他系统的处理结果,就能立即返回给用户下单成功的响应。系统的可扩展性增强,当业务量增加时,可以通过增加消费者的实例数量来提高处理能力。系统的稳定性也得到了保障,即使某个系统出现故障,其他系统仍然可以继续处理已接收的消息,不会影响整个下单流程的正常运转 。

以某知名电商平台为例,在未使用RabbitMQ之前,下单高峰期系统响应时间长达数秒,且经常出现订单处理失败的情况。引入RabbitMQ后,系统响应时间缩短至毫秒级,订单处理成功率提升至99%以上,大大提升了用户体验和业务的稳定性 。

### (二)日志处理系统搭建

在大型应用系统中,日志的收集、存储和分析是非常重要的环节。传统的日志处理方式往往是同步写入文件或数据库,这种方式在高并发场景下会严重影响系统的性能 。

利用RabbitMQ构建高效的日志处理系统,可以很好地解决这些问题。应用程序作为生产者,将日志消息发送到RabbitMQ的日志队列中。日志处理系统作为消费者,从日志队列中获取日志消息,并进行异步存储和分析 。

在实际应用中,可以使用Elasticsearch来存储日志数据,利用其强大的搜索和分析功能,实现对日志的快速查询和统计。可以结合Kibana等可视化工具,对日志数据进行直观的展示和分析 。

例如,一个大型互联网公司的应用系统,每天会产生海量的日志数据。通过使用RabbitMQ搭建日志处理系统,将日志消息异步发送到Elasticsearch中存储,再通过Kibana进行可视化分析,运维人员可以实时监控系统的运行状态,快速定位和解决问题,大大提高了运维效率

## 六、常见问题与解决方案

![need_search_image_by_title]()

在使用RabbitMQ的过程中,我们可能会遇到各种各样的问题。下面为大家列举一些常见问题,并给出相应的解决方案 。

### (一)消息丢失

消息丢失是使用RabbitMQ时较为常见且严重的问题。它可能发生在消息生产、存储和消费的各个环节。在生产环节,当网络出现波动时,生产者可能会误以为消息已发送成功,但实际上RabbitMQ服务器并未收到消息。在存储环节,如果RabbitMQ服务器突然宕机或重启,而队列和消息未进行持久化设置,那么内存中的消息就会丢失。在消费环节,若消费者在处理消息过程中出现异常,而又采用了自动确认(Auto Ack)模式,RabbitMQ会认为消息已被成功消费,从而将消息从队列中删除,导致消息丢失 。

为解决这一问题,可从以下几个方面入手。在生产者端,开启确认模式(Confirm 模式),并在发送消息后通过监听确认信号来判断消息是否成功发送到交换机。若未收到确认信号,可进行消息重发。同时,捕获发送过程中的异常,以便及时处理。在存储环节,将交换机、队列和消息都设置为持久化。在Java中,声明交换机时设置 `durable = true`,声明队列时同样设置 `durable = true`,发送消息时通过 `AMQP.BasicProperties.Builder().deliveryMode(2).build()` 设置消息持久化 。在消费者端,采用手动确认(Manual Ack)模式,确保在消息处理完成后再向RabbitMQ发送确认信号 。

### (二)重复消费

消息重复消费也是一个需要关注的问题。其产生原因主要是网络波动或消费者服务异常。当消费者正常处理完消息,但还没来得及向RabbitMQ发送确认时,若出现网络抖动或者消费者服务挂掉的情况,等网络恢复或者消费者服务重启后,由于RabbitMQ之前未收到确认,消息仍然在队列中,并且因为有重试机制,消费者就会重新消费这条消息 。

解决消息重复消费的问题,可从业务层面保证幂等性。例如在电商系统中,对于订单支付成功后的状态修改操作,可将未支付状态作为修改语句的执行条件,这样即使重复执行该操作,也不会对结果产生影响。也可以通过设置消息唯一标识ID来解决。在消费者接收消息时,对这个ID进行校验,若该ID已被处理过,则不再重复处理 。

### (三)性能瓶颈

随着业务量的增长,RabbitMQ可能会出现性能瓶颈。例如,在高并发场景下,消息的处理速度跟不上消息的产生速度,导致队列中消息堆积严重,从而影响系统的整体性能。造成性能瓶颈的原因可能是多方面的,如消费者处理逻辑复杂、系统资源(如CPU、内存)不足、队列配置不合理等 。

为优化性能,可采取以下措施。优化消费者的处理逻辑,减少不必要的计算和I/O操作,提高处理速度。增加消费者的数量,以并行处理更多的消息。合理配置队列参数,如设置合适的预取计数(prefetch count),避免消费者一次性拉取过多消息导致内存溢出 。对系统资源进行监控和优化,确保服务器有足够的资源来处理消息 。

  

 

 

相关文章:

从入门到精通:RabbitMQ的深度探索与实战应用

目录 一、RabbitMQ 初相识 二、基础概念速览 &#xff08;一&#xff09;消息队列是什么 &#xff08;二&#xff09;RabbitMQ 核心组件 三、RabbitMQ 基本使用 &#xff08;一&#xff09;安装与环境搭建 &#xff08;二&#xff09;简单示例 &#xff08;三&#xff09;…...

深入理解若依RuoYi-Vue数据字典设计与实现

深入理解若依数据字典设计与实现 一、Vue2版本主要文件目录 组件目录src/components&#xff1a;数据字典组件、字典标签组件 工具目录src/utils&#xff1a;字典工具类 store目录src/store&#xff1a;字典数据 main.js&#xff1a;字典数据初始化 页面使用字典例子&#xf…...

Cursor 帮你写一个小程序

Cursor注册地址 首先下载客户端 点击链接下载 1 打开微信开发者工具创建一个小程序项目 选择TS-基础模版 官方 2 然后使用Cursor打开小程序创建的项目 3 在CHAT聊天框输入自己的需求 比如 小程序功能描述&#xff1a;吃什么助手 项目名称&#xff1a; 吃什么小程序 功能目标…...

进程控制的学习

目录 1.进程创建 1.1 fork函数 1.2 fork函数返回值 1.3 写时拷贝 1.4 fork 常规用法 1.5 fork 调用失败的原因 2. 进程终止 2.1 进程退出场景 2.2 进程常见退出方法 2.2.1 从main 返回 2.2.2 echo $&#xff1f; 查看进程退出码 2.2.2.1 我们如何得到退出码代表的含…...

一文讲解Java中的接口和抽象类

抽象类和接口有什么区别&#xff1f; 一个类只能继承一个抽象类&#xff1b;但一个类可以实现多个接口。所以我们在新建线程类的时候&#xff0c;一般推荐使用Runnable接口的方式&#xff0c;这样线程类还可以继承其他类&#xff0c;而不单单是Thread类&#xff1b;抽象类符合…...

Vue 3 30天精进之旅:Day 05 - 事件处理

引言 在前几天的学习中&#xff0c;我们探讨了Vue实例、计算属性和侦听器。这些概念为我们搭建了Vue应用的基础。今天&#xff0c;我们将专注于事件处理&#xff0c;这是交互式Web应用的核心部分。通过学习如何在Vue中处理事件&#xff0c;你将能够更好地与用户进行交互&#…...

STM32完全学习——RT-thread在STM32F407上移植

一、写在前面 关于源码的下载&#xff0c;以及在KEIL工程里面添加操作系统的源代码&#xff0c;这里就不再赘述了。需要注意的是RT-thread默认里面是会使用串口的&#xff0c;因此需要额外的进行串口的初始化&#xff0c;有些人可能会问&#xff0c;为什么不直接使用CubMAX直接…...

Shodan Dorks安装指南,通过Shodan搜索漏洞

Shodan Dorks是一种基于Shodan的工具&#xff0c;不知道Shodan是什么的不必阅读下面的内容。简单的说就是&#xff0c;利用预定义的查询&#xff08;dorks&#xff09;&#xff0c;通过Shodan轻松搜索漏洞和机密信息。 推荐渗透测试人员自行测试。 安装方法&#xff1a; 1.确…...

poi在word中打开本地文件

poi版本 5.2.0 方法1&#xff1a;使用XWPFFieldRun&#xff08;推荐&#xff09; 比如打开当前相对路径的aaaaa.docx XWPFFieldRun run paragraph.createFieldRun();CTRPr ctrPr run.getCTR().addNewRPr();CTFonts font ctrPr.addNewRFonts();// 设置字体font.setAscii(&quo…...

Linux查看服务器的内外网地址

目录&#xff1a; 1、内网地址2、外网地址3、ping时显示地址与真实不一致 1、内网地址 ifconfig2、外网地址 curl ifconfig.me3、ping时显示地址与真实不一致 原因是dns缓存导致的&#xff0c;ping这种方法也是不准确的&#xff0c;有弊端不建议使用&#xff0c;只适用于测试…...

OAuth1和OAuth2授权协议

OAuth 1 授权协议 1. 概述 OAuth1 是 OAuth 标准的第一个正式版本&#xff0c;它通过 签名和令牌 的方式&#xff0c;实现用户授权第三方访问其资源的功能。在 OAuth1 中&#xff0c;安全性依赖于签名机制&#xff0c;无需传递用户密码。 2. 核心特性 使用 签名&#xff08…...

DeepSeek学术题目选择效果怎么样?

论文选题 一篇出色的论文背后&#xff0c;必定有一个“智慧的选题”在撑腰。选题足够好文章就能顺利登上高水平期刊&#xff1b;选题不行再精彩的写作也只能“当花瓶”。然而许多宝子们常常忽视这个环节&#xff0c;把大量时间花在写作上&#xff0c;选题时却像抓阄一样随便挑一…...

数据结构(一)顺序表和链表

目录 1. 时间复杂度和空间复杂度 2. 顺序表 3. 链表 1. 时间复杂度和空间复杂度 如何估算一个算法的效率高低一般就是使用到时间复杂度和空间复杂度; 时间复杂度是评价一个算法运行快慢的, 而空间复杂度是算法额外需要空间大小. 1.1 时间复杂度的计算: 准确来说时间复杂度是…...

单相可控整流电路——单相桥式全控整流电路

以下是关于单相桥式整流电路的介绍&#xff1a; 电路构成&#xff08;带阻性负载的工作情况&#xff09; - 二极管&#xff1a;是电路0的核心元件&#xff0c;通常采用四个同型号或根据需求选择不同型号的二极管&#xff0c;如1N4001、1N4007等&#xff0c;如图Vt1和Vt4是一对…...

DeepSeek-R1:性能对标 OpenAI,开源助力 AI 生态发展

DeepSeek-R1&#xff1a;性能对标 OpenAI&#xff0c;开源助力 AI 生态发展 在人工智能领域&#xff0c;大模型的竞争一直备受关注。最近&#xff0c;DeepSeek 团队发布了 DeepSeek-R1 模型&#xff0c;并开源了模型权重&#xff0c;这一举动无疑为 AI 领域带来了新的活力。今…...

【Maui】提示消息的扩展

文章目录 前言一、问题描述二、解决方案三、软件开发&#xff08;源码&#xff09;3.1 消息扩展库3.2 消息提示框使用3.3 错误消息提示使用3.4 问题选择框使用 四、项目展示 前言 .NET 多平台应用 UI (.NET MAUI) 是一个跨平台框架&#xff0c;用于使用 C# 和 XAML 创建本机移…...

001 mybatis入门

文章目录 mybatis是什么ORM是什么ORM框架和MyBatis的区别#{}和${}的区别编码流程UserDaoImpl.javaUserDao.javaUser.javadb.propertiesSqlMapConfig.xmlUserMapper.xmlMybatisTest.javapom.xmluser.sql 表现层 SpringMVC 业务层 Spring 持久层 Mybatis https://mybatis.org/myb…...

tomcat的accept-count、max-connections、max-threads三个参数的含义

tomcat的accept-count、max-connections、max-threads三个参数的含义 tomcat的accept-count、max-connections、max-threads三个参数的含义 max-connections&#xff1a;最大连接数 最大连接数是指&#xff0c;同一时刻&#xff0c;能够连接的最大请求数 需要注意的是&#x…...

8.2 从看图识字到智能解读:GPT-4 with Vision 开启多模态 AI 新纪元

从看图识字到智能解读:GPT-4 with Vision 开启多模态 AI 新纪元 引言:AI 的多模态跃迁 随着人工智能技术的快速发展,我们正迈入一个新的智能交互时代。传统的 AI 模型主要聚焦于文本处理,而多模态 AI 模型如 GPT-4 with Vision(GPT-4V) 则能够同时处理图像和文本。GPT-4…...

.strip()用法

.strip("") 是 Python 字符串方法 strip() 的一个用法&#xff0c;它会去除字符串两端指定字符集中的字符。 基本语法&#xff1a; string.strip([chars])string: 这是你要操作的字符串。chars: 可选参数&#xff0c;表示你想要去除的字符集&#xff08;默认为空格…...

蓝桥杯例题三

无论前方困难如何重重&#xff0c;我们都要坚定信念&#xff0c;勇往直前。面对挑战和困境&#xff0c;不要退缩&#xff0c;不要放弃&#xff0c;要坚持走下去。当我们感到疲惫时&#xff0c;要告诉自己&#xff1a;“我可以&#xff0c;我一定行&#xff01;”相信自己的实力…...

关于pygame窗口输入法状态异常切换现象的分析报告

一、问题描述 1.1 需求说明 我们准备使用Pygame开发一个键盘输入测试程序&#xff0c;需要确保输入时窗口始终处于英文输入模式&#xff0c;也就是禁止中文输入&#xff1b; 1.2 现象描述 控制台种显示&#xff0c;程序在初始化时&#xff0c;会有两次IMM状态切换操作&…...

【JavaEE进阶】应用分层

目录 &#x1f38b;序言 &#x1f343;什么是应用分层 &#x1f38d;为什么需要应用分层 &#x1f340;如何分层(三层架构) &#x1f384;MVC和三层架构的区别和联系 &#x1f333;什么是高内聚低耦合 &#x1f38b;序言 通过上⾯的练习,我们学习了SpringMVC简单功能的开…...

两数相加:链表操作的基础与扩展

两数相加&#xff1a;链表操作的基础与扩展 引言 链表&#xff08;Linked List&#xff09;是一种灵活且高效的数据结构&#xff0c;特别适用于动态增删操作。无论是初学者还是资深程序员&#xff0c;链表的基本操作都是算法学习中的重要一环。而 “两数相加” 问题则是链表操…...

ChatGPT从数据分析到内容写作建议相关的46个提示词分享!

在当今快节奏的学术环境中&#xff0c;研究人员面临着海量的信息和复杂的研究任务。幸运的是&#xff0c;随着人工智能技术的发展&#xff0c;像ChatGPT这样的先进工具为科研人员提供了强大的支持。今天就让我们一起探索如何利用ChatGPT提升研究效率进一步优化研究流程。 ChatG…...

解析“in the wild”——编程和生活中的俚语妙用

解析“in the wild”——编程和生活中的俚语妙用 看下面的技术文章中遇到 in the wild这个词&#xff0c;想要研究一下&#xff0c;遂产生此文。 Are there ever pointers to pointers to pointers? There is an old programming joke which says you can rate C programmers…...

rocketmq原理源码分析之控制器模式- dledger

简介 RocketMQ 4.5 版本之前&#xff0c;RocketMQ 的broker是 Master/Slave部署架构&#xff0c;一组 broker 有一个 Master &#xff0c;有0到若干Slave&#xff0c;Slave复制Master消息存储&#xff0c;随时替代下线的Master。Master/Slave部署架构提供一定的高可用性&#x…...

Hello Moto

“Hello Moto” 是摩托罗拉&#xff08;Motorola&#xff09;的一句经典广告口号&#xff0c;用于推广其品牌和产品&#xff0c;特别是在手机领域。以下是它的含义和背景&#xff1a; 1. 品牌宣传的标志性语句 直白含义&#xff1a;简单地向摩托罗拉打招呼&#xff08;“Hell…...

存储基础 -- SCSI命令格式与使用场景

SCSI命令格式与使用场景 1. SCSI命令描述符块&#xff08;CDB&#xff09; 1.1 CDB基本概念 SCSI命令通过**命令描述符块&#xff08;CDB, Command Descriptor Block&#xff09;**表示。 CDB长度&#xff1a;SCSI命令根据使用场景有不同长度的CDB&#xff0c;常见的有6字节…...

ceph基本概念,架构,部署(一)

一、分布式存储概述 1.存储分类 存储分为封闭系统的存储和开放系统的存储&#xff0c;而对于开放系统的存储又被分为内置存储和外挂存储。 外挂存储又被细分为直连式存储(DAS)和网络存储(FAS)&#xff0c;而网络存储又被细分网络接入存储(NAS)和存储区域网络(SAN)等。 DAS(D…...

CNN-GRU卷积门控循环单元时间序列预测(Matlab完整源码和数据)

CNN-GRU卷积门控循环单元时间序列预测&#xff08;Matlab完整源码和数据&#xff09; 目录 CNN-GRU卷积门控循环单元时间序列预测&#xff08;Matlab完整源码和数据&#xff09;预测效果基本介绍CNN-GRU卷积门控循环单元时间序列预测一、引言1.1、研究背景与意义1.2、研究现状1…...

Ubuntu 顶部状态栏 配置,gnu扩展程序

顶部状态栏 默认没有配置、隐藏的地方 安装使用Hide Top Bar 或Just Perfection等进行配置 1 安装 sudo apt install gnome-shell-extension-manager2 打开 安装的“扩展管理器” 3. 对顶部状态栏进行配置 使用Hide Top Bar 智能隐藏&#xff0c;或者使用Just Perfection 直…...

React应用深度优化与调试实战指南

一、渲染性能优化进阶 1.1 精细化渲染控制 typescript 复制 // components/HeavyComponent.tsx import React, { memo, useMemo } from react;interface Item {id: string;complexData: {// 复杂嵌套结构}; }const HeavyComponent memo(({ items }: { items: Item[] }) &g…...

Spring中的事件和事件监听器是如何工作的?

目录 一、事件&#xff08;Event&#xff09; 二、事件发布器&#xff08;Event Publisher&#xff09; 三、事件监听器&#xff08;Event Listener&#xff09; 四、使用场景 五、总结 以下是关于Spring中的事件和事件监听器的介绍与使用说明&#xff0c;结合了使用场景&…...

Vue.js组件开发-实现多个文件附件压缩下载

在 Vue 项目中实现多个附件压缩下载&#xff0c;可以借助 jszip 库来创建压缩文件&#xff0c;以及 file-saver 库来保存生成的压缩文件。 步骤 1&#xff1a;安装依赖 首先&#xff0c;在 Vue 项目中安装 jszip 和 file-saver&#xff1a; npm install jszip file-saver步骤…...

基于dlib/face recognition人脸识别推拉流实现

目录 一.环境搭建 二.推拉流代码 三.人脸检测推拉流 一.环境搭建 1.下载RTSP服务器MediaMTX与FFmpeg FFmpeg是一款功能强大的开源多媒体处理工具,而MediaMTX则是一个轻量级的流媒体服务器。两者结合,可以实现将本地视频或者实时摄像头画面推送到RTSP流,从而实现视频…...

qt QNetworkRequest详解

1、概述 QNetworkRequest是Qt网络模块中的一个核心类&#xff0c;专门用于处理网络请求。它封装了网络请求的所有关键信息&#xff0c;包括请求的URL、HTTP头部信息等&#xff0c;使得开发者能够方便地在Qt应用程序中执行网络操作&#xff0c;如文件下载、网页内容获取等。QNe…...

uvm timeout的哪些事

如下图所示&#xff0c;设置timeout并未生效&#xff0c;原因多了一个空格&#xff0c;坑 进一步分析&#xff0c;默认是overidable的 是否加括号呢&#xff0c;如下所示&#xff0c;这两个造型都可以&#xff0c;中间有空格也行 那么&#xff0c;我加上单位可以吗&#xff0c;…...

JavaScript赋能智能网页设计

构建AI驱动的实时风格迁移系统 案例概述 本案例将实现一个基于深度学习的实时图像风格迁移系统&#xff0c;通过浏览器端神经网络推理实现以下高级特性&#xff1a; WebAssembly加速的ONNX模型推理 WebGL Shader实现的风格混合算法 WebRTC实时视频流处理 基于Web Workers的…...

全面了解 Web3 AIGC 和 AI Agent 的创新先锋 MelodAI

不管是在传统领域还是 Crypto&#xff0c;AI 都是公认的最有前景的赛道。随着数字内容需求的爆炸式增长和技术的快速迭代&#xff0c;Web3 AIGC&#xff08;AI生成内容&#xff09;和 AI Agent&#xff08;人工智能代理&#xff09;正成为两大关键赛道。 AIGC 通过 AI 技术生成…...

leetcode_链表 234.回文链表

234.回文链表 给你一个单链表的头节点head&#xff0c;请你判断该链表是否为回文链表。如果是, 返回 true ; 否则, 返回false。思路&#xff1a; 找到中间节点(快慢指针法)反转后半部分的链表比较前半部分和后半部分链表 # Definition for singly-linked list. # class List…...

cloc下载和使用

cloc&#xff08;Count Lines of Code&#xff09;是一个跨平台的命令行工具&#xff0c;用于计算代码行数。以下是下载和使用 cloc 的步骤&#xff1a; 下载 cloc 对于 Windows 用户&#xff1a; 访问 cloc 的 GitHub 仓库&#xff1a;https://github.com/AlDanial/cloc在 …...

在 Windows 系统上,将 Ubuntu 从 C 盘 迁移到 D 盘

在 Windows 系统上&#xff0c;如果你使用的是 WSL&#xff08;Windows Subsystem for Linux&#xff09;并安装了 Ubuntu&#xff0c;你可以将 Ubuntu 从 C 盘 迁移到 D 盘。迁移过程涉及导出当前的 Ubuntu 发行版&#xff0c;然后将其导入到 D 盘的目标目录。以下是详细的步骤…...

【Redis】Redis入门以及什么是分布式系统{Redis引入+分布式系统介绍}

文章目录 介绍redis的引入 分布式系统单机架构应用服务和数据库服务分离【负载均衡】引入更多的应用服务器节点 单机架构 分布式是什么 数据库分离和负载均衡 理解负载均衡 数据库读写分离 引入缓存 数据库分库分表 引入微服务 介绍 The open source, in-memory data store us…...

wow-agent---task4 MetaGPT初体验

先说坑&#xff1a; 1.使用git clone模式安装metagpt 2.模型尽量使用在线模型或本地高参数模型。 这里使用python3.10.11调试成功 一&#xff0c;安装 安装 | MetaGPT&#xff0c;参考这里的以开发模型进行安装 git clone https://github.com/geekan/MetaGPT.git cd /you…...

Leetcode::3432. 统计元素和差值为偶数的分区方案

3432. 统计元素和差值为偶数的分区方案 已解答 简单 相关企业 提示 给你一个长度为 n 的整数数组 nums 。 分区 是指将数组按照下标 i &#xff08;0 < i < n - 1&#xff09;划分成两个 非空 子数组&#xff0c;其中&#xff1a; 左子数组包含区间 [0, i] 内的所…...

linux如何修改密码,要在CentOS 7系统中修改密码

要在CentOS 7系统中修改密码&#xff0c;你可以按照以下步骤操作&#xff1a; 步骤 1: 登录到系统 在登录提示符 localhost login: 后输入你的用户名。输入密码并按回车键。 步骤 2: 修改密码 登录后&#xff0c;使用 passwd 命令来修改密码&#xff1a; passwd 系统会提…...

GIS与相关专业软件汇总

闲来无事突然想整理一下看看 GIS及相关领域 究竟有多少软件或者工具包等。 我询问了几个AI工具并汇总了一个软件汇总&#xff0c;不搜不知道&#xff0c;一搜吓一跳&#xff0c;搜索出来了大量的软件&#xff0c;大部分软件或者工具包都没有见过&#xff0c;不知大家还有没有要…...

云计算架构学习之LNMP架构部署、架构拆分、负载均衡-会话保持

一.LNMP架构部署 1.1. LNMP服务搭建 1.磁盘信息 2.内存 3.负载信息 4.Nginx你们公司都用来干嘛 5.文件句柄(文件描述符 打开文件最大数量) 6.你处理过系统中的漏洞吗 SSH漏洞 7.你写过什么shell脚本 8.监控通过什么告警 zabbix 具体监控哪些内容 9.mysql redis查询 你好H…...

mamba论文学习

rnn 1986 训练速度慢 testing很快 但是很快就忘了 lstm 1997 训练速度慢 testing很快 但是也会忘&#xff08;序列很长的时候&#xff09; GRU实在lstm的基础上改进&#xff0c;改变了一些门 transformer2017 训练很快&#xff0c;testing慢些&#xff0c;时间复杂度高&am…...