RabbitMQ 深度解析:从核心组件到复杂应用场景
一.RabbitMQ简单介绍
消息队列作为分布式系统中不可或缺的组件,承担着解耦系统组件、保障数据可靠传输、提高系统吞吐量等重要职责。在众多消息队列产品中,RabbitMQ 凭借其可靠性和丰富的特性,在企业级应用中获得了广泛应用。本研究报告将全面剖析 RabbitMQ 的核心组件、工作原理,深入探讨 Java 开发中常见的消息处理问题,并详细阐述如何实现复杂的消息处理场景,同时揭示 RabbitMQ 底层的负载均衡机制。通过本报告,读者将不仅能够理解 RabbitMQ 的基本使用,更能掌握如何在实际开发中解决各种挑战,充分发挥消息队列的价值。
二.RabbitMQ 核心组件与工作原理
RabbitMQ 作为一个基于 AMQP(Advanced Message Queuing Protocol,高级消息队列协议)标准的开源消息系统,其设计理念源于"生产者-消费者"模式,旨在实现系统组件间的异步通信和解耦。在深入探讨 RabbitMQ 的应用之前,我们首先需要理解其核心组件及其工作原理。
Broker:消息中枢
Broker 是 RabbitMQ 的核心组件,扮演着消息中转站的角色。它接收来自生产者的消息,并根据预设的路由规则将消息转发给相应的消费者。在 RabbitMQ 的架构中,Broker 是消息流通的枢纽,负责消息的存储、转发和管理。当消息被生产者发送到 Broker 时,Broker 会根据消息的路由键(Routing Key)和交换机(Exchange)类型,将消息路由到相应的消息队列(Queue)。Broker 的存在使得生产者和消费者无需直接通信,从而实现了系统的解耦。此外,Broker 还负责管理消息的持久化、过期时间以及消息的确认机制等,确保消息的可靠传输。在高可用性场景下,RabbitMQ 支持集群部署,多个 Broker 可以协同工作,提供故障转移和负载均衡能力,进一步提高系统的稳定性和性能。
Producer:消息生产者
Producer 是消息的源头,负责生成并发送消息到 Broker。在 RabbitMQ 中,生产者通过定义消息的路由键(route key)和交换机(exchanger)类型,指定消息的流向。生产者可以是任何能够连接到 RabbitMQ 服务的应用程序或服务。当生产者发送消息时,它需要指定消息的内容、路由键以及交换机类型。交换机类型决定了消息如何被路由到队列。RabbitMQ 支持多种交换机类型,如直连交换机(Direct Exchange)、主题交换机(Topic Exchange)、扇出交换机(Fanout Exchange)和头交换机(Headers Exchange)等。每种交换机类型都有其特定的路由规则,允许生产者灵活控制消息的分发方式。例如,直连交换机根据路由键的精确匹配将消息路由到特定的队列,而主题交换机则支持基于通配符(使用 *(匹配一个词)和 #(匹配多个词))的路由键匹配。生产者还可以设置消息的属性,如消息过期时间、优先级等,进一步控制消息的行为。此外,生产者可以通过设置消息的持久化级别,确保消息在 Broker 重启后不会丢失。
Consumer:消息消费者
Consumer 是消息的接收和处理端,从 Broker 获取消息并进行相应的业务逻辑处理。在 RabbitMQ 中,消费者通过订阅特定的消息队列来接收消息。消费者可以是任何能够连接到 RabbitMQ 服务的应用程序或服务。当消费者订阅了一个队列后,它会接收到该队列中的消息,并根据业务逻辑进行处理。RabbitMQ 支持多种消息确认机制,允许消费者在处理消息后向 Broker 发送确认信号,表示消息已成功处理。如果消费者未确认消息,Broker 会认为消息未被成功处理,并会在一定时间后将消息重新发送给其他消费者。这种机制确保了消息的可靠性,即使在消费者出现故障的情况下,消息也不会丢失。此外,消费者还可以设置消息的处理超时时间,防止长时间未处理的消息占用队列资源。
WorkQueue:消息队列
WorkQueue 是消息的存储和中转站,负责暂存消息直到被消费者处理。在 RabbitMQ 中,消息队列是消息存储和管理的基本单位。当生产者发送消息到 Broker 时,Broker 会根据路由规则将消息存储在相应的消息队列中。消费者通过订阅消息队列来接收和处理消息。消息队列可以配置为持久化或非持久化。持久化队列的消息会保存到磁盘上,即使 Broker 重启,消息也不会丢失。而非持久化队列的消息只保存在内存中,Broker 重启后消息会丢失。根据应用场景的需求,我们可以灵活选择消息队列的持久化策略。消息队列还可以设置最大消息数量或最大存储空间,防止队列无限增长导致系统资源耗尽。此外,消息队列还支持消息的 TTL(Time To Live,生存时间)设置,控制消息的有效期,过期的消息会自动从队列中删除。
RabbitMQ 工作原理
RabbitMQ 的工作原理基于生产者-消费者模式,通过消息的异步传输实现系统组件的解耦。当生产者发送消息时,它将消息发送到 Broker,指定消息的路由键和交换机类型。Broker 根据交换机类型和路由键,将消息路由到相应的消息队列中。消费者通过订阅消息队列,从队列中获取消息并进行处理。这种设计使得生产者和消费者无需直接通信,也无需同时在线,极大地提高了系统的灵活性和可靠性。
RabbitMQ 的消息传输过程可以分为以下几个步骤:
- 生产者发送消息:生产者通过 RabbitMQ 客户端将消息发送到 Broker。消息包含内容和路由信息(如路由键)。
- Broker 接收消息:Broker 接收生产者发送的消息,并根据消息的路由信息和交换机类型,将消息路由到相应的消息队列。
- 消息存储:消息被存储在消息队列中,等待消费者处理。根据队列的配置,消息可以是持久化的(保存到磁盘)或非持久化的(只保存在内存中)。
- 消费者接收消息:消费者从消息队列中获取消息。RabbitMQ 支持多种消息接收方式,如轮询(Polling)和推送(Push)。
- 消息处理:消费者处理获取到的消息,并根据业务逻辑执行相应的操作。
- 消息确认:消费者处理消息后,向 Broker 发送确认信号,表示消息已成功处理。如果消费者未确认消息,Broker 会认为消息未被成功处理,并会在一定时间后将消息重新发送给其他消费者。
这种消息确认机制确保了消息的可靠性,即使在消费者出现故障的情况下,消息也不会丢失。此外,RabbitMQ 还支持多种消息投递策略,如消息的公平分配(Fair Dispatch)、消息的优先级等,进一步提高了系统的灵活性和性能。
三.Java 开发中 RabbitMQ 常见问题及解决方案
在 Java 应用开发中使用 RabbitMQ 时,开发者常常会遇到各种挑战,如消息堆积、消息幂等性判断和消息丢失等问题。这些问题如果处理不当,将直接影响系统的性能和可靠性。本节将深入探讨这些问题的原因、影响以及相应的解决方案。
消息堆积问题
消息堆积是指消息队列中的消息数量迅速增加,导致系统性能下降甚至崩溃。在高并发场景下,消息堆积是一个常见且严重的问题。
消息堆积的原因
消息堆积通常由以下几个因素引起:
- 生产者发送消息的速度快于消费者处理消息的速度:这是消息堆积最直接的原因。当生产者发送消息的速度远快于消费者处理消息的速度时,消息队列中的消息数量会迅速增加,最终导致队列堵塞。
- 消费者处理消息的逻辑复杂,耗时长:如果消费者处理每条消息需要较长时间,而生产者又不断发送新消息,消息队列中的消息数量会不断增加。
- 系统资源不足:如果服务器的 CPU、内存、网络带宽等资源不足,消费者处理消息的速度会受到影响,导致消息堆积。
- 消息队列配置不当:如果消息队列没有设置合理的参数,如最大消息数量、消息过期时间等,可能会导致消息无限堆积。
消息堆积的影响
消息堆积会对系统产生多方面的负面影响: - 系统性能下降:消息队列中的消息数量过多会占用大量内存和磁盘空间,导致系统响应变慢,甚至崩溃。
- 业务处理延迟增加:消费者处理消息的速度变慢,业务处理延迟增加,影响用户体验。
- 资源耗尽:消息队列占用过多资源,可能导致系统资源耗尽,无法正常运行。
消息堆积的解决方案
针对消息堆积问题,我们可以采取以下几种解决方案: - 增加消费者数量:通过增加消费者实例来提高消息处理速度。RabbitMQ 支持负载均衡,多个消费者可以同时处理消息队列中的消息。
在 Java 代码中,我们可以通过创建多个消费者线程或进程来处理消息。例如,可以在配置文件中设置消费者线程池的大小:@Configuration public class RabbitConfig {@Beanpublic ConnectionFactory connectionFactory() {CachingConnectionFactory connectionFactory = new CachingConnectionFactory("localhost");connectionFactory.setPort(5672);return connectionFactory;}@Beanpublic RabbitTemplate rabbitTemplate(ConnectionFactory connectionFactory) {return new RabbitTemplate(connectionFactory);}@Beanpublic Queue queue() {return new Queue("my_queue");}@Beanpublic SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MyConsumer myConsumer) {SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();container.setConnectionFactory(connectionFactory);container.setQueueNames("my_queue");container.setMessageListener(myConsumer);container.setConcurrentConsumers(5); // 设置并发消费者数量return container;} }
- 优化消费者代码:分析消费者处理消息的逻辑,优化代码,减少消息处理时间。例如,避免在消息处理过程中进行耗时的数据库操作或网络调用,可以考虑将这些操作异步化。
在 Java 代码中,可以使用异步数据库访问或消息队列来处理耗时操作:@Component public class MyConsumer {@Autowiredprivate EntityManager entityManager;@RabbitListener(queues = "my_queue")public void receiveMessage(String message) {// 将消息处理逻辑异步化 才想起来之前的并发工具类还没写CompletableFuture.runAsync(() -> {try {// 处理消息processMessage(message);// 持久化结果entityManager.persist(new MessageProcessingResult(message));} catch (Exception e) {// 处理异常handleException(e, message);}});}private void processMessage(String message) {// 具体的消息处理逻辑}private void handleException(Exception e, String message) {// 异常处理逻辑} }
- 设置消息 TTL:为消息设置过期时间(TTL),确保消息不会在队列中无限期地堆积。当消息超过 TTL 时,会被自动删除。(redis策略)
在 RabbitMQ 中,可以通过队列参数设置消息的 TTL:@Configuration public class RabbitConfig {@Beanpublic Queue queue() {Map<String, Object> args = new HashMap<>();args.put("x-message-ttl", 3600000); // 设置消息 TTL 为 1 小时return new Queue("my_queue", true, false, false, args);} }
- 配置队列长度限制:设置队列的最大消息数量或最大存储空间,防止队列无限增长。当队列达到限制时,新的消息会被拒绝或丢弃。
在 RabbitMQ 中,可以通过队列参数设置最大消息数量:@Configuration public class RabbitConfig {@Beanpublic Queue queue() {Map<String, Object> args = new HashMap<>();args.put("max-length", 10000); // 设置队列最大消息数量为 10000return new Queue("my_queue", true, false, false, args);} }
- 监控和告警:实施消息队列的监控和告警机制,及时发现消息堆积问题。当队列中的消息数量超过预设阈值时,系统会发出告警,提醒管理员采取措施。
可以使用 RabbitMQ 的管理界面或第三方监控工具来监控队列的状态:@Configuration public class RabbitConfig {@Beanpublic ManagementConfigurer managementConfigurer() {return new ManagementConfigurer() {@Overridepublic void configure(ManagementEndpoint endpoint) {endpoint.setPort(15672);endpoint.setUsername("guest");endpoint.setPassword("guest");}};} }
消息幂等性问题
消息幂等性是指同一条消息被多次处理时,结果与一次处理相同。在 RabbitMQ 中,由于消息的可靠传输机制,消息可能会被重复发送,导致重复处理。如果系统的消息处理逻辑不是幂等的,可能会导致数据不一致或重复提交等严重问题。
消息幂等性判断的必要性
- 消息重复投递:在消息传输过程中,由于网络问题或系统故障,消息可能会被重复投递。如果消费者没有幂等性判断机制,可能会多次处理同一条消息,导致业务逻辑执行多次。
- 系统重启恢复:当消费者或整个系统重启后,未确认的消息可能会被重新投递。如果没有幂等性判断,重启后的系统可能会重复处理之前已经处理过的消息。
- 分布式系统中的竞态条件:在分布式系统中,多个消费者可能同时处理消息,如果没有幂等性判断,可能会导致竞态条件,影响系统的一致性。
消息幂等性判断的方法
为了确保消息处理的幂等性,可以采取以下几种方法:
1 唯一标识符(tag)检查:为每条消息生成一个唯一标识符(如 UUID),消费者在处理消息前检查该标识符是否已经处理过。如果已经处理过,则跳过该消息。
在 Java 代码中,可以使用 Redis 来记录已处理的消息 ID:
@Component
public class MyConsumer {@Autowiredprivate RedisTemplate<String, String> redisTemplate;@RabbitListener(queues = "my_queue")public void receiveMessage(String message) {// 生成消息 IDString messageId = UUID.randomUUID().toString();// 检查消息是否已处理if (!redisTemplate.hasKey("processed_messages:" + messageId)) {// 处理消息processMessage(message);// 标记消息为已处理redisTemplate.opsForValue().set("processed_messages:" + messageId, "processed");}}private void processMessage(String message) {// 具体的消息处理逻辑}
}
- 数据库唯一约束(unique index):在数据库表中为消息的唯一标识符设置唯一约束。当尝试插入重复的消息时,会抛出唯一性冲突异常,消费者可以捕获该异常并跳过消息处理。
在 Java 代码中,可以使用 JPA 的 @Table(uniqueConstraints=…) 注解来设置唯一约束:@Entity @Table(uniqueConstraints = @UniqueConstraint(columnNames = "message_id")) public class MessageEntity {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Long id;@Column(nullable = false)private String messageId;@Column(nullable = false)private String content;// 省略 getter 和 setter 方法 }
- 版本号控制:为消息添加版本号,消费者在处理消息时检查消息的版本号是否是最新的。如果不是最新的版本,跳过处理。
在 Java 代码中,可以使用乐观锁来实现版本号控制:@Component public class MyConsumer {@Autowiredprivate EntityManager entityManager;@RabbitListener(queues = "my_queue")public void receiveMessage(String message) {// 解析消息MessageDto messageDto = JsonUtil.fromJson(message, MessageDto.class);// 查询最新的消息版本Query query = entityManager.createQuery("SELECT m FROM MessageEntity m WHERE m.messageId = :messageId");query.setParameter("messageId", messageDto.getMessageId());MessageEntity latestMessage = (MessageEntity) query.getSingleResult();// 比较版本号if (latestMessage.getVersion() < messageDto.getVersion()) {// 处理消息processMessage(messageDto);// 更新消息状态entityManager.merge(latestMessage);}}private void processMessage(MessageDto messageDto) {// 具体的消息处理逻辑} }
- 时间戳判断:为消息添加时间戳,消费者在处理消息时检查消息的创建时间。如果消息的创建时间与当前时间相差较大,可能是重复消息,跳过处理。
在 Java 代码中,可以使用消息的时间戳来判断:@Component public class MyConsumer {@RabbitListener(queues = "my_queue")public void receiveMessage(String message) {// 解析消息MessageDto messageDto = JsonUtil.fromJson(message, MessageDto.class);// 检查消息是否过时if (System.currentTimeMillis() - messageDto.getCreateTime() > 3600000) { // 超过 1 小时// 跳过处理return;}// 处理消息processMessage(messageDto);}private void processMessage(MessageDto messageDto) {// 具体的消息处理逻辑} }
消息丢失问题
消息丢失是指消息从队列中消失,但没有被任何消费者处理。消息丢失会直接导致系统功能异常或数据不一致,是一个严重的可靠性问题。
消息丢失的原因
- 生产者未确认消息发送:在 RabbitMQ 中,生产者将消息发送到 Broker 时,如果不设置消息确认机制,生产者无法知道消息是否成功发送到 Broker。如果网络问题或 Broker 故障导致消息未成功发送,生产者可能已经认为消息已发送,但实际上消息丢失了。
- Broker 故障:如果 Broker 发生故障(如断电、硬件故障等),且消息未持久化,消息会丢失。即使消息已持久化,如果 Broker 的数据存储介质发生故障,消息也可能丢失。
- 消费者未确认消息接收:在 RabbitMQ 中,消费者接收消息后,如果不发送确认信号,Broker 会认为消息未被成功处理,并会在一定时间后将消息重新发送给其他消费者。如果消费者接收到消息但未处理就崩溃了,且未发送确认信号,Broker 会将消息重新发送给其他消费者,导致消息重复处理。
- 消息队列配置不当:如果消息队列配置为非持久化,且 Broker 重启,消息会丢失。此外,如果消息队列设置了消息过期时间,过期的消息会自动删除。
消息丢失的解决方案
1 生产者确认消息发送:在生产者发送消息时,设置消息确认机制,确保消息已成功发送到 Broker。在 RabbitMQ 中,可以通过设置消息的 deliveryMode 为 2(持久化)来确保消息持久化到磁盘。
在 Java 代码中,可以设置消息的持久化:@Component public class MyProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendMessage(String message) {// 设置消息的 deliveryMode 为 2(持久化)MessageProperties messageProperties = new MessageProperties();messageProperties.setDeliveryMode(2); // 1 表示非持久化,2 表示持久化Message rabbitMessage = new Message(message.getBytes(), messageProperties);rabbitTemplate.send("my_exchange", "my_routing_key", rabbitMessage);} }
2.Broker 高可用性配置:通过配置 RabbitMQ 集群或主从复制,提高 Broker 的可靠性。如果单个 Broker 故障,其他 Broker 可以接管,确保消息不丢失。
在 RabbitMQ 中,可以通过配置镜像队列(Mirrored Queues)或使用 RabbitMQ HA(High Availability)插件来提高消息的可靠性:
@Configuration
public class RabbitConfig {@Beanpublic Queue queue() {Map<String, Object> args = new HashMap<>();args.put("x-ha-policy", "all"); // 配置镜像队列策略为 allreturn new Queue("my_queue", true, false, false, args);}
}
3.消费者确认消息接收:在消费者接收消息后,及时发送确认信号,确保 Broker 知道消息已被成功处理。在 RabbitMQ 中,消费者可以通过调用 acknowledge() 方法来确认消息。
在 Java 代码中,可以设置消费者自动确认或手动确认:
@Component
public class MyConsumer {@RabbitListener(queues = "my_queue", autoStartup = "false")public void receiveMessage(String message) {try {// 处理消息processMessage(message);// 确认消息getChannel().basicAck(getDeliveryTag(), false);} catch (Exception e) {// 处理异常handleException(e, message);// 拒绝消息,不重新投递getChannel().basicReject(getDeliveryTag(), false);}}private void processMessage(String message) {// 具体的消息处理逻辑}private void handleException(Exception e, String message) {// 异常处理逻辑}
}
- 设置消息过期时间:为消息设置合理的过期时间,防止消息在队列中无限期地堆积。过期的消息会自动删除,释放队列空间。
在 Java 代码中,可以设置消息的过期时间:@Component public class MyProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendMessage(String message) {// 设置消息的过期时间为 1 小时MessageProperties messageProperties = new MessageProperties();messageProperties.setExpiration("3600000"); // 3600000 毫秒 = 1 小时Message rabbitMessage = new Message(message.getBytes(), messageProperties);rabbitTemplate.send("my_exchange", "my_routing_key", rabbitMessage);} }
三.RabbitMQ 特殊消息类型实现
在实际应用中,我们常常需要实现一些特殊类型的消息,如延迟消息、顺序消息和全局顺序消息。这些特殊消息类型可以满足不同的业务需求,提高系统的灵活性和可靠性。
延迟消息实现
延迟消息是指消息在指定时间后才被消费者处理。在某些业务场景中,我们可能需要延迟处理某些消息,如订单超时自动取消、优惠券过期提醒等。
延迟消息的实现方法
在 RabbitMQ 中,实现延迟消息主要有以下几种方法:
- 使用 TTL(Time To Live):为消息设置过期时间,消息在过期前无法被消费者处理。当消息过期后,会被自动投递给消费者。
在 RabbitMQ 中,可以通过设置消息的 TTL 来实现延迟消息。TTL 是消息的生存时间,超过 TTL 的消息会被视为可消费的。
在 Java 代码中,可以设置消息的 TTL:
但是,使用 TTL 有一个问题:消息会在 TTL 到期后立即被消费者消费,而不是延迟处理。为了实现真正的延迟消息,我们可以使用 RabbitMQ 的插件或额外的队列设计。@Component public class DelayedMessageProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendDelayedMessage(String message, long delayMs) {// 设置消息的 TTLMessageProperties messageProperties = new MessageProperties();messageProperties.setExpiration(String.valueOf(delayMs));Message rabbitMessage = new Message(message.getBytes(), messageProperties);rabbitTemplate.send("delayed_exchange", "delayed_routing_key", rabbitMessage);} }
- 使用插件:RabbitMQ 提供了多个插件可以实现延迟消息,如
rabbitmq_delayed_message
插件。
rabbitmq_delayed_message
是一个官方插件,允许我们设置消息的延迟时间。使用该插件,我们可以实现精确的延迟消息。
在 Java 代码中,使用插件实现延迟消息:
需要注意的是,使用插件可能会引入额外的复杂性和性能开销。此外,插件的功能和性能可能会受到 RabbitMQ 版本和配置的影响。@Component public class DelayedMessageProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendDelayedMessage(String message, long delayMs) {// 设置消息的延迟时间Map<String, Object> headers = new HashMap<>();headers.put("x-delay", delayMs);MessageProperties messageProperties = new MessageProperties();messageProperties.setHeaders(headers);Message rabbitMessage = new Message(message.getBytes(), messageProperties);rabbitTemplate.send("delayed_exchange", "delayed_routing_key", rabbitMessage);} }
- 使用额外的队列和定时任务:通过设计额外的队列和定时任务,实现延迟消息。具体来说,可以将消息首先发送到一个临时队列,然后通过定时任务将消息移动到目标队列。
这种方法的优点是不需要依赖插件,完全基于标准的 RabbitMQ 功能实现。缺点是实现较为复杂,需要额外的开发和维护。
在 Java 代码中,可以使用定时任务实现延迟消息:
需要注意的是,这种方法可能会引入消息丢失的风险,因为消息在临时队列中可能会被其他消费者处理。此外,定时任务的精度和可靠性也需要考虑。@Component public class DelayedMessageProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendDelayedMessage(String message, long delayMs) {// 发送消息到临时队列rabbitTemplate.send("temp_exchange", "temp_routing_key", message);// 设置定时任务,延迟将消息移动到目标队列Executors.newSingleThreadScheduledExecutor().schedule(() -> {// 从临时队列获取消息List<Message> messages = rabbitTemplate.receive("temp_queue", 0);// 将消息移动到目标队列for (Message message : messages) {rabbitTemplate.send("target_exchange", "target_routing_key", message);}}, delayMs, TimeUnit.MILLISECONDS);} }
顺序消息实现
顺序消息是指消息按照特定的顺序被处理。在某些业务场景中,消息的处理顺序非常重要,如订单处理、交易记录等。
顺序消息的实现方法
- 单线程消费:通过设置消费者为单线程,确保消息按顺序处理。每个消费者只能处理一条消息,消息处理完成后再处理下一条消息。
这种方法简单直接,但可能会降低系统的吞吐量,因为消费者不能并行处理消息。
在 Java 代码中,可以设置消费者为单线程:@Configuration public class RabbitConfig {@Beanpublic SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MyConsumer myConsumer) {SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();container.setConnectionFactory(connectionFactory);container.setQueueNames("my_queue");container.setMessageListener(myConsumer);container.setConcurrentConsumers(1); // 设置并发消费者数量为 1return container;} }
- 消息组:使用消息组(Message Groups)确保消息按顺序处理。消息组是一个逻辑概念,表示一组消息需要按顺序处理。在 RabbitMQ 中,可以通过设置消息的组 ID 来实现。
在 Java 代码中,可以设置消息的组 ID:@Component public class OrderedMessageProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendOrderedMessage(String message, String groupId) {// 设置消息的组 IDMessageProperties messageProperties = new MessageProperties();messageProperties.setGroupId(groupId);Message rabbitMessage = new Message(message.getBytes(), messageProperties);rabbitTemplate.send("ordered_exchange", "ordered_routing_key", rabbitMessage);} }
- 队列绑定顺序:通过设计队列的绑定顺序,确保消息按顺序处理。具体来说,可以将消息先发送到一个预处理队列,处理后再发送到目标队列。
这种方法通过队列的顺序处理来保证消息的顺序性,但可能会增加系统的复杂性和延迟。
在 RabbitMQ 中,可以通过队列的绑定顺序来实现:
在 Java 代码中,可以实现消息的顺序处理:@Configuration public class RabbitConfig {@Beanpublic Queue preProcessQueue() {return new Queue("pre_process_queue");}@Beanpublic Queue targetQueue() {return new Queue("target_queue");}@Beanpublic Binding binding() {return BindingBuilder.bind(targetQueue()).to("pre_process_exchange").with("pre_process_routing_key").noargs();} }
@Component public class PreProcessConsumer {@RabbitListener(queues = "pre_process_queue")public void receiveMessage(String message) {// 处理消息processMessage(message);// 发送消息到目标队列rabbitTemplate.send("target_exchange", "target_routing_key", message);}private void processMessage(String message) {// 具体的预处理逻辑} }
全局顺序消息实现
全局顺序消息是指所有消息按照全局唯一的顺序被处理。在某些业务场景中,消息的全局顺序非常重要,如金融交易、区块链等。
全局顺序消息的实现方法
- 单线程消费+全局锁:通过设置系统为单线程消费,并使用全局锁保证消息处理的顺序性。所有消息只能由一个消费者处理,确保全局顺序。
这种方法简单直接,但系统吞吐量会受到严重影响,特别是在高并发场景下。
在 Java 代码中,可以使用分布式锁实现全局顺序消息:@Component public class GlobalOrderedMessageConsumer {@Autowiredprivate DistributedLock distributedLock;@RabbitListener(queues = "global_ordered_queue")public void receiveMessage(String message) {// 获取全局锁if (distributedLock.acquire("global_lock", 1000)) {try {// 处理消息processMessage(message);} finally {// 释放锁distributedLock.release("global_lock");}} else {// 处理获取锁失败的情况handleLockFailure(message);}}private void processMessage(String message) {// 具体的消息处理逻辑}private void handleLockFailure(String message) {// 处理获取锁失败的情况} }
- 消息分片+顺序处理:将消息按照一定规则分片,每个分片内部按顺序处理。通过合理设计分片规则,可以在一定程度上保证全局顺序。
这种方法通过分片来平衡系统性能和顺序性,但实现较为复杂,需要仔细设计分片规则。
在 Java 代码中,可以使用消息分片实现全局顺序消息:
每个分片的消费者按顺序处理消息:@Component public class GlobalOrderedMessageProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendGlobalOrderedMessage(String message) {// 计算分片 IDString shardId = calculateShardId(message);// 发送消息到对应分片的队列rabbitTemplate.send("shard" + shardId + "_exchange", "shard" + shardId + "_routing_key", message);}private String calculateShardId(String message) {// 根据消息内容计算分片 IDreturn message.hashCode() % 10; // 假设分片数量为 10} }
@Component public class ShardConsumer {@RabbitListener(queues = "shard${shardId}_queue")public void receiveMessage(String message) {// 处理消息processMessage(message);}private void processMessage(String message) {// 具体的消息处理逻辑} }
- 外部排序+消息队列:先将消息存储在外部系统中,按顺序排序后再发送到消息队列。通过外部系统的排序功能,保证消息的全局顺序。
这种方法利用外部系统的排序功能,实现全局顺序消息,但可能会增加系统的复杂性和延迟。
在 Java 代码中,可以使用外部排序实现全局顺序消息:
外部排序服务实现:@Component public class GlobalOrderedMessageProducer {@Autowiredprivate ExternalSortingService externalSortingService;public void sendGlobalOrderedMessage(String message) {// 将消息发送到外部排序服务externalSortingService.sortAndSend(message);} }
@Component public class ExternalSortingService {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sortAndSend(String message) {// 将消息存储到列表中List<String> messages = new ArrayList<>();messages.add(message);// 排序消息Collections.sort(messages);// 发送排序后的消息到消息队列for (String sortedMessage : messages) {rabbitTemplate.send("global_ordered_exchange", "global_ordered_routing_key", sortedMessage);}} }
四.RabbitMQ 底层原理
RabbitMQ 的消息读写和负载均衡机制是其核心功能,理解这些底层原理对于优化系统性能和解决实际问题至关重要。
消息读写机制
RabbitMQ 的消息读写机制涉及消息的生产、传输、存储和消费等多个环节,每个环节都有其特定的实现原理和优化策略。
消息生产机制
在 RabbitMQ 中,消息生产主要涉及以下几个步骤:
- 生产者连接 Broker:生产者通过 TCP 连接到 RabbitMQ Broker,建立网络连接。RabbitMQ 支持多种协议,如 AMQP 0-9-1、AMQP 1.0 和 MQTT 等。
- 声明交换机和队列:生产者需要声明消息将要发送的交换机和队列。交换机是消息路由的起点,队列是消息存储的容器。
- 发送消息:生产者将消息内容和路由信息(如路由键)封装成消息包,发送到 Broker。
- 消息确认:如果启用了消息确认机制,Broker 会向生产者发送确认消息,表示消息已成功接收。
在 Java 代码中,使用 Spring AMQP 发送消息:
@Autowired
private RabbitTemplate rabbitTemplate;
public void sendMessage(String message) {rabbitTemplate.send("my_exchange", "my_routing_key", new Message(message.getBytes(), new MessageProperties()));
}
消息存储机制
RabbitMQ 的消息存储机制主要涉及以下几个方面:
- 内存存储:RabbitMQ 默认将消息存储在内存中,以提高消息读写的性能。内存存储速度快,但消息在系统重启后会丢失。
- 持久化存储:为了提高消息的可靠性,RabbitMQ 支持将消息持久化到磁盘。持久化消息在系统重启后不会丢失,但写入磁盘的开销较大,影响性能。
- 消息索引:RabbitMQ 为消息创建索引,加速消息的查找和检索。索引包括消息的元数据,如消息 ID、队列 ID 等。
- 消息过期:RabbitMQ 支持设置消息的过期时间,过期的消息会自动删除,释放存储空间。
在 RabbitMQ 的配置中,可以通过设置队列参数来控制消息的存储策略:
@Bean
public Queue queue() {Map<String, Object> args = new HashMap<>();args.put("durable", true); // 设置队列为持久化return new Queue("my_queue", true, false, false, args);
}
消息消费机制
在 RabbitMQ 中,消息消费主要涉及以下几个步骤:
- 消费者连接 Broker:消费者通过 TCP 连接到 RabbitMQ Broker,建立网络连接。
- 声明队列:消费者需要声明将要消费的消息队列。
- 订阅队列:消费者订阅队列,表示愿意接收该队列中的消息。
- 接收消息:消费者从队列中接收消息。RabbitMQ 支持两种消息接收模式:轮询(Polling)和推送(Push)。
- 消息确认:消费者处理消息后,向 Broker 发送确认信号,表示消息已成功处理。如果消费者未确认消息,Broker 会认为消息未被成功处理,并会在一定时间后将消息重新发送给其他消费者。
在 Java 代码中,使用 Spring AMQP 消费消息:
@Component
public class MyConsumer {@RabbitListener(queues = "my_queue")public void receiveMessage(String message) {// 处理消息processMessage(message);// 确认消息getChannel().basicAck(getDeliveryTag(), false);}private void processMessage(String message) {// 具体的消息处理逻辑}
}
负载均衡机制
负载均衡是 RabbitMQ 重要的特性之一,它允许系统在多个消费者之间分配消息处理任务,提高系统的吞吐量和可靠性。
负载均衡的实现原理
RabbitMQ 的负载均衡主要通过以下几种机制实现:
- 轮询分发(Round Robin):这是 RabbitMQ 的默认负载均衡策略。消息会按照轮询的方式分发给不同的消费者。每个消费者会接收到大致相同数量的消息。
- 消息分片(Sharding):通过将消息按照一定规则分片,每个分片由特定的消费者处理。这种策略可以保证特定类型的消息总是由特定的消费者处理,适用于消息需要按特定属性分组处理的场景。
- 消费者权重(Consumer Weights):允许为不同的消费者设置不同的权重,权重高的消费者会接收到更多的消息。这种策略可以适应不同消费者处理能力的差异。
- 队列亲和性(Queue Affinity):允许将队列绑定到特定的消费者,确保队列中的消息只由特定的消费者处理。这种策略适用于需要特定消费者处理特定队列消息的场景。
在 RabbitMQ 中,可以通过设置消费者参数来实现不同的负载均衡策略。例如,设置消费者的并发数量:
@Bean
public SimpleMessageListenerContainer container(ConnectionFactory connectionFactory, MyConsumer myConsumer) {SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();container.setConnectionFactory(connectionFactory);container.setQueueNames("my_queue");container.setMessageListener(myConsumer);container.setConcurrentConsumers(5); // 设置并发消费者数量return container;
}
负载均衡的配置与优化
- 合理设置消费者数量:根据系统的处理能力和消息流量,合理设置消费者的数量。过多的消费者会增加系统的开销,过少的消费者则无法充分利用系统的处理能力。
- 配置消费者权重:如果不同消费者的处理能力不同,可以为它们设置不同的权重,使系统能够更有效地分配消息处理任务。
- 优化消息分片策略:根据业务需求和消息特性,设计合理的消息分片策略,确保消息能够被正确地分发给适合的消费者。
- 监控和调整:实施系统的监控机制,实时监控消息队列和消费者的性能,根据监控结果动态调整系统的配置,确保系统的负载均衡和性能最优。
在 Java 代码中,可以设置消费者的权重:
@Bean
public SimpleMessageListenerContainer container1(ConnectionFactory connectionFactory, MyConsumer myConsumer1) {SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();container.setConnectionFactory(connectionFactory);container.setQueueNames("my_queue");container.setMessageListener(myConsumer1);container.setConcurrentConsumers(3); // 设置消费者1的并发数量为3return container;
}
@Bean
public SimpleMessageListenerContainer container2(ConnectionFactory connectionFactory, MyConsumer myConsumer2) {SimpleMessageListenerContainer container = new SimpleMessageListenerContainer();container.setConnectionFactory(connectionFactory);container.setQueueNames("my_queue");container.setMessageListener(myConsumer2);container.setConcurrentConsumers(2); // 设置消费者2的并发数量为2return container;
}
感谢你看到这里,喜欢的可以点点关注哦!
相关文章:
RabbitMQ 深度解析:从核心组件到复杂应用场景
一.RabbitMQ简单介绍 消息队列作为分布式系统中不可或缺的组件,承担着解耦系统组件、保障数据可靠传输、提高系统吞吐量等重要职责。在众多消息队列产品中,RabbitMQ 凭借其可靠性和丰富的特性,在企业级应用中获得了广泛应用。本研究报告将全…...
linux 使用nginx部署ssl证书,将http升级为https
前言 本文基于:操作系统 CentOS Stream 8 使用工具:Xshell8、Xftp8 服务器基础环境: nginx - 请查看 linux 使用nginx部署vue、react项目 所需服务器基础环境,请根据提示进行下载、安装。 1.下载证书 以腾讯云为例ÿ…...
iview 分页改变每页条数时请求两次问题
问题 在iview page分页的时候,修改每页条数时,会发出两次请求。 iview 版本是4.0.0 原因 iview 的分页在调用on-page-size-change之前会调用on-Change。默认会先调用on-Change回到第一页,再调用on-page-size-change改变分页显示数量 此时就会…...
【Hive入门】Hive与Spark SQL深度集成:Metastore与Catalog兼容性全景解析
目录 引言 1 元数据管理体系架构对比 1.1 Hive Metastore架构解析 1.2 Spark Catalog系统设计 2 元数据兼容性深度剖析 2.1 元数据模型映射关系 2.2 元数据同步机制 3 生产环境配置指南 3.1 基础兼容性配置 3.1.1 Spark连接Hive Metastore 3.1.2 多引擎共享配置 3.…...
C#与西门子PLC通信:S7NetPlus和HslCommunication使用指南
西门子S7协议是用来和PLC进行通讯的一个协议,默认端口是102,数据会保存在一个个DB块中,比较经典的用法是一个DB块专门用来读取,一个用来写入。 DB(数据块) {块号}.DBX/DBD/DBW{字节地址}.{位偏移} 1、数据…...
湖北理元理律师事务所:法律科技融合下的债务管理实践
随着债务纠纷数量攀升,如何通过合法途径化解债务风险成为社会焦点。湖北理元理律师事务所作为国家司法局注册的债事服务机构,尝试以“法律技术”重构传统服务模式,为债务人提供系统性解决方案。 专业化服务架构 该律所设立客服、运营、法务…...
Spring Cloud Gateway MVC 基于 Spring Boot 3.4 以 WAR 包形式部署于外部 Tomcat 实战
一、引言 随着微服务架构的广泛应用,Spring Cloud Gateway 作为网关层的核心组件,为服务间的通信与流量管理提供了强大支持。spring-cloud-starter-gateway-mvc 则进一步助力开发者以熟悉的 MVC 模式进行网关开发。同时,将项目以 WAR 包形式…...
LLM论文笔记 27: Looped Transformers for Length Generalization
Arxiv日期:2024.9.25 关键词 长度泛化 transformer结构优化 核心结论 1. RASP-L限制transformer无法处理包含循环的任务的长度泛化 2. Loop Transformer显著提升了长度泛化能力 Input Injection 显著提升了模型的长度泛化性能,尤其在二进制加法等复杂…...
PCIe TLP | 报头 / 包格式 / 地址转换 / 寄存器 / 配置空间类型
注:本文为 “PCIe TLP” 相关文章合辑。 英文引文,机翻未校。 中文引文,未整理去重。 图片清晰度受引文原图所限。 略作重排,如有内容异常,请看原文。 PCIe - TLP Header, Packet Formats, Address Translation, Conf…...
《AI大模型应知应会100篇》第46篇:大模型推理优化技术:量化、剪枝与蒸馏
第46篇:大模型推理优化技术:量化、剪枝与蒸馏 📌 目标读者:人工智能初中级入门者 🧠 核心内容:量化、剪枝、蒸馏三大核心技术详解 实战代码演示 案例部署全流程 💻 实战平台:PyTor…...
C++/SDL 进阶游戏开发 —— 双人塔防(代号:村庄保卫战 20)
🎁个人主页:工藤新一 🔍系列专栏:C面向对象(类和对象篇) 🌟心中的天空之城,终会照亮我前方的路 🎉欢迎大家点赞👍评论📝收藏⭐文章 文章目录 三…...
【Python生成器与迭代器】核心原理与实战应用
目录 前言技术背景与价值当前技术痛点解决方案概述目标读者说明一、技术原理剖析核心概念图解核心作用讲解关键技术模块说明技术选型对比二、实战演示环境配置要求核心代码实现案例1:自定义迭代器类案例2:生成器函数案例3:生成器表达式运行结果验证三、性能对比测试方法论量…...
2025年最新嵌入式开发STM32单片机详细教程(更新中)
ARM 处理器架构 ARM 处理器从 1984 ARM-1 发展到 2004 ARM-11 之后,放弃数字命名,用 cortex 来命令处理器产品。 Cortex-A系列 主打高性能 手机,平板,智能电视等 Cortex-R系列 主打实时 汽车,工业控…...
neatchat轻量级丝滑的ai模型web客户端
NeatChat 人工智能模型对话web客户端 前言 此项目是nextchat分支,相比原者更加简洁流畅。 部署 docker部署 name: next-chat services:chatgpt-next-web:ports:- 8080:3000environment:- OPENAI_API_KEYsk-xx543Ef3d- BASE_URLhttps://api.ai.com- GOOGLE_API_K…...
学习黑客分析案例
▶️ Day 2 任务 – 「怪物图鉴」实战 选一条最新安全事件(国内外均可,建议 1 年内) 例:CVE-2024-21887 Ivanti VPN RCE 用下列表格框架,3 句话归纳它的“派系”“CIA 受击点”“一句话原理”: 攻击流派…...
sonar-scanner在扫描JAVA项目时为什么需要感知.class文件
1 概述 SonarQube是一个静态代码分析工具,主要用于检查源代码的质量,包括代码重复、潜在漏洞、代码风格问题等。而SonarScanner是SonarQube的客户端工具,负责将代码进行形态分析,并将结果发送到SonarQube服务器。所以,…...
AtCoder Beginner Contest 404(ABCDE)
A - Not Found 翻译: 给您一个字符串S,长度在1 到25 之间,由小写英文字母组成。 输出S 中没有出现的一个小写英文字母。 如果有多个这样的字母,可以输出其中任何一个。 思路: 数组记录存在于 s 中的字母。(…...
【言语理解】中心理解题目之结构分析
front:中心理解题目之抓住关键信息 3.1 五种常见对策表达方式 3.1.1 祈使或建议给对策 应该(应) 需要(要) eg:……。对此,媒体要做好自我规约。……。 eg:……。然而,两地仅简单承接…...
DeepSeek-Prover-V2-671B:AI在数学定理证明领域的重大突破
文章目录 什么是DeepSeek-Prover-V2-671B?核心技术亮点1. **超大规模参数与高效推理**2. **超长上下文窗口**3. **强化学习与合成数据** 主要应用场景1. **教育领域**2. **科学研究**3. **工程设计**4. **金融分析** 开源与商业化性能表现总结 2025年4月30日&#x…...
React18组件通信与插槽
1、为DOM组件设置Props 在react中jsx中的标签属性被称为Props DOM组件的类属性,为了防止与js中的class属性冲突改成了className DOM组件的style属性 import image from "./logo.svg"; function App() {const imgStyleObj {width: 200,height: 200,};re…...
第15章 对API的身份验证和授权
第15章 对API的身份验证和授权 在构建RESTful API时,确保只有经过身份验证和授权的用户才能访问特定资源是至关重要的。身份验证是确认用户身份的过程,而授权则是决定用户是否有权访问特定资源的过程。在本章中,我们将详细探讨如何在ASP.NET Core Web API中实现身份验证和授…...
【项目归档】数据抓取+GenAI+数据分析
年后这两个月频繁组织架构变动,所以博客很久没更新。现在暂时算是尘埃落定,趁这段时间整理一下。 入职九个月,自己参与的项目有4个,负责前后端开发,测试,devops(全栈/doge)ÿ…...
如何优化MySQL主从复制的性能?
优化MySQL主从复制的性能需要从硬件、配置、架构设计和运维策略等多方面入手。以下是详细的优化方案: 一、减少主库写入压力 1. 主库优化 二进制日志(binlog)优化: 使用 binlog_formatROW 以获得更高效的复制和更少的数…...
asp.net客户管理系统批量客户信息上传系统客户跟单系统crm
# crm-150708 客户管理系统批量客户信息上传系统客户跟单系统 # 开发背景 本软件是给郑州某企业管理咨询公司开发的客户管理系统软件 # 功能 1、导入客户数据到系统 2、批量将不同的客户分配给不同的业务员跟进 3、可以对客户数据根据紧急程度标记不同的颜色,…...
PCIe | TLP | 报头 / 包格式 / 地址转换 / 配置空间 / 寄存器 / 配置类型
注:本文为 “PCIe - TLP” 相关文章合辑。 英文引文,机翻未校。 中文引文,未整理去重。 图片清晰度受引文原图所限。 略作重排,如有内容异常,请看原文。 PCIe - TLP Header, Packet Formats, Address Translation, Co…...
ip和域名
好的,我来依次回答你的问题: 域名和 IP 地址是什么关系? IP 地址 (Internet Protocol Address):可以想象成互联网上每台设备(比如服务器、电脑、手机)的门牌号码。它是一串数字(例如 IPv4 地址 …...
《解锁GCC版本升级:开启编程新世界大门》
《解锁GCC版本升级:开启编程新世界大门》 一、引言:GCC 版本升级的魔法钥匙 在编程的广阔天地里,GCC(GNU Compiler Collection)宛如一座灯塔,为无数开发者照亮前行的道路。它是一款开源且功能强大的编译器集合,支持 C、C++、Objective - C、Fortran、Ada 等多种编程语言…...
前端跨域问题怎么在后端解决
目录 简单的解决方法: 添加配置类: 为什么会跨域 1. 什么是源 2. URL结构 3. 同源不同源举🌰 同源例子 不同源例子 4. 浏览器为什么需要同源策略 5. 常规前端请求跨域 简单的解决方法: 添加配置类: packag…...
生成式 AI 的工作原理
在科技浪潮汹涌澎湃的当下,生成式 AI 宛如一颗璀璨的新星,照亮了我们探索未知的征程。它不再仅仅是科幻电影中的幻想,而是已经悄然融入我们生活的方方面面,从智能客服的贴心应答,到艺术创作的天马行空,生成式 AI 正以一种前所未有的姿态重塑着世界。然而,你是否曾好奇,…...
DeepSeek辅助学术写作之修订与校稿以及发表与推广相关提示词分享祝你顺利毕业~
目录 1.修订与校对 2.发表与推广 大家好这里是AIWritePaper官方账号,官网👉AIWritePaper~ 宝子们可以使用小编精选的“ChatGPT研究论文提示词”集合来创建研究论文。利用ChatGPT的智能回应生成详尽有效的内容,这样可以加快研究论文的策划、…...
叠层阻抗线框
1.阻抗介绍 特性阻抗:又称“特征阻抗”,它不是直流电阻,属于长线传输中的概念。在高频范围内,信号传输过程中,信号沿到达的地方,信号线和参考平面(电源或地平面)间由于电场的建立&am…...
大数据:驱动技术创新与产业转型的引擎
📝个人主页🌹:慌ZHANG-CSDN博客 🌹🌹期待您的关注 🌹🌹 在全球数字化转型的浪潮中,大数据已经成为推动各行各业革新的核心力量。随着信息技术的迅猛发展,数据的收集、存储、处理和分析能力不断提升,大数据不仅改变了企业的运营模式,更引领了技术创新和产业结构…...
C++继承基础总结
引言 在编写多个类时,类之间可能会存在多个相同的成员变量,导致代码冗余度过高,C继承的出现,使得我们可以在已有类的基础上构建新类,从而实现代码复用与结构扩展。 一、继承的基本概念 继承是指子类(派生…...
2025年斯诺克世锦赛——“75三杰”在1/4决赛作为对手的概率
“在最近的斯诺克世锦赛中,中国两名球员成功挺进前8强。此外,前8强也出现令人感慨的一幕:75三杰全部进入到了八强,这也是历史第5次,50岁正是打拼的年纪!传奇之旅继续!”——摘自50岁正是打拼的年…...
Python绘制地球的重力地图
文章目录 Boule重力地图从ensaio下载重力数据Boule boule中定义了多种参考椭球,可用于表示地球、火星等星体的重力分布。可通过pip安装 pip install bouleboule中已经定义的椭球如下 椭球GRS80WGS84MARSMERCURYMOONVENUSVESTA星体地球地球火星水星月球金星灶神星这些椭球可直…...
多端定制系统开发:打造高效全平台覆盖的APP解决方案
在移动互联网时代,用户需求日益多样化,企业若想触达更广泛的受众,仅靠单一平台的应用已远远不够。无论是iOS、Android、Web端,还是智能手表、车载设备等新兴终端,多端适配已成为企业数字化转型的刚需。多端定制系统开发…...
WGDI-分析WGD及祖先核型演化的集成工具-文献精读126
WGDI: A user-friendly toolkit for evolutionary analyses of whole-genome duplications and ancestral karyotypes WGDI:一款面向全基因组重复事件与祖先核型演化分析的易用工具集 摘要 在地球上大多数主要生物类群中,人们已检测到全基因组复制&…...
旋转矩阵公式理解
这里给出其中一种理解方法: 设原始直角坐标系下的坐标为(x,y),我们可以将它分解为两个向量(x,0)和(0,y)。接下来将两个向量分别顺时针旋转θ度,二者就会分别变成:(xcosθ,xsinθ)和(-ysinθ,ycosθ)。 也就是说,二者…...
网络Tips20-002
1..某主机接口的IP地址为192.16.7.131/26.则该IP地址所在网络的广播地址是:192 16.7.191 广播地址是指在特定网络上发送广播消息的地址。它用于向网络上的所有设备发送信息。 方法1:广播地址掩码取反和网络地址的或运算 方法2:广播地址将网…...
firewall docker 冲突问题解决(亲测有效)
# 关闭iptables,使用firewall systemctl disable iptables # 禁用服务 systemctl stop iptables # 关闭服务 systemctl status iptables # 查看服务状态 systemctl enable firewalld # 设置防火墙开机自启动 systemctl start firewalld # 开启服务 systemctl s…...
SwiftUI-MLX本地大模型开发(二)
介绍 在 SwiftUI-MLX本地大模型开发一文中,我们已经详细讲了如何利用 MLX 进行本地大模型的开发。但是通过案例可以发现 2 个问题: MLX 内置的大模型数量有限。每次大模型都需要从 HuggingFace 下载。 如何解决这 2 个问题,方案是:…...
基于「骑手外卖系统」串联7大设计原则
你说得对!这些设计原则听起来都很抽象、很“玄”,如果不是实际开发过系统,很难理解“到底为什么要这样设计”。 那我现在就用一个你能想象得很清楚的真实例子,帮你把这7个设计原则一一落地到具体情境里,你会一眼明白。…...
泰迪杯特等奖案例学习资料:基于时空图卷积网络的城市排水系统水位精准重建与异常检测
(第十四届泰迪杯数据挖掘挑战赛A题特等奖案例解析) 一、案例背景与核心挑战 1.1 应用场景与行业痛点 城市排水系统(Urban Drainage Network, UDN)是城市基础设施的重要组成部分,其运行效率直接影响防洪排涝能力和水环境质量。然而,实际运维中面临以下难题: 监测数据稀…...
嵌入式Linux应用项目----智能网关
一、网关概述: Linux网关的作用在于,通过蓝牙、LoRa、串口、CAN等接口,与哪些无法连接网络的设备建立联系,将它们的数据转发至服务器。这过程中,网关充当了数据的桥梁,将下级设备所产生的数据发送至服务器。…...
C++ 中的继承
目录 前言 一、继承的概念及定义 二、基类和派生类对象赋值转换 三、继承中的作用域 四、派生类的默认成员函数 五、继承与友元 六、继承与静态成员 七、复杂的菱形继承及菱形虚拟继承 (一)单继承与多继承 (二)菱形继承 …...
【中间件】brpc_基础_用户态线程上下文
文章目录 context介绍1 简单介绍2 主要功能2.1 上下文结构定义2.2 上下文切换实现2.3 协程栈管理2.4 平台兼容性处理2.5 性能优化 3 关键代码逻辑示例3.1 上下文初始化 (bthread_make_fcontext)3.2 上下文切换 (bthread_jump_fcontext) 4 与 BRPC 其他模块的协作5 性能与稳定性…...
蟋蟀的叫声,大自然的温度计
夏夜草丛中,蟋蟀的鸣叫声此起彼伏。有趣的是,它们的叫声频率竟然与气温有关!根据图片中的公式: 气温 ( X 8 ) 5 9 \text{气温} \frac{(X 8) \times 5}{9} 气温9(X8)5 只需记录蟋蟀在 15 秒内的鸣叫次数( X X X…...
文献总结:TPAMI端到端自动驾驶综述——End-to-End Autonomous Driving: Challenges and Frontiers
端到端自动驾驶综述 1. 文章基本信息2. 背景介绍3. 端到端自动驾驶主要使用方法3. 1 模仿学习3.2 强化学习 4. 测试基准4.1 真实世界评估4.2 在线/闭环仿真测试4.3 离线/开环测试评价 5. 端到端自动驾驶面临的挑战5.1 多模态输入5.2 对视觉表征的依赖5.3 基于模型的强化学习的世…...
二极管反向恢复的定义和原理
二极管的反向恢复定义 二极管的反向恢复是指二极管从正向导通状态切换到反向阻断状态时,电流从正向变为负向并最终回到零所需的时间。具体过程如下: 正向导通:当二极管正向偏置时,电流可以顺利通过,此时二极管处于导…...
# 基于词袋模型(BoW)的猫狗图像分类实践
基于词袋模型(BoW)的猫狗图像分类实践 在计算机视觉领域,图像分类是一项基础且重要的任务。本文将介绍如何使用词袋模型(Bag of Words, BoW)结合支持向量机(SVM)实现猫狗图像分类。通过详细的代…...