RabbitMQ保证消息被成功发送和消费

一 : 在使用 RabbitMQ 作为消息队列时,保证消息被成功发送和消费是一个非常重要的问题。以下是一些关键点和最佳实践,以确保消息的可靠传输和处理。*
配置方式:

保证消息被成功发送
确认模式(Confirm Mode):生产者可以启用确认模式,确保消息成功到达交换机。
使用 channel.confirmSelect() 启用确认模式。
使用 channel.waitForConfirms() 或 channel.addConfirmListener() 来处理确认消息。
事务模式(Transaction Mode):生产者可以使用事务模式,确保消息成功到达队列。
使用 channel.txSelect() 开启事务,channel.txCommit() 提交事务,channel.txRollback() 回滚事务。
处理发送失败:实现重试机制,可以在发送失败时重试。
使用死信交换机(Dead Letter Exchange, DLX)来存储处理失败的消息。
保证消息被成功消费
手动确认(Manual Acknowledgment):消费者应该使用手动确认模式,确保消息被成功处理后再确认。
使用 channel.basicConsume(queue, false, consumer) 开启手动确认模式。
在消息处理成功后,调用 channel.basicAck(deliveryTag, false) 确认消息。
处理消费失败:实现消费失败的重试机制。
使用死信交换机(DLX)来存储处理失败的消息。
幂等性:确保消费者处理消息的幂等性,避免重复消费导致的问题。

二:通过记录消息到数据库中,采用定时任务轮询方式:

1 这是一个 Spring 组件,用于构建和发布返利消息事件。

//topic 字段从配置文件中获取,表示消息队列的 topic。
//buildEventMessage 方法用于构建 EventMessage 对象,包含随机生成的 ID、当前时间戳和数据。
//topic 方法返回消息队列的 topic。
//RebateMessage 是一个内部类,定义了返利消息的结构@Component
public class SendRebateMessageEvent extends BaseEvent<SendRebateMessageEvent.RebateMessage> {@Value("${spring.rabbitmq.topic.send_rebate}")private String topic;@Overridepublic EventMessage<RebateMessage> buildEventMessage(RebateMessage data) {return EventMessage.<SendRebateMessageEvent.RebateMessage>builder().id(RandomStringUtils.randomNumeric(11)).timestamp(new Date()).data(data).build();}@Overridepublic String topic() {return topic;}@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic static class RebateMessage {private String userId;private String rebateDesc;private String rebateType;private String rebateConfig;private String bizId;}
}@Data
public abstract class BaseEvent<T> {public abstract EventMessage<T> buildEventMessage(T data);public abstract String topic();@Data@Builder@AllArgsConstructor@NoArgsConstructorpublic static class EventMessage<T> {private String id;private Date timestamp;private T data;}}

2 生产者示例

//topic 字段从配置文件中获取,表示消息队列的 topic。
//listener 方法使用 @RabbitListener 注解监听指定队列的消息。
//消息到达后,解析消息内容,根据 rebateType 字段的不同,调用相应的服务方法处理消息。
//异常处理机制确保了消息处理的健壮性。@Component
public class RebateMessageCustomer {@Value("${spring.rabbitmq.topic.send_rebate}")private String topic;@Resourceprivate IRaffleActivityAccountQuotaService raffleActivityAccountQuotaService;@Resourceprivate ICreditAdjustService creditAdjustService;@RabbitListener(queuesToDeclare = @Queue(value = "${spring.rabbitmq.topic.send_rebate}"))public void listener(String message) {try {log.info("监听用户行为返利消息 topic: {} message: {}", topic, message);BaseEvent.EventMessage<SendRebateMessageEvent.RebateMessage> eventMessage = JSON.parseObject(message, new TypeReference<BaseEvent.EventMessage<SendRebateMessageEvent.RebateMessage>>() {}.getType());SendRebateMessageEvent.RebateMessage rebateMessage = eventMessage.getData();switch (rebateMessage.getRebateType()) {case "sku":SkuRechargeEntity skuRechargeEntity = new SkuRechargeEntity();skuRechargeEntity.setUserId(rebateMessage.getUserId());skuRechargeEntity.setSku(Long.valueOf(rebateMessage.getRebateConfig()));skuRechargeEntity.setOutBusinessNo(rebateMessage.getBizId());skuRechargeEntity.setOrderTradeType(OrderTradeTypeVO.rebate_no_pay_trade);raffleActivityAccountQuotaService.createOrder(skuRechargeEntity);break;case "integral":TradeEntity tradeEntity = new TradeEntity();tradeEntity.setUserId(rebateMessage.getUserId());tradeEntity.setTradeName(TradeNameVO.REBATE);tradeEntity.setTradeType(TradeTypeVO.FORWARD);tradeEntity.setAmount(new BigDecimal(rebateMessage.getRebateConfig()));tradeEntity.setOutBusinessNo(rebateMessage.getBizId());creditAdjustService.createOrder(tradeEntity);break;}} catch (AppException e) {if (ResponseCode.INDEX_DUP.getCode().equals(e.getCode())) {log.warn("监听用户行为返利消息,消费重复 topic: {} message: {}", topic, message, e);return;}throw e;} catch (Exception e) {log.error("监听用户行为返利消息,消费失败 topic: {} message: {}", topic, message, e);throw e;}}
}

3 消费者示例


//这个方法用于保存用户返利记录,并在事务中插入用户行为返利订单和任务对象。
//在事务外,同步发送 MQ 消息。
//发送消息时,调用 eventPublisher.publish 方法发布消息到指定的 topic。public void saveUserRebateRecord(String userId, List<BehaviorRebateAggregate> behaviorRebateAggregates) {try {dbRouter.doRouter(userId);transactionTemplate.execute(status -> {try {for (BehaviorRebateAggregate behaviorRebateAggregate : behaviorRebateAggregates) {BehaviorRebateOrderEntity behaviorRebateOrderEntity = behaviorRebateAggregate.getBehaviorRebateOrderEntity();UserBehaviorRebateOrder userBehaviorRebateOrder = new UserBehaviorRebateOrder();userBehaviorRebateOrder.setUserId(behaviorRebateOrderEntity.getUserId());userBehaviorRebateOrder.setOrderId(behaviorRebateOrderEntity.getOrderId());userBehaviorRebateOrder.setBehaviorType(behaviorRebateOrderEntity.getBehaviorType());userBehaviorRebateOrder.setRebateDesc(behaviorRebateOrderEntity.getRebateDesc());userBehaviorRebateOrder.setRebateType(behaviorRebateOrderEntity.getRebateType());userBehaviorRebateOrder.setRebateConfig(behaviorRebateOrderEntity.getRebateConfig());userBehaviorRebateOrder.setOutBusinessNo(behaviorRebateOrderEntity.getOutBusinessNo());userBehaviorRebateOrder.setBizId(behaviorRebateOrderEntity.getBizId());userBehaviorRebateOrderDao.insert(userBehaviorRebateOrder);TaskEntity taskEntity = behaviorRebateAggregate.getTaskEntity();Task task = new Task();task.setUserId(taskEntity.getUserId());task.setTopic(taskEntity.getTopic());task.setMessageId(taskEntity.getMessageId());task.setMessage(JSON.toJSONString(taskEntity.getMessage()));task.setState(taskEntity.getState().getCode());taskDao.insert(task);}return 1;} catch (DuplicateKeyException e) {status.setRollbackOnly();log.error("写入返利记录,唯一索引冲突 userId: {}", userId, e);throw new AppException(ResponseCode.INDEX_DUP.getCode(), ResponseCode.INDEX_DUP.getInfo());}});} finally {dbRouter.clear();}for (BehaviorRebateAggregate behaviorRebateAggregate : behaviorRebateAggregates) {TaskEntity taskEntity = behaviorRebateAggregate.getTaskEntity();Task task = new Task();task.setUserId(taskEntity.getUserId());task.setMessageId(taskEntity.getMessageId());try {eventPublisher.publish(taskEntity.getTopic(), taskEntity.getMessage());taskDao.updateTaskSendMessageCompleted(task);} catch (Exception e) {log.error("写入返利记录,发送MQ消息失败 userId: {} topic: {}", userId, task.getTopic());taskDao.updateTaskSendMessageFail(task);}}
}public List<String> createOrder(BehaviorEntity behaviorEntity) {// 1. 查询返利配置List<DailyBehaviorRebateVO> dailyBehaviorRebateVOS = behaviorRebateRepository.queryDailyBehaviorRebateConfig(behaviorEntity.getBehaviorTypeVO());if (null == dailyBehaviorRebateVOS || dailyBehaviorRebateVOS.isEmpty()) return new ArrayList<>();// 2. 构建聚合对象List<String> orderIds = new ArrayList<>();List<BehaviorRebateAggregate> behaviorRebateAggregates = new ArrayList<>();for (DailyBehaviorRebateVO dailyBehaviorRebateVO : dailyBehaviorRebateVOS) {// 拼装业务ID;用户ID_返利类型_外部透彻业务IDString bizId = behaviorEntity.getUserId() + Constants.UNDERLINE + dailyBehaviorRebateVO.getRebateType() + Constants.UNDERLINE + behaviorEntity.getOutBusinessNo();BehaviorRebateOrderEntity behaviorRebateOrderEntity = BehaviorRebateOrderEntity.builder().userId(behaviorEntity.getUserId()).orderId(RandomStringUtils.randomNumeric(12)).behaviorType(dailyBehaviorRebateVO.getBehaviorType()).rebateDesc(dailyBehaviorRebateVO.getRebateDesc()).rebateType(dailyBehaviorRebateVO.getRebateType()).rebateConfig(dailyBehaviorRebateVO.getRebateConfig()).outBusinessNo(behaviorEntity.getOutBusinessNo()).bizId(bizId).build();orderIds.add(behaviorRebateOrderEntity.getOrderId());// MQ 消息对象SendRebateMessageEvent.RebateMessage rebateMessage = SendRebateMessageEvent.RebateMessage.builder().userId(behaviorEntity.getUserId()).rebateType(dailyBehaviorRebateVO.getRebateType()).rebateConfig(dailyBehaviorRebateVO.getRebateConfig()).bizId(bizId).build();// 构建事件消息BaseEvent.EventMessage<SendRebateMessageEvent.RebateMessage> rebateMessageEventMessage = sendRebateMessageEvent.buildEventMessage(rebateMessage);// 组装任务对象TaskEntity taskEntity = new TaskEntity();taskEntity.setUserId(behaviorEntity.getUserId());taskEntity.setTopic(sendRebateMessageEvent.topic());taskEntity.setMessageId(rebateMessageEventMessage.getId());taskEntity.setMessage(rebateMessageEventMessage);taskEntity.setState(TaskStateVO.create);BehaviorRebateAggregate behaviorRebateAggregate = BehaviorRebateAggregate.builder().userId(behaviorEntity.getUserId()).behaviorRebateOrderEntity(behaviorRebateOrderEntity).taskEntity(taskEntity).build();behaviorRebateAggregates.add(behaviorRebateAggregate);}// 3. 存储聚合对象数据behaviorRebateRepository.saveUserRebateRecord(behaviorEntity.getUserId(), behaviorRebateAggregates);// 4. 返回订单ID集合return orderIds;}@Data
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class TaskEntity {/** 活动ID */private String userId;/** 消息主题 */private String topic;/** 消息编号 */private String messageId;/** 消息主体 */private BaseEvent.EventMessage<SendRebateMessageEvent.RebateMessage> message;/** 任务状态;create-创建、completed-完成、fail-失败 */private TaskStateVO state;}@Data
public class Task {/** 自增ID */private String id;/** 活动ID */private String userId;/** 消息主题 */private String topic;/** 消息编号 */private String messageId;/** 消息主体 */private String message;/** 任务状态;create-创建、completed-完成、fail-失败 */private String state;/** 创建时间 */private Date createTime;/** 更新时间 */private Date updateTime;}

4 定时任务示例

// @Scheduled(cron = "0/5 * * * * ?")
public void exec_db01() {try {// 设置库表dbRouter.setDBKey(1);dbRouter.setTBKey(0);// 查询未发送的任务List<TaskEntity> taskEntities = taskService.queryNoSendMessageTaskList();if (taskEntities.isEmpty()) return;// 发送MQ消息for (TaskEntity taskEntity : taskEntities) {try {taskService.sendMessage(taskEntity);taskService.updateTaskSendMessageCompleted(taskEntity.getUserId(), taskEntity.getMessageId());} catch (Exception e) {log.error("定时任务,发送MQ消息失败 userId: {} topic: {}", taskEntity.getUserId(), taskEntity.getTopic());taskService.updateTaskSendMessageFail(taskEntity.getUserId(), taskEntity.getMessageId());}}} catch (Exception e) {log.error("定时任务,扫描MQ任务表发送消息失败。", e);} finally {dbRouter.clear();}
}@Data
public class TaskEntity {/** 活动ID */private String userId;/** 消息主题 */private String topic;/** 消息编号 */private String messageId;/** 消息主体 */private String message;}

三: 串联流程

生产消息:saveUserRebateRecord 方法在事务中插入用户行为返利订单和任
务对象。
在事务外,调用 eventPublisher.publish 方法发布消息到指定的 
topic。
消息构建和发布:SendRebateMessageEvent 类构建 EventMessage 对象,包含随机
生成的 ID、当前时间戳和数据,并返回消息队列的 topic。
消费消息:RebateMessageCustomer 类监听指定队列的消息,解析消息内容,
根据 rebateType 字段的不同,调用相应的服务方法处理消息。
定时任务补偿:SendMessageTaskJob 类定时扫描数据库中的任务表,发送未发送的
消息到 MQ 队列,并更新任务状态。如果发送失败,记录错误日志并
更新任务状态为发送失败。

四:配置文件

spring:rabbitmq:addresses: ****port: ***username: **password: **listener:simple:prefetch: 1 # 每次投递n个消息,消费完在投递n个topic:send_rebate: send_rebate

五: 消费失败

消息发送:
生产者在发送消息时,会将消息的相关信息(如消息内容、发送状态等)记录到 task 表中。
如果消息发送成功,则更新 task 表中的状态为“已发送”。
如果消息发送失败,则更新 task 表中的状态为“发送失败”。
定时任务会扫描 task 表,查找状态为“发送失败”的消息,并重试发送。消息消费:
消费者在处理消息时,也会将消息的相关信息(如消息内容、处理状态等)记录到 task 表中。
如果消息处理成功,则更新 task 表中的状态为“已处理”。
如果消息处理失败,则更新 task 表中的状态为“处理失败”。
定时任务会扫描 task 表,查找状态为“处理失败”的消息,并重试处理。
通过这种方式,可以确保即使消息在发送或消费过程中出现失败,也能够通过重试机制最终成功发送或处理。

示例:

import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;@Service
public class MessageService {@Autowiredprivate RabbitTemplate rabbitTemplate;@Autowiredprivate TaskRepository taskRepository;@Transactionalpublic void sendMessage(String message) {try {rabbitTemplate.convertAndSend("send_rebate", message);TaskEntity task = new TaskEntity();task.setMessage(message);task.setStatus("SENT");taskRepository.save(task);} catch (Exception e) {TaskEntity task = new TaskEntity();task.setMessage(message);task.setStatus("SEND_FAILED");taskRepository.save(task);}}
}
import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;@Component
public class MessageConsumer {@Autowiredprivate TaskRepository taskRepository;@RabbitListener(queues = "${spring.rabbitmq.topic.send_rebate}")public void handleMessage(String message) {try {// 处理消息的逻辑System.out.println("Processing message: " + message);// 假设处理成功TaskEntity task = new TaskEntity();task.setMessage(message);task.setStatus("PROCESSED");taskRepository.save(task);} catch (Exception e) {// 处理失败的逻辑TaskEntity task = new TaskEntity();task.setMessage(message);task.setStatus("PROCESS_FAILED");taskRepository.save(task);}}
}
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;import java.util.List;@Component
public class RetryTaskJob {@Autowiredprivate TaskRepository taskRepository;@Autowiredprivate MessageService messageService;@Autowiredprivate MessageConsumer messageConsumer;@Scheduled(fixedRate = 60000) // 每分钟执行一次public void retryFailedMessages() {// 重试发送失败的消息List<TaskEntity> sendFailedTasks = taskRepository.findByStatus("SEND_FAILED");for (TaskEntity task : sendFailedTasks) {try {messageService.sendMessage(task.getMessage());task.setStatus("SENT");taskRepository.save(task);} catch (Exception e) {// 记录日志或进行其他处理}}// 重试处理失败的消息List<TaskEntity> processFailedTasks = taskRepository.findByStatus("PROCESS_FAILED");for (TaskEntity task : processFailedTasks) {try {messageConsumer.handleMessage(task.getMessage());task.setStatus("PROCESSED");taskRepository.save(task);} catch (Exception e) {// 记录日志或进行其他处理}}}
}

六: 防止重复消费

保证消息消费的幂等性是确保消息系统可靠性的重要一环。幂等性意
味着无论消息被处理一次还是多次,结果都是相同的。以下是一些常见的策略来保证消息消费的幂等性:唯一标识符:为每条消息生成一个唯一的标识符(如 UUID),并在
处理消息时检查该标识符是否已经被处理过。如果已经处理过,则忽略该消息。状态检查:在处理消息之前,检查系统的状态,确保该消息对应的操
作尚未执行。例如,如果消息是要更新某个资源,可以先检查该资源的状态,确保更新操作尚未执行。数据库唯一约束:在数据库中为消息处理结果创建唯一约束,确保相
同的消息不会被重复处理。幂等API设计:设计幂等的API,确保相同的请求多次执行不会产生不
同的结果。例如,使用“PUT”方法更新资源,而不是“POST”方法。

使用 Redis 来实现消息消费的幂等性是一个非常有效的方法。Redis 是一个高性能的内存数据库,适合用于存储临时状态信息。
1 消费者处理消息并记录到 Redis

import org.springframework.amqp.rabbit.annotation.RabbitListener;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;import java.util.concurrent.TimeUnit;@Component
public class MessageConsumer {@Autowiredprivate StringRedisTemplate redisTemplate;@RabbitListener(queues = "${spring.rabbitmq.topic.send_rebate}")public void handleMessage(String message) {// 假设消息中包含唯一标识符String messageId = extractMessageId(message);// 检查消息是否已经处理过或正在处理if (redisTemplate.hasKey(messageId)) {System.out.println("Message already processed or being processed: " + messageId);return;}// 将消息ID存入Redis,设置过期时间redisTemplate.opsForValue().set(messageId, "PROCESSING", 60, TimeUnit.SECONDS);try {// 处理消息的逻辑System.out.println("Processing message: " + message);// 假设处理成功redisTemplate.opsForValue().set(messageId, "PROCESSED", 60, TimeUnit.SECONDS);} catch (Exception e) {// 处理失败的逻辑redisTemplate.delete(messageId); // 删除Redis中的记录,以便可以重试}}private String extractMessageId(String message) {// 假设消息中包含唯一标识符,例如 JSON 格式中的 "id" 字段// 这里只是一个示例,实际实现可能需要解析消息内容return message.substring(0, 36); // 假设 UUID 长度为 36}
}
spring:redis:host: localhostport: 6379
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

通过上述方法和配置,您可以确保消息消费的幂等性。消费者在处理消息之前会检查消息的唯一标识符是否已经存在于 Redis 中,如果存在,则忽略该消息,从而避免重复处理。同时,通过设置过期时间,可以确保在处理过程中出现异常时,Redis 中的记录会被删除,从而允许消息重试。

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

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

相关文章

在SpringBoot使用AOP防止接口重复提交

前言 防止接口重复提交有跟多种方法&#xff0c;可以在前端做处理。同样在后端也能处理&#xff0c;而且后端的处理也有很多中方法。最先能想到的就是加锁&#xff0c;也可以直接在该接口的实现过程中进行处理&#xff08;可以参考防止数据重复提交的6种方法(超简单)&#xff…

动手学Avalonia:基于硅基流动构建一个文生图应用(一)

文生图 文生图&#xff0c;全称“文字生成图像”&#xff08;Text-to-Image&#xff09;&#xff0c;是一种AI技术&#xff0c;能够根据给定的文本描述生成相应的图像。这种技术利用深度学习模型&#xff0c;如生成对抗网络&#xff08;GANs&#xff09;或变换器&#xff08;T…

【Mac】Charles for Mac(HTTP协议抓包工具)及同类型软件介绍

软件介绍 Charles for Mac 是一款功能强大的网络调试工具&#xff0c;主要用于HTTP代理/HTTP监视器。以下是它的一些主要特点和功能&#xff1a; 1.HTTP代理&#xff1a;Charles 可以作为HTTP代理服务器&#xff0c;允许你查看客户端和服务器之间的所有HTTP和SSL/TLS通信。 …

金航标kinghelm宋仕强在介绍自己公司时说

金航标kinghelm宋仕强在介绍自己公司时说&#xff0c;金航标成立于2007年&#xff0c;成立地点在华强北雷圳大厦803室&#xff0c;后搬到华强北广业大厦24楼CD室&#xff0c;后搬迁到龙华展滔科技大厦C座C809和C817室&#xff0c;现在的办公地址为龙岗区坂田街道百瑞达大厦&…

WSL安装USB驱动

wsl用不了USB盘&#xff0c;需要安装驱动 1、安装windows驱动 https://github.com/dorssel/usbipd-win/releases 下载msi&#xff0c;并且安装 2、linux里面安装 sudo apt install linux-tools-5.4.0-77-generic hwdata sudo update-alternatives --install /usr/local/bin/usb…

PageDTO<T>,PageQuery,BeanUtils,CollUtils的封装

一、PageDTO<T> import com.baomidou.mybatisplus.extension.plugins.pagination.Page; import com.fasterxml.jackson.annotation.JsonIgnore; import com.tianji.common.utils.BeanUtils; import com.tianji.common.utils.CollUtils; import com.tianji.common.utils.…

C#中的MD5摘要算法与哈希算法

文章目录 一、哈希算法基础二、MD5 算法原理三、MD5摘要算法四、哈希算法五、C#实现示例MD5算法示例哈希算法示例字符串MD5值对比 六、总结 一、哈希算法基础 哈希算法是一种单向密码体制&#xff0c;它将任意长度的数据转换成固定长度的字符串。这种转换是不可逆的&#xff0…

IDEA中配置代理,解决Codearts Snap登陆不了的问题

问题描述&#xff1a;在mac电脑中的idea中安装了华为的codearts snap插件&#xff0c;一直登录不了&#xff0c;账号是没问题的&#xff0c;后来我怀疑是我的代理有问题&#xff0c;找到IDEA中的代理设置先是有这个问题“You have JVM property "https.proxyHost" se…

千呼新零售2.0分销商城视频介绍

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货、宠物等连锁店使用。 详细介绍请…

C语言 将两个字符串连接起来,不用strcat函数

编一个程序,将两个字符串连接起来,不要用strcat函数。 #include <stdio.h>void my_strcat(char *s1, const char *s2) {while (*s1) {s1;}while (*s2) {*s1 *s2;s1;s2;}*s1 \0; }int main() {char s1[100] "Hello, ";char s2[] "World!";my_str…

Android初学者书籍推荐

书单 1.《Android应用开发项目式教程》&#xff0c;机械工业出版社&#xff0c;2024年出版2.《第一行代码Android》第二版3.《第一行代码Android》第三版4.《疯狂Android讲义》第四版5.《Android移动应用基础教程&#xff08;Android Studio 第2版&#xff09;》 从学安卓到用安…

uniapp 打包成安卓APP预览base64pdf实现方法

下载PDF.js 问题描述 在uniapp中预览base64的PDF&#xff0c;可以使用web-view组件嵌入一个PDF.js的实例。以下是一个简单的示例&#xff1a; 解决方案&#xff1a; 1.在页面的.vue文件中添加web-view组件&#xff1a; <template><view style"width: 50%;&qu…

【机器学习】支持向量机与主成分分析在机器学习中的应用

文章目录 一、支持向量机概述什么是支持向量机&#xff1f;超平面和支持向量大边距直觉 二、数据预处理与可视化数据集的基本信息导入必要的库加载数据集数据概况数据可视化特征对的散点图矩阵类别分布条形图平均面积与平均光滑度的散点图变量之间的相关性热图 三、模型训练&am…

JS【详解】类 class ( ES6 新增语法 )

本质上&#xff0c;类只是一种特殊的函数。 console.log(typeof 某类); //"function"声明类 class 方式 1 – 类声明 class Car {constructor(model, year) {this.model model;this.year year;} }方式 2 – 类表达式 匿名式 const Car class {constructor(mod…

在conda的环境中安装Jupyter及其他软件包

Pytorch版本、安装和检验 大多数软件包都是随Anaconda安装的&#xff0c;也可以根据需要手动安装一些其他软件包。 目录 创建虚拟环境 进入虚拟环境 安装Jupyter notebook 安装matplotlib 安装 pandas 创建虚拟环境 基于conda包的环境创建、激活、管理与删除http://t.cs…

podman 替代 docker ? centos Stream 10 已经弃用docker,开始用podman了!

&#x1f468;‍&#x1f393;博主简介 &#x1f3c5;CSDN博客专家   &#x1f3c5;云计算领域优质创作者   &#x1f3c5;华为云开发者社区专家博主   &#x1f3c5;阿里云开发者社区专家博主 &#x1f48a;交流社区&#xff1a;运维交流社区 欢迎大家的加入&#xff01…

浅谈React

forwardRef和useImperativeHandle的联动使用 import React, { useImperativeHandle, useRef } from "react" import { forwardRef } from "react"const CustomInput forwardRef((props, ref) > {const inputRef useRef<HTMLInputElement>(null…

Java中锁的分类、原理、使用场景、注意事项、优缺点等详解

Java开发中&#xff0c;锁是保证多线程安全的重要手段。Java提供了多种类型的锁来满足不同的同步需求。在这篇文章中&#xff0c;我将为您介绍以下几种常见的锁类型&#xff1a; 偏向锁/轻量级锁/重量级锁 偏向锁&#xff1a;当一个线程获取一个对象的锁时&#xff0c;如果发现…

解决MCM功率电源模块EMC的关键

对MCM功率电源而言&#xff0c;由于其工作在几百kHz的高频开关状态&#xff0c;故易成为干扰源。电磁兼容性EMC&#xff08;Electro Magnetic Compatibility&#xff09;&#xff0c;是指设备或系统在其电磁环境中符合要求运行并不对其环境中的任何设备产生无法忍受的电磁干扰的…

react父调用子的方法,子调用父的方法

父调用子的方法 // 子组件 import React, { useRef, useEffect } from react;const ChildComponent ({ childMethodRef }) > {const childMethod useRef(null);useEffect(() > {childMethodRef.current childMethod;}, []);const someMethod () > {console.log(子…