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

#RabbitMQ# 消息队列进阶

目录

消息可靠性

一 生产者的可靠性

1 生产者的重连

2 生产者的确认

(1 Confirm*

(2 Return

二 MQ的可靠性

1 数据持久化

2 Lazy Queue*

三 消费者的可靠性

1 消费者确认机制

2 消费失败处理

3 业务幂等性

四 延迟消息


消息可靠性

在消息队列中,可靠性(Reliability) 是指确保消息在 生产者、Broker(消息中间件)、消费者 三者之间传输时,不会因系统故障、网络问题或程序错误而丢失或重复处理。其核心目标是保证消息从发送到最终消费的 完整性和一致性

首先整体概括一下大体的内容,首先我们研究的对象为三个,生产者消费者Broker

生产者的可靠性的保障需要有retry重试机制以及确认机制。确认机制有两个confirm和return(confirm是监听消息到达Broker,return是监听消息从交换机路由到队列)retry是生产者连接或 Confirm 失败后的重试。

Broker可靠性需要数据的持久化以及懒队列存储方式(默认)的实现

消费者的可靠性的保障需要有确认机制以及失败重试机制,确认机制主要是分为两种,一种是自动一种是手动。失败重试机制先可以指定重试规则如果失败可借助死信队列的机制实现最终的处理

一 生产者的可靠性

1 生产者的重试

可以人为配置,但是要注意配置的时长,防止影响业务性能。并且SpringAMQP的重试机制默认是阻塞式重试,会出现线程资源占用。

2 生产者的确认

首先生产者的确认有两种机制,一种是Publisher Confirm 一种是Publisher Return.

(1 Confirm*

其作用是确保将信息发送到Broke.

1 配置:开启confirm机制

correlated:使用异步回调,自己可以定义一个回调实现类用于接收回调消息,不需要阻塞等待,可以提高性能。

# 配置
spring:rabbitmq:publisher-confirm-type: correlated  # 开启Confirm模式

2 然后编写回调实现类

这个onfirm方法的作用是当生产者通过RabbitTemplate发送消息时,若启动Confirm机制,消息会异步发送到Broker,在Broke处理完消息后,会向生产者发送ack,或者nack,这个重写的confirm方法是在底层被SpringAMQP监听的,当SpringAMQP监听到Broke返回的确认结果时,会自动调用已注册的ConfirmCallBack实现类的confirm方法。

@Component
@Slf4j
public class RabbitConfirmCallback implements RabbitTemplate.ConfirmCallback {@Overridepublic void confirm(CorrelationData correlationData, boolean ack, String cause) {if (ack) {log.info("Confirm 成功: 消息ID={}", correlationData.getId());} else {log.error("Confirm 失败: 消息ID={}, 原因={}", correlationData.getId(), cause);}}
}

3 发送消息

@Service
public class OrderProducer {@Autowiredprivate RabbitTemplate rabbitTemplate;public void sendOrderMessage(String message) {// 1. 创建关联数据(用于 Confirm 回调)CorrelationData cd = new CorrelationData(UUID.randomUUID().toString());// 2. 发送消息到交换机rabbitTemplate.convertAndSend("order.direct",  // 交换机名称"order.key",    // 路由键message,         // 消息内容cd              // 关联数据);log.info("消息已发送: ID={}", cd.getId());}
}

(2 Return

其作用是确保信息到达Broke后,并能正确路由到队列。(这种情况可以避免,人为确保可以正确路由)

1 配置开启Return机制

publisher-returns: true              # 启用Return机制

2 回调实现类Return

@Component
@Slf4j
public class RabbitReturnCallback implements RabbitTemplate.ReturnsCallback {@Overridepublic void returnedMessage(ReturnedMessage returned) {log.error("Return 回调: 消息无法路由到队列 [Exchange={}, RoutingKey={}, ReplyCode={}]", returned.getExchange(), returned.getRoutingKey(), returned.getReplyCode());}
}

二 MQ的可靠性

在默认情况下,RabbitMQ会将连接收到的消息保存在内存中以降低消息收发的延迟。

  • 一旦MQ宕机,内存当中的消息会丢失
  • 内存空间有限,当消费者故障或者处理过慢时,会导致消息积压,会触发PageOut机制存储,将消息写入磁盘,但是会引发MQ阻塞。

1 数据持久化

RabbitMQ实现数据持久化包括3个方面

  • 交换机持久化
  • 队列持久化
  • 消息持久化
  1. 三要素需同时持久化

    • 队列(durable=true)、交换机(durable=true)、消息(deliveryMode=2三者缺一不可。
    • 任意一环未持久化,消息可能丢失。
    • 队列持久化与交换机持久化都可以通过注解当中的配置属性指定。
  2. 消费者手动确认

    • 消费端需关闭自动确认(autoAck=false),并手动发送ACK(channel.basicAck())后,消息才会从队列删除。

代码:

举例说明

@Configuration
public class RabbitMQConfig {// 持久化交换机@Beanpublic DirectExchange durableExchange() {return new DirectExchange("durable.direct", true, false); // durable=true}// 持久化队列@Beanpublic Queue durableQueue() {return new Queue("durable.queue", true); // durable=true}// 绑定关系@Beanpublic Binding durableBinding() {return BindingBuilder.bind(durableQueue()).to(durableExchange()).with("routing.key");}
}
@Service
@RequiredArgsConstructor
public class MessageProducer {private final RabbitTemplate rabbitTemplate;public void sendPersistentMessage() {// 消息内容String message = "hello everyone!";// 创建消息属性并设置持久化MessageProperties properties = new MessageProperties();properties.setDeliveryMode(MessageDeliveryMode.PERSISTENT); // deliveryMode=2// 构建消息Message amqpMessage = new Message(message.getBytes(), properties);// 发送消息(交换机、路由键、消息、关联数据)rabbitTemplate.convertAndSend("durable.direct",   // 交换机名称"routing.key",      // 路由键amqpMessage,        // 消息对象new CorrelationData(UUID.randomUUID().toString()));}
}

持久化的本质是通过磁盘写入实现数据的持久存储

2 Lazy Queue*

从 RabbitMQ 3.6.0 版本开始,引入了惰性队列 Lazy Queue 的概念,也就是惰性队列。

惰性队列的特性:

  • 接收到消息后直接存入磁盘而非内存。(内存中会存储少量消息)
  • 消费者要获取消息时才会从磁盘中读取并加载到内存当中。
  • 支持数百万条的消息存储。
  • 在3.12版本之后所有的队列都是LazyQueue模式,无法更改。
  • 开启持久化和生产者确认时,只有在消息持久化完成后才会给生产者返回ACK

如果是老版本的话需要手动再指定,新版本不需指定,默认

@RabbitListener(queuesToDeclare = @Queue(name = "explicit.lazy.queue",durable = "true",arguments = @Argument(name = "x-queue-mode", value = "lazy") // 显式设置惰性队列
))
public void listen(String msg) {// 处理消息
}
@Bean
public Queue explicitLazyQueue() {return QueueBuilder.durable("explicit.lazy.queue").lazy() // 显式声明惰性队列(兼容低版本 RabbitMQ).build();
}

三 消费者的可靠性

1 消费者确认机制

概念:为了确认消费者是否成功处理消息,RabbitMQ提供了消费者确认机制。当消费者消息处理结束时向Broker发送一个回执,告知消息处理的状态。

  • ack:成功处理消息,RabbitMQ从队列当中删除消息。
  • nack:消息处理失败,RabbitMQ需要再次投递消息。
  • reject:消息处理失败并拒绝该消息,RabbitMQ从队列中删除该消息。

配置

spring:rabbitmq:host: 192.168.100.100port: 5672virtual-host: /hmallusername: hmallpassword: 123456connection-timeout: 1slistener:simple:prefetch: 1acknowledge-mode: auto #开启消费者确认机制publisher-confirm-type: correlated # 开启confirm模式

2 消费失败处理

自动确认

当消费者出现异常后,消息会不断requeue重新入队,再重新发送给消费者,然后再次异常,再次requeue,无限循环,导致mq的消息处理飙升,带来不必要的压力。

    listener:simple:prefetch: 1acknowledge-mode: auto # 默认自动确认retry:enabled: true # 开启消费者重试机制max-attempts: 3 # 最大重试次数multiplier: 1 # 重试时间间隔initial-interval: 1000ms # 初始重试时间间隔

本地重试(Local Retry)
Spring 的 retry 机制通过 本地重试 替代 RabbitMQ 的 requeue,避免消息反复入队。从而提升性能。

将失败策略修改为RepublicMessageRecoverer,首先先定义接收失败的交换机队列机器绑定关系,然后定义RepublicMessageRecoverer

@Configuration
@ConditionalOnProperty(prefix = "spring.rabbitmq.listener.simple.retry", name = "enabled",havingValue = "true")
public class RabbitListenerConfig {@Beanpublic MessageRecoverer republishMessageRecoverer(RabbitTemplate rabbitTemplate) {// 关键点:失败时将消息重新发布到死信交换机return new RepublishMessageRecoverer(rabbitTemplate,"dlx.direct",   // 死信交换机名称"dlx.routing.key" // 死信路由键);}
}

然后再定义:死信队列+死信交换机+正常队列

首先定义死信交换机死信队列并绑定,然后定义队列规则,将死信队列交换机与队列绑定

可在yml配置中编写重试规则

    // 1. 定义死信交换机(DLX)@Beanpublic DirectExchange dlxExchange() {return new DirectExchange("dlx.direct", true, false);}// 2. 定义死信队列(DLQ)@Beanpublic Queue dlq() {return new Queue("dlq.queue", true);}// 3. 绑定死信队列到死信交换机@Beanpublic Binding dlqBinding() {return BindingBuilder.bind(dlq()).to(dlxExchange()).with("dlx.routing.key");}// 4. 定义原始队列,并绑定到死信交换机@Beanpublic Queue originQueue() {Map<String, Object> args = new HashMap<>();args.put("x-dead-letter-exchange", "dlx.direct");    // 指定死信交换机args.put("x-dead-letter-routing-key", "dlx.routing.key"); // 指定死信路由键return new Queue("origin.queue", true, false, false, args);}

如果这个队列出现重试依旧出错将会进入死信交换机后再路由进入死信队列

生产者发送消息 → 原始队列(origin.queue)↓
消费者尝试处理消息 → 成功 → ACK↓
消费者处理失败 → 重试 → 仍失败 → 拒绝消息(requeue=false)↓
消息变为死信 → 转发到死信交换机(dlx.direct)↓
死信交换机根据路由键(dlx.routing.key) → 路由到死信队列(dlx.queue)↓
死信队列中的消息被专门的消费者处理(如人工干预、日志记录、重试等)

手动确认

1 xml文件

spring:rabbitmq:host: localhostport: 5672username: guestpassword: guestlistener:simple:acknowledge-mode: manual # 手动确认模式retry:enabled: true         # 开启消费者重试max-attempts: 3       # 最大重试次数(含首次消费)initial-interval: 1000 # 初始重试间隔(ms)multiplier: 2          # 重试间隔乘数(1s → 2s → 4s)max-interval: 10000    # 最大重试间隔(ms)

2 声明队列,交换机以及绑定关系

@Configuration
public class RabbitMQConfig {// 正常业务交换机public static final String ORDER_EXCHANGE = "order_exchange";// 正常业务队列public static final String ORDER_QUEUE = "order_queue";// 死信交换机public static final String DLX_EXCHANGE = "dlx_exchange";// 死信队列public static final String DLX_QUEUE = "dlx_queue";/*** 声明正常业务交换机(直连交换机)*/@Beanpublic DirectExchange orderExchange() {return new DirectExchange(ORDER_EXCHANGE);}/*** 声明正常业务队列,并绑定死信交换机*/@Beanpublic Queue orderQueue() {return QueueBuilder.durable(ORDER_QUEUE).deadLetterExchange(DLX_EXCHANGE) // 绑定死信交换机.deadLetterRoutingKey("dlx.routing.key") // 死信路由键.build();}/*** 绑定队列与交换机*/@Beanpublic Binding orderBinding() {return BindingBuilder.bind(orderQueue()).to(orderExchange()).with("order.routing.key");}/*** 声明死信交换机(直连交换机)*/@Beanpublic DirectExchange dlxExchange() {return new DirectExchange(DLX_EXCHANGE);}/*** 声明死信队列*/@Beanpublic Queue dlxQueue() {return new Queue(DLX_QUEUE, true);}/*** 绑定死信队列与交换机*/@Beanpublic Binding dlxBinding() {return BindingBuilder.bind(dlxQueue()).to(dlxExchange()).with("dlx.routing.key");}
}

3 配置重试策略与失败消息转发

@Configuration
public class RetryConfig {@Autowiredprivate RabbitTemplate rabbitTemplate;/*** 配置重试策略*/@Beanpublic RetryPolicy retryPolicy() {return new SimpleRetryPolicy();}/*** 配置重试模板*/@Beanpublic RetryTemplate retryTemplate(RetryPolicy retryPolicy) {RetryTemplate retryTemplate = new RetryTemplate();retryTemplate.setRetryPolicy(retryPolicy);// 指数退避策略ExponentialBackOffPolicy backOffPolicy = new ExponentialBackOffPolicy();backOffPolicy.setInitialInterval(1000);    // 初始间隔 1sbackOffPolicy.setMultiplier(2);            // 间隔乘数backOffPolicy.setMaxInterval(10000);       // 最大间隔 10sretryTemplate.setBackOffPolicy(backOffPolicy);return retryTemplate;}/*** 配置消息恢复器(失败后投递到死信交换机)*/@Beanpublic MessageRecoverer messageRecoverer() {return new RepublishMessageRecoverer(rabbitTemplate,RabbitMQConfig.DLX_EXCHANGE,  // 死信交换机"dlx.routing.key"             // 死信路由键);}/*** 配置监听器容器工厂(核心)*/@Beanpublic SimpleRabbitListenerContainerFactory rabbitListenerContainerFactory(ConnectionFactory connectionFactory,RetryTemplate retryTemplate,MessageRecoverer messageRecoverer) {SimpleRabbitListenerContainerFactory factory = new SimpleRabbitListenerContainerFactory();factory.setConnectionFactory(connectionFactory);factory.setAcknowledgeMode(AcknowledgeMode.MANUAL); // 手动确认// 配置重试factory.setAdviceChain(RetryInterceptorBuilder.stateless().retryOperations(retryTemplate).recoverer(messageRecoverer) // 失败后转发到死信队列.build());return factory;}
}

4 消费者实现

@Component
public class OrderConsumer {private static final Logger log = LoggerFactory.getLogger(OrderConsumer.class);@RabbitListener(queues = RabbitMQConfig.ORDER_QUEUE)public void handleOrderMessage(@Payload Order order, Channel channel, @Header(AmqpHeaders.DELIVERY_TAG) long deliveryTag) {try {// 业务处理逻辑(模拟可能抛出异常)processOrder(order);// 处理成功,手动确认channel.basicAck(deliveryTag, false);log.info("订单处理成功: {}", order.getId());} catch (Exception e) {// 记录异常日志log.error("订单处理失败: {}", order.getId(), e);// 手动拒绝消息(不重新入队,由重试机制处理)channel.basicNack(deliveryTag, false, false);}}private void processOrder(Order order) throws Exception {// 模拟业务处理(如数据库操作、远程调用等)if (order.getAmount() < 0) {throw new IllegalArgumentException("订单金额非法");}// ... 其他业务逻辑}
}

死信队列消费者

@Component
public class DlxConsumer {@RabbitListener(queues = RabbitMQConfig.DLX_QUEUE)public void handleDlxMessage(Order failedOrder) {// 记录日志或人工处理System.err.println("收到死信消息: " + failedOrder);// 可在此处发送告警邮件或记录到数据库}
}

3 业务幂等性

解决方案:

1 唯一id

代码:

    @Beanpublic MessageConverter jacksonMessageConvertor() {Jackson2JsonMessageConverter converter = new Jackson2JsonMessageConverter();converter.setCreateMessageIds(true);return converter;}

为每一条消息设置一个全局唯一id以达到幂等性

2 业务判断

四 延迟消息

1 死信交换机

2 延迟消息插件

RabbitMQ系列(18)--RabbitMQ基于插件实现延迟队列_rabbitmq延迟队列插件-CSDN博客

spring:rabbitmq:host: 127.0.0.1  # RabbitMQ服务器地址port: 5672         # RabbitMQ默认端口username: guest    # RabbitMQ用户名password: guest    # RabbitMQ密码virtual-host: /    # 虚拟主机路径(默认为/)# 连接工厂配置connection-timeout: 5000ms  # 连接超时时间requested-heartbeat: 60s    # 心跳间隔requested-channel-max: 200  # 最大通道数publisher-confirm-type: correlated  # 启用生产者确认publisher-returns: true             # 启用未路由消息返回template:retry:enabled: true                   # 启用重试机制max-attempts: 3                 # 最大重试次数initial-interval: 1000ms        # 初始重试间隔listener:simple:acknowledge-mode: manual      # 消费者手动确认模式auto-startup: true            # 自动启动监听器concurrency: 1-5              # 消费者线程池范围(最小1~最大5)max-concurrency: 10           # 最大并发数prefetch-count: 10            # 每次从MQ拉取的消息数量(防止内存溢出)default-requeue-rejected: false  # 拒绝的消息不重新入队retry:enabled: true               # 启用消费者重试max-attempts: 3             # 最大重试次数initial-interval: 2000ms    # 初始重试间隔# 消息转换器配置(JSON序列化)message-converter: jackson2JsonMessageConverter  # 使用Jackson2JsonMessageConverter# 高级配置(惰性队列)lazy-queues: true  # 启用惰性队列(RabbitMQ 3.12+ 默认开启)queue:mode: lazy       # 显式声明队列为惰性队列(兼容旧版本)version: 2       # 使用Classic Queue v2(CQv2)优化性能# RabbitMQ连接超时相关配置(可选)
rabbitmq:connection:timeout: 5000msblocking-io-timeout: 5000ms  # 阻塞连接检查超时时间

相关文章:

#RabbitMQ# 消息队列进阶

目录 消息可靠性 一 生产者的可靠性 1 生产者的重连 2 生产者的确认 (1 Confirm* (2 Return 二 MQ的可靠性 1 数据持久化 2 Lazy Queue* 三 消费者的可靠性 1 消费者确认机制 2 消费失败处理 3 业务幂等性 四 延迟消息 消息可靠性 在消息队列中&#xff0c;可靠性…...

React从基础入门到高级实战:React 核心技术 - React Router:路由管理

React Router&#xff1a;路由管理 在现代 Web 应用开发中&#xff0c;路由管理 是构建多页面或单页应用&#xff08;SPA&#xff09;的核心技术之一。React Router 是 React 生态中最受欢迎的路由管理库&#xff0c;它为开发者提供了强大的工具来实现页面导航、动态路由和权限…...

【深度学习】损失“三位一体”——从 Fisher 的最大似然到 Shannon 的交叉熵再到 KL 散度,并走进 PET·P-Tuning微调·知识蒸馏的实战

一页速览&#xff1a; 1912 Fisher 用最大似然把「让数据出现概率最高」变成参数学习&#xff1b; 1948 Shannon 把交叉熵解释成「最短平均编码长度」&#xff1b; 1951 Kullback-Leibler 用相对熵量化「多余信息」。 三条历史线落到今天深度学习同一个损失——交叉熵。 也…...

5 分钟速通密码学!

让我们开始第一部分&#xff1a;密码学基础 (Cryptography Basics)。 第一部分&#xff1a;密码学基础 (Cryptography Basics) 1. 什么是密码学&#xff1f; 想象一下&#xff0c;在古代战争中&#xff0c;将军需要向远方的部队传递作战指令。如果直接派人送信&#xff0c;信…...

Linux——IP协议

1. 现实意义 • IP协议&#xff1a;提供一种能力&#xff0c;把数据报从主机A跨网络送到主机B • TCP/IP协议&#xff1a;核心功能&#xff0c;把数据100%可靠的从主机A跨网络送到主机B 注&#xff1a;TCP协议负责百分百可靠&#xff0c;通过三次握手、滑动窗口、拥塞控制、延…...

Lua 脚本在 Redis 中的运用-24 (使用 Lua 脚本实现原子计数器)

实践练习:使用 Lua 脚本实现原子计数器 实现原子计数器是许多应用程序中的常见需求,例如跟踪网站访问量、限制 API 请求或管理库存。虽然 Redis 提供了 INCR 命令用于递增整数,但在复杂场景或与其他操作结合时直接使用它可能并不足够。本课程探讨了如何在 Redis 中利用 Lua…...

Linux信号量(32)

文章目录 前言一、POSIX 信号量信号量的基础知识信号量的基本操作 二、基于环形队列实现生产者消费者模型环形队列单生产单消费模型多生产多消费模型 总结 前言 加油&#xff0c;加油&#xff01;&#xff01;&#xff01; 一、POSIX 信号量 信号量的基础知识 互斥、同步 不只…...

技术视界 | 打造“有脑有身”的机器人:ABC大脑架构深度解析(上)

ABC大脑架构&#xff1a;连接大模型与物理世界的具身智能新范式 在具身智能和类人机器人技术快速发展的背景下&#xff0c;如何高效整合“大模型的认知理解能力”与“对真实物理世界的精准控制”&#xff0c;成为当前智能体系统设计中最具挑战性也是最关键的问题之一。尽管大语…...

使用堡塔和XShell

使用堡塔和XShell 一、SSH协议介绍 SSH为SecureShell的缩写&#xff0c;由IETF的网络小组(NetworkWorkingGroup)所制定;SSH为建立在应用层基础上的安全协议。SSH是较可靠&#xff0c;专为远程登录会话和其他网络服务提供安全性的协议。利用SSH协议可以有效防止远程管理过程中…...

软件项目交付阶段,验收报告记录了什么?有哪些标准要求?

软件项目交付阶段&#xff0c;验收报告扮演着至关重要的角色&#xff0c;它相当于一份详尽的“成绩单”&#xff0c;具体记录了项目完成的具体情况以及是否达到了既定的标准。 项目基本信息 该环节将展示软件项目的核心信息&#xff0c;包括项目名称、开发团队构成、项目实施…...

LightGBM的python实现及参数优化

文章目录 1. LightGBM模型参数介绍2. 核心优势3. python实现LightGBM3.1 基础实现3.1.1 Scikit-learn接口示例3.1.2 Python API示例 3.2 模型调优3.2.1 GridSearchCV简介3.2.2 LightGBM超参调优3.2.3 GridSearchCV寻优结果解读 在之前的文章 Boosting算法【AdaBoost、GBDT 、X…...

封装渐变堆叠柱状图组件附完整代码

组件功能 这是一个渐变堆叠柱状图组件&#xff0c;主要功能包括&#xff1a; 在一根柱子上同时显示高、中、低三种危险级别数据使用渐变色区分不同危险级别&#xff08;高危红色、中危橙色、低危蓝色&#xff09;悬停显示详细数据信息&#xff08;包括总量和各级别数据&#…...

分布式项目保证消息幂等性的常见策略

Hello&#xff0c;大家好&#xff0c;我是灰小猿&#xff01; 在分布式系统中&#xff0c;由于各个服务之间独立部署&#xff0c;各个服务之间依靠远程调用完成通信&#xff0c;再加上面对用户重复点击时的重复请求等情况&#xff0c;所以如何保证消息消费的幂等性是在分布式或…...

山东大学软件学院创新项目实训开发日志——第十三周

目录 1.开展prompt工程&#xff0c;创建个性化AI助理&#xff0c;能够基于身份实现不同角度和语言风格的回答。 2.对输出进行格式化&#xff0c;生成特定格式的会议计划文档。 3.学习到的新知识 本阶段我所做的工作 1.开展prompt工程&#xff0c;创建个性化AI助理&#xff…...

如何在sublime text中批量为每一行开头或者结尾添加删除指定内容

打开你的文件&#xff1a;首先&#xff0c;在 Sublime Text 中打开你想要编辑的文件&#xff0c;然后全选 行首插入&#xff1a; 选择所有行的开头&#xff1a; 使用快捷键 Ctrl Shift L&#xff08;Windows/Linux&#xff09;或 Cmd Shift L&#xff08;Mac&#xff09;&…...

Cesium 透明渐变墙 解决方案

闭合路径修复 通过增加额外点确保路径首尾相接 透明渐变效果 使用RGBA颜色模式实现从完全不透明到完全透明的平滑渐变 参数可调性 提供多个可调参数&#xff0c;轻松自定义颜色、高度和圆环尺寸 完整代码实现 <!DOCTYPE html> <html> <head><meta …...

网络原理与 TCP/IP 协议详解

一、网络通信的本质与基础概念 1.1 什么是网络通信&#xff1f; 网络通信的本质是跨设备的数据交换&#xff0c;其核心目标是让不同物理位置的设备能够共享信息。这种交换需要解决三个核心问题&#xff1a; 如何定位设备&#xff1f; → IP地址如何找到具体服务&#xff1f;…...

day022-定时任务-故障案例与发送邮件

文章目录 1. cron定时任务无法识别命令1.1 故障原因1.2 解决方法1.2.1 对命令使用绝对路径1.2.2 在脚本开头定义PATH 2. 发送邮件2.1 安装软件2.2 配置邮件信息2.3 巡检脚本与邮件发送2.3.1 巡检脚本内容2.3.2 制作时任务发送邮件 3. 调取API发送邮件3.1 编写文案脚本3.2 制作定…...

新增 git submodule 子模块

文章目录 1、基本语法2、添加子模块后的操作3、拉取带有submodule的仓库 git submodule add 是 Git 中用于将另一个 Git 仓库作为子模块添加到当前项目中的命令。 子模块允许你将一个 Git 仓库作为另一个 Git 仓库的子目录&#xff0c;同时保持它们各自的提交历史独立。 1、基…...

List优雅分组

一、前言 最近小永哥发现&#xff0c;在开发过程中&#xff0c;经常会遇到需要对list进行分组&#xff0c;就是假如有一个RecordTest对象集合&#xff0c;RecordTest对象都有一个type的属性&#xff0c;需要将这个集合按type属性进行分组&#xff0c;转换为一个以type为key&…...

Linux 使用 Docker 安装 Milvus的两种方式

一、使用 Docker Compose 运行 Milvus (Linux) 安装并启动 Milvus Milvus 在 Milvus 资源库中提供了 Docker Compose 配置文件。要使用 Docker Compose 安装 Milvus&#xff0c;只需运行 wget https://github.com/milvus-io/milvus/releases/download/v2.5.10/milvus-standa…...

AR眼镜+AI视频盒子+视频监控联网平台:消防救援的智能革命

在火灾现场&#xff0c;每一秒都关乎生死。传统消防救援方式面临信息滞后、指挥盲区、环境复杂等挑战。今天&#xff0c;一套融合AR智能眼镜AI视频分析盒子智能监控管理平台的"三位一体"解决方案&#xff0c;正在彻底改变消防救援的作业模式&#xff0c;为消防员装上…...

编程技能:字符串函数10,strchr

专栏导航 本节文章分别属于《Win32 学习笔记》和《MFC 学习笔记》两个专栏&#xff0c;故划分为两个专栏导航。读者可以自行选择前往哪个专栏。 &#xff08;一&#xff09;WIn32 专栏导航 上一篇&#xff1a;编程技能&#xff1a;字符串函数09&#xff0c;strncmp 回到目录…...

使用tunasync部署企业内部开源软件镜像站-Centos Stream 9

使用tunasync部署企业内部开源软件镜像站 tunasync 是清华大学 TUNA 镜像源目前使用的镜像方案&#xff0c;本文将介绍如何使用 tunasync 部署企业内部开源软件镜像站。 基于tunasync mirror-web nginx进行镜像站点搭建。 1. tunasync设计 tunasync架构如下&#xff1a; …...

c/c++的opencv像素级操作二值化

图像级操作&#xff1a;使用 C/C 进行二值化 在数字图像处理中&#xff0c;图像级操作 (Image-Level Operations) 是指直接在图像的像素级别上进行处理&#xff0c;以改变图像的视觉特性或提取有用信息。这些操作通常不依赖于图像的全局结构&#xff0c;而是关注每个像素及其邻…...

C++----Vector的模拟实现

上一节讲了string的模拟实现&#xff0c;string的出现时间比vector靠前&#xff0c;所以一些函数给的也比较冗余&#xff0c;而后来的vector、list等在此基础上做了优化。这节讲一讲vector的模拟实现&#xff0c;vector与模板具有联系&#xff0c;而string的底层就是vector的一…...

Mac redis下载和安装

目录 1、官网&#xff1a;https://redis.io/ 2、滑到最底下 3、下载资源 4、安装&#xff1a; 5、输入 sudo make test 进行编译测试 会提示 ​编辑 6、sudo make install 继续 7、输入 src/redis-server 启动服务器 8、输入 src/redis-cli 启动测试端 1、官网&#xff…...

[25-cv-05718]BSF律所代理潮流品牌KAWS公仔(商标+版权)

潮流品牌KAWS公仔 案件号&#xff1a;25-cv-05718 立案时间&#xff1a;2025年5月21日 原告&#xff1a;KAWS, INC. 代理律所&#xff1a;Boies Schiller Flexner LLP 原告介绍 原告是一家由美国街头艺术家Brian Donnelly创立的公司&#xff0c;成立于2002年2月25日&…...

【PhysUnits】9 取负重载(negation.rs)

一、源码 这段代码是类型级二进制数&#xff08;包括正数和负数&#xff09;的取反和取负操作。它使用了类型系统来表示二进制数&#xff0c;并通过特质&#xff08;trait&#xff09;和泛型来实现递归操作。 use super::basic::{B0, B1, Z0, N1}; use core::ops::Neg;// 反…...

深度思考、弹性实施,业务流程自动化的实践指南

随着市场环境愈发复杂化&#xff0c;各类型企业的业务步伐为了跟得上市场节奏也逐步变得紧张&#xff0c;似乎只有保持极强的竞争力、削减成本、提升抗压能力才能在市场洪流中博得一席之位。此刻企业需要制定更明智的解决方案&#xff0c;以更快、更准确地优化决策流程。与简单…...