基于 FastExcel 与消息队列高效生成及导入机构用户数据

🎯 本文档详细介绍了开发机构用户数据导入功能的必要性及实现方法,如针对教育机构如学校场景下提高用户体验和管理效率的需求。文中首先分析了直接对接学生管理系统与平台对接的优势,包括减少人工审核成本、提高身份验证准确性等。接着介绍了FastExcel作为处理Excel文件的高效工具及其在用户数据导入导出中的应用,并展示了如何利用JavaFaker生成测试数据,以及通过消息队列异步处理Excel数据导入以保证效率和稳定性。最后,提供了SQL批量插入语句示例,用于处理用户数据的高效入库。

需求说明

为什么需要开发机构用户数据导入功能?平台不是本身就存在注册功能吗,为啥要多此一举?

场快订采用SaaS模式开发,支持多租户使用,不同租户对其资源的管理方式可能是不同的。

例如,在部分学校中,其内部场馆的预订服务可能仅对本校的教师、学生及工作人员开放。因此,为了准确识别和区分用户身份,确保只有特定机构内的成员能够访问相应的资源和服务,我们需要引入基于机构ID的身份绑定功能。

尽管平台本身已经具备注册功能,但对于类似学校等机构而言,直接利用其现有的学生管理系统与我们的平台进行对接,将极大地提高效率并减少管理成本。如果要求所有学生首先通过上传学生证等方式来完成注册审核流程,不仅会面临伪造证件的风险(如通过P图方式绕过审核),还会因需要大量人工审核而消耗不必要的资源。此外,这种做法对于学生来说也不够友好,增加了他们使用平台的复杂度。

相比之下,如果能够实现从学校的学生管理系统直接导入数据的功能,不仅可以避免上述问题,还能简化学生的登录过程——他们只需使用学号等已有的信息即可快速登录平台,享受便捷的服务。这种方式既提高了验证学生身份的准确性,也提升了整体用户体验,实现了双赢的局面。因此,开发这一功能具有显著的实际意义和应用价值。

FastExcel简介

POI 是 Apache 软件基金会下的一个项目,提供了用于操作各种文档格式的Java API,包括Microsoft Office中的Word、Excel和PowerPoint等格式。特别是对于Excel文件的操作,Apache POI提供了一套完整的解决方案,允许开发者读取、创建和修改Excel文件。然而,随着数据量的增长以及对处理效率要求的提高,使用POI操作Excel文件时遇到了性能瓶颈,尤其是在处理大文件时,内存消耗过大导致效率低下,甚至导致内存溢出。

在这种背景下,EasyExcel应运而生。它是由阿里巴巴开源的一个基于Java的数据处理库,专门用来简化Excel文件的读写操作。EasyExcel采用了不同于POI的实现方式,通过按行读取Excel文件而不是一次性加载整个文件到内存中,大大降低了内存占用,提高了处理效率。此外,EasyExcel还支持多种便捷的功能,如自定义转换器、注解式编程模型等,使得开发者能够更高效地进行Excel文件的操作。

EasyExcel的强大之处主要体现在以下几个方面:

  1. 低内存消耗:EasyExcel采用流式读取Excel文件的方式,避免了将整个文件加载到内存中,极大地减少了内存占用。
  2. 高效率:由于其优化的内部机制,无论是读取还是写入Excel文件,都比传统的POI方式更快。
  3. 易用性:提供了简洁明了的API接口,并支持通过注解配置Excel与Java对象之间的映射关系,极大地方便了开发者的使用。
  4. 灵活性:支持自定义转换器,可以根据需要灵活处理不同类型的数据转换问题。
  5. 扩展性:易于集成到现有的工程中,并且支持进一步的定制化开发,满足不同场景下的需求。

然而,这么优秀的项目却停止维护了

在这里插入图片描述

但是问题不大,EasyExcel 的原作者开了新的项目 FastExcel ,原本使用了 EasyExcel 的项目,可以平滑过渡到这个项目中,只需要修改一下 Maven 依赖导入和导包路径,即可接着使用。为啥使用 FastExcel ?

  • 原作者通过对底层算法的优化和内存管理的改进,让 FastExcel 的性能更高。
  • 相较于 EasyExcel ,FastExcel 还将持续更新,未来将提供更多的功能。
  • 此外,FastExcel 免费开源,真心感谢大佬造福人类。

感兴趣的朋友可以去关注大佬的微信公众号

在这里插入图片描述

FastExcel项目说明:https://mp.weixin.qq.com/s/XMXLEFaTjaU7QVnSYRFC_A

项目开源仓库地址:https://github.com/CodePhiliaX/fastexcel

用户数据生成

使用 Faker 库虚构数据

Faker 是一个方便的Java库,用于生成各种虚构数据,如姓名、地址、公司信息等,非常适合测试和开发时填充数据库或模拟真实数据场景。通过简单的API调用,它能提供丰富多样的数据类型,并支持多语言和地区设置,帮助开发者高效地创建贴近实际的测试案例,而无需手动编写虚假数据。

依赖

<!-- javafaker 虚构数据 -->
<dependency><groupId>com.github.javafaker</groupId><artifactId>javafaker</artifactId><version>1.0.2</version>
</dependency>

使用

使用方式非常简单,new一个 faker 对象出来之后,直接调用相关方法即可

Faker faker = new Faker(Locale.CHINA);
faker.number().digits(10) 

FastExcel 写出 excel 表格

依赖

<dependency><groupId>cn.idev.excel</groupId><artifactId>fastexcel</artifactId><version>1.0.0</version>
</dependency>

实现

首先声明一个实体类,来接收 faker 生成的数据,同时这个实体类也是 FastExcel 的输入

import cn.idev.excel.annotation.ExcelProperty;
import cn.idev.excel.annotation.write.style.ColumnWidth;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;/*** @Author dam* @create 2025/1/12 15:14*/
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class ExcelUserData {// 设置相关列的宽度@ColumnWidth(30)// 设置相关列的名称@ExcelProperty("用户名")private String userName;@ExcelProperty("昵称")private String nickName;@ColumnWidth(20)@ExcelProperty("手机号")private String phoneNumber;@ColumnWidth(30)@ExcelProperty("邮箱")private String email;@ColumnWidth(10)@ExcelProperty("性别")private int gender;@ColumnWidth(20)@ExcelProperty("密码")private String password;
}

生成数据,并导出 excel 表格

package com.vrs;import cn.hutool.core.io.FileUtil;
import cn.idev.excel.EasyExcel;
import cn.idev.excel.util.ListUtils;
import com.github.javafaker.Faker;
import com.vrs.common.entity.ExcelUserData;
import org.junit.Test;import java.io.File;
import java.nio.file.Paths;
import java.util.List;
import java.util.Locale;
import java.util.Random;/*** 模拟用户数据生成** @Author dam* @create 2025/1/12 15:06*/
public class UserDataGenerateTest {/*** 用户数量*/private final int userNum = 10000;private final Faker faker = new Faker(Locale.CHINA);/*** excel地址*/private final String excelPath = Paths.get("").toAbsolutePath().getParent().getParent() + File.separator + "tmp";@Testpublic void generate() {if (!FileUtil.exist(excelPath)) {FileUtil.mkdir(excelPath);}// 数据生成List<ExcelUserData> list = ListUtils.newArrayList();for (int i = 0; i < userNum; i++) {ExcelUserData data = ExcelUserData.builder()// 随机生成10位数字,并拼接成邮箱.email(faker.number().digits(10) + "@qq.com")// 生成一个随机的电话号码.phoneNumber(faker.phoneNumber().cellPhone())// 生成一个随机的用户名,使用 faker 库的 regexify 方法生成 3 到 10 个字母组成的字符串.userName(faker.regexify("[a-zA-Z]{3,10}"))// 生成一个随机的昵称,使用 faker 库的 name 方法生成一个名字作为昵称.nickName(faker.name().firstName())// 示例性别选项.gender(faker.options().option(0, 1))// 生成随机密码.password(faker.internet().password()).build();list.add(data);}// 输出ExcelString fileName = excelPath + File.separator + "机构用户生成.xlsx";EasyExcel.write(fileName, ExcelUserData.class).sheet("用户表").doWrite(list);}
}

不如得感叹,这也太方便了,一句代码就完成了导出

EasyExcel.write(fileName, ExcelUserGenerateData.class).sheet("用户表").doWrite(list);

导出之后的表格如下

在这里插入图片描述

用户数据导入

FastExcel 读入 excel 表格

监听器

监听器需要继承 AnalysisEventListener ,泛型指定为 ExcelUserData 之后,读取的时候会将每一行数据转化为 ExcelUserData 对象

import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import com.vrs.common.entity.ExcelUserData;
import lombok.Getter;/*** @Author dam* @create 2025/1/12 17:05*/
public class UserDataRowListener extends AnalysisEventListener<ExcelUserData> {/*** 处理每一行数据* @param excelUserData* @param analysisContext*/@Overridepublic void invoke(ExcelUserData excelUserData, AnalysisContext analysisContext) {}/*** 所有数据解析完成之后的操作* @param analysisContext*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {}
}

进行读取

UserDataRowListener listener = new UserDataRowListener();
EasyExcel.read(importUserExcelMqDTO.getExcelPath(), ExcelUserData.class, listener).sheet().doRead();

Controller

/*** 上传excel表,并解析导入机构用户数据*/
@Operation(summary = "机构数据excel导入")
@PostMapping("/importUserExcel")
public Result importUserExcel(MultipartFile file) throws Exception {userService.importUserExcel(file);return Results.success();
}

Service

注意,为了提高接口的效率,使用消息队列异步解析 excel

@Override
public void importUserExcel(MultipartFile file) {/// 数据校验// 校验用户是否有绑定机构Long organizationId = UserContext.getOrganizationId();if (organizationId == null) {throw new ClientException(BaseErrorCode.USER_NOT_SET_ORGANIZATION_ERROR);}// 校验用户权限,是否为机构管理员Integer userType = UserContext.getUserType();UserTypeConstant.validateInstituteManager(userType);// 文件类型校验String absPath;String name = file.getOriginalFilename();if (!name.contains(".")) {// --if-- 如果图片没有正常后缀throw new ClientException(BaseErrorCode.NO_SUFFIX_ERROR);} else if (name.endsWith(".xlsx")) {absPath = EXCEL_TEMP_PATH + File.separator + UUID.randomUUID() + ".xlsx";} else if (name.endsWith(".xls")) {absPath = EXCEL_TEMP_PATH + File.separator + UUID.randomUUID() + ".xls";} else {throw new ClientException(BaseErrorCode.EXCEL_TYPE_ERROR);}/// 暂存文件到服务器本地// 如果不存在目录,创建目录if (!FileUtil.exist(EXCEL_TEMP_PATH)) {FileUtil.mkdir(EXCEL_TEMP_PATH);}// 将输入流中的数据复制到目标文件中try (InputStream is = file.getInputStream()) {File targetFile = new File(absPath);// 将输入流中的数据复制到目标文件中java.nio.file.Files.copy(is, targetFile.toPath(), java.nio.file.StandardCopyOption.REPLACE_EXISTING);} catch (IOException e) {e.printStackTrace();throw new ServiceException(e.getMessage());}/// 发送消息,执行excel数据解析并将用户数据导入数据库SendResult sendResult = importUserExcelProducer.sendMessage(ImportUserExcelMqDTO.builder().organizationId(UserContext.getOrganizationId()).excelPath(absPath).build());if (!sendResult.getSendStatus().equals(SendStatus.SEND_OK)) {log.error("消息发送失败: " + sendResult.getSendStatus());throw new ServiceException(BaseErrorCode.MQ_SEND_ERROR);}// todo 记录导入任务到数据库中
}

【excel读取与数据导入】

/*** 正式执行excel数据解析与导入** @param importUserExcelMqDTO*/
@Override
public void handleImportUserExcel(ImportUserExcelMqDTO importUserExcelMqDTO) {OrganizationDO organizationDO = organizationService.getById(importUserExcelMqDTO.getOrganizationId());UserDataRowListener listener = new UserDataRowListener(this, importUserExcelMqDTO.getOrganizationId(), organizationDO.getMark());EasyExcel.read(importUserExcelMqDTO.getExcelPath(), ExcelUserData.class, listener).sheet().doRead();// 删除暂存的 excel 文件FileUtil.del(importUserExcelMqDTO.getExcelPath());
} 

【读取行监听器】

注意:在解析导入用户数据的时候,需要保证导入的效率,又要保证数据尽量都插入成功,如果有的数据插入不成功,需要向管理员反馈,哪些数据导入失败。

为什么会出现导入失败?

  • username冲突:有可能用户一开始已经将部分用户导入了平台中,后面机构有了新用户,例如学校每年有新学生,此时再导入,会出现 username 重复插入的现象。为了处理这个情况,本文通过使用INSERT IGNORE语句来处理,即如果想数据库中插入一批数据,对于之前还没有插入过的数据,正常执行插入。对于已经插入过的数据,则跳过插入,因为这些用户可能已经使用了平台一段时间
  • 部分字段缺失:可能存在部分数据条目,如用户名等关键字段缺失
  • 数据类型不匹配:如字符串填入到整型字段中
  • 违反非空约束:部分必填的字段为空
  • 数据库连接超时
  • 数据库性能负载过高
  • 网络中断
  • 内存、磁盘资源不足
  • 数据库服务异常

这段代码的作用是读取 Excel 文件里的用户数据,然后把这些数据存到数据库里。为了提高效率,它会先尝试一批一批地插入数据(比如每次插 1000 条)。如果某批数据插入失败了,它会把这批数据拆成更小的批次(比如每次插 250 条)再试一次。如果拆到最小批次(比如 50 条)还是失败,就改成一条一条地插入。最后,如果还有插不进去的数据,就把这些失败的数据保存到一个新的 Excel 文件里,方便以后查看和处理。整个过程既保证了速度,又确保了数据不会丢失。

package com.vrs.service.excel;import cn.idev.excel.EasyExcel;
import cn.idev.excel.context.AnalysisContext;
import cn.idev.excel.event.AnalysisEventListener;
import com.vrs.common.entity.ExcelUserData;
import com.vrs.domain.entity.UserDO;
import com.vrs.service.UserService;
import com.vrs.utils.SnowflakeIdUtil;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;import java.io.File;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.UUID;/*** @Author dam* @create 2025/1/12 17:05*/
@Slf4j
public class UserDataRowListener extends AnalysisEventListener<ExcelUserData> {private UserService userService;private Long organizationId;private String organizationMark;public UserDataRowListener(UserService userService, Long organizationId, String organizationMark) {this.userService = userService;this.organizationId = organizationId;this.organizationMark = organizationMark;}/*** excel表中的数据行数*/@Getterprivate int rowCount = 0;/*** 一批的数据量*/private final int BATCH_SIZE = 1000;/*** 最小批次大小*/private final int MIN_BATCH_SIZE = 50;public static final String EXCEL_TEMP_PATH = System.getProperty("user.dir") + File.separator + "tmp" + File.separator + "excel";/*** 数据插入缓冲区,满了就存储到数据库中*/private List<UserDO> userDOBuffer = new ArrayList<>();/*** 记录插入失败的数据*/private List<UserDO> failedData = new ArrayList<>();/*** 处理每一行数据** @param excelUserData* @param analysisContext*/@Overridepublic void invoke(ExcelUserData excelUserData, AnalysisContext analysisContext) {UserDO userDO = new UserDO();BeanUtils.copyProperties(excelUserData, userDO);userDO.setOrganizationId(organizationId);userDO.setUserName(organizationMark + "_" + userDO.getUserName());userDO.setId(SnowflakeIdUtil.nextId());userDO.setCreateTime(new Date());userDOBuffer.add(userDO);if (userDOBuffer.size() >= BATCH_SIZE) {processBatch(userDOBuffer, BATCH_SIZE);userDOBuffer.clear();}}/*** 所有数据解析完成之后的操作** @param analysisContext*/@Overridepublic void doAfterAllAnalysed(AnalysisContext analysisContext) {if (userDOBuffer.size() > 0) {processBatch(userDOBuffer, BATCH_SIZE);userDOBuffer.clear();}log.info("-------------------------- 机构用户全部导入完毕 ----------------------------");if (failedData.size() > 0) {String fileName = EXCEL_TEMP_PATH + File.separator + "fail " + UUID.randomUUID() + ".xlsx";// todo 如果错误的数据很多的话,之类需要优化,否则占用内存较高EasyExcel.write(fileName, ExcelUserData.class).sheet("导入失败用户").doWrite(failedData);// todo 导出excel之后,将excel请求路径存在到数据库中,供后续被管理员下载}// todo 修改数据库中的导入任务状态为成功或部分失败}/*** 处理批次数据** @param batch     当前批次数据* @param batchSize 当前批次大小*/private void processBatch(List<UserDO> batch, int batchSize) {try {// 尝试批量插入userService.saveBatchIgnore(batch);} catch (Exception e) {log.error("批量插入失败,当前批次大小:{},尝试拆分批次", batchSize, e);if (batchSize > MIN_BATCH_SIZE) {// 拆分批次为更小的批次int newBatchSize = batchSize / 4;List<List<UserDO>> smallerBatches = splitBatch(batch, newBatchSize);for (List<UserDO> smallerBatch : smallerBatches) {processBatch(smallerBatch, newBatchSize); // 递归处理更小的批次}} else {// 如果批次已经缩小到最小批次,逐条插入processSingleBatch(batch);}}}/*** 将批次拆分为更小的批次** @param batch        原始批次* @param newBatchSize 新批次大小* @return 拆分后的批次列表*/private List<List<UserDO>> splitBatch(List<UserDO> batch, int newBatchSize) {List<List<UserDO>> smallerBatches = new ArrayList<>();for (int i = 0; i < batch.size(); i += newBatchSize) {int end = Math.min(i + newBatchSize, batch.size());smallerBatches.add(batch.subList(i, end));}return smallerBatches;}/*** 逐条插入数据** @param batch 当前批次数据*/private void processSingleBatch(List<UserDO> batch) {for (UserDO userDO : batch) {try {// 逐条插入userService.save(userDO);} catch (Exception e) {log.error("单条插入失败,失败数据:{}", userDO, e);// 记录失败数据failedData.add(userDO);}}}}

MQ

【生产者】

import cn.hutool.core.util.StrUtil;
import com.vrs.constant.RocketMqConstant;
import com.vrs.domain.dto.mq.ImportUserExcelMqDTO;
import com.vrs.templateMethod.AbstractCommonSendProduceTemplate;
import com.vrs.templateMethod.BaseSendExtendDTO;
import com.vrs.templateMethod.MessageWrapper;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.common.message.MessageConst;
import org.springframework.messaging.Message;
import org.springframework.messaging.support.MessageBuilder;
import org.springframework.stereotype.Component;import java.util.UUID;/*** 机构用户数据导入 生产者** @Author dam* @create 2024/9/20 16:00*/
@Slf4j
@Component
public class ImportUserExcelProducer extends AbstractCommonSendProduceTemplate<ImportUserExcelMqDTO> {@Overrideprotected BaseSendExtendDTO buildBaseSendExtendParam(ImportUserExcelMqDTO messageSendEvent) {return BaseSendExtendDTO.builder().eventName("机构用户数据导入").keys(String.valueOf(messageSendEvent.getOrganizationId())).topic(RocketMqConstant.ADMIN_TOPIC).tag(RocketMqConstant.IMPORT_USER_EXCEL_TAG).sentTimeout(2000L).build();}@Overrideprotected Message<?> buildMessage(ImportUserExcelMqDTO messageSendEvent, BaseSendExtendDTO requestParam) {String keys = StrUtil.isEmpty(requestParam.getKeys()) ? UUID.randomUUID().toString() : requestParam.getKeys();return MessageBuilder.withPayload(new MessageWrapper(keys, messageSendEvent)).setHeader(MessageConst.PROPERTY_KEYS, keys).setHeader(MessageConst.PROPERTY_TAGS, requestParam.getTag()).build();}
}

【消费者】

package com.vrs.rocketMq.listener;import com.vrs.annotation.Idempotent;
import com.vrs.constant.RocketMqConstant;
import com.vrs.domain.dto.mq.ImportUserExcelMqDTO;
import com.vrs.enums.IdempotentSceneEnum;
import com.vrs.service.UserService;
import com.vrs.templateMethod.MessageWrapper;
import lombok.RequiredArgsConstructor;
import lombok.SneakyThrows;
import lombok.extern.slf4j.Slf4j;
import org.apache.rocketmq.spring.annotation.MessageModel;
import org.apache.rocketmq.spring.annotation.RocketMQMessageListener;
import org.apache.rocketmq.spring.annotation.SelectorType;
import org.apache.rocketmq.spring.core.RocketMQListener;
import org.springframework.stereotype.Component;/*** @Author dam* @create 2024/9/20 21:30*/
@Slf4j(topic = RocketMqConstant.VENUE_TOPIC)
@Component
@RocketMQMessageListener(topic = RocketMqConstant.ADMIN_TOPIC,consumerGroup = RocketMqConstant.ADMIN_CONSUMER_GROUP + "-" + RocketMqConstant.IMPORT_USER_EXCEL_TAG,messageModel = MessageModel.CLUSTERING,// 监听tagselectorType = SelectorType.TAG,selectorExpression = RocketMqConstant.IMPORT_USER_EXCEL_TAG
)
@RequiredArgsConstructor
public class ImportUserExcelListener implements RocketMQListener<MessageWrapper<ImportUserExcelMqDTO>> {private final UserService userService;/*** 消费消息的方法* 方法报错就会拒收消息** @param messageWrapper 消息内容,类型和上面的泛型一致。如果泛型指定了固定的类型,消息体就是我们的参数*/@Idempotent(uniqueKeyPrefix = "import_user_excel:",key = "#messageWrapper.getMessage().getOrganizationId()+''",scene = IdempotentSceneEnum.MQ,keyTimeout = 3600L)@SneakyThrows@Overridepublic void onMessage(MessageWrapper<ImportUserExcelMqDTO> messageWrapper) {// 开头打印日志,平常可 Debug 看任务参数,线上可报平安(比如消息是否消费,重新投递时获取参数等)log.info("[消费者] 机构用户数据导入,机构ID:{}", messageWrapper.getMessage().getOrganizationId());userService.handleImportUserExcel(messageWrapper.getMessage());}
}

SQL

这段SQL是一个用于批量插入数据的语句,具体来说是使用INSERT IGNORE语法向名为user的表中插入多条记录。如果遇到主键冲突或者唯一键冲突,它不会插入那条特定的记录,但会继续尝试插入其他记录,而不是直接中断整个操作。

<insert id="saveBatchIgnore">INSERT IGNORE INTO user (id, user_name, nick_name, user_type, email, phone_number, gender, avatar,avatar_type, password, status, login_ip, login_date, point, organization_id,create_time, update_time) VALUES<foreach collection="userDOBatch" item="userDO" separator=",">(#{userDO.id}, #{userDO.userName}, #{userDO.nickName}, #{userDO.userType},#{userDO.email}, #{userDO.phoneNumber}, #{userDO.gender}, #{userDO.avatar},#{userDO.avatarType}, #{userDO.password}, #{userDO.status}, #{userDO.loginIp},#{userDO.loginDate}, #{userDO.point}, #{userDO.organizationId},#{userDO.createTime}, #{userDO.updateTime})</foreach>
</insert>

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

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

相关文章

校园跑腿小程序---轮播图,导航栏开发

hello hello~ &#xff0c;这里是 code袁~&#x1f496;&#x1f496; &#xff0c;欢迎大家点赞&#x1f973;&#x1f973;关注&#x1f4a5;&#x1f4a5;收藏&#x1f339;&#x1f339;&#x1f339; &#x1f981;作者简介&#xff1a;一名喜欢分享和记录学习的在校大学生…

前端练习题

图片&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html> <head><meta charset"UTF-8"><title>用户信息页面</title><style>body {font-family: Arial, sans-serif;margin: 20px;}.user-info {display: flex;align-it…

AllData是怎么样的一款数据中台产品?

&#x1f525;&#x1f525; AllData大数据产品是可定义数据中台&#xff0c;以数据平台为底座&#xff0c;以数据中台为桥梁&#xff0c;以机器学习平台为中层框架&#xff0c;以大模型应用为上游产品&#xff0c;提供全链路数字化解决方案。 ✨奥零数据科技官网&#xff1a;…

一学就废|Python基础碎片,OS模块

Python 中的操作系统模块提供了与操作系统交互的功能。操作系统属于 Python 的标准实用程序模块。该模块提供了一种使用依赖于操作系统的功能的可移植方式。os和os. path模块包括许多与文件系统交互的函数。 Python-OS 模块函数 我们将讨论 Python os 模块的一些重要功能&…

2.Numpy练习(1)

一.练习一&#xff1a; 1.打印当前numpy版本&#xff1a; 2.构造一个全零的矩阵&#xff0c;并打印其占用内存大小&#xff1a; 3.打印一个函数的帮助文档&#xff0c;比如numpy.add&#xff1a; 4.创建一个10~49数组&#xff0c;并将其倒序排列: 5.找到一个数组中不为0的索引…

Ubuntu Server挂载AWS S3成一个本地文件夹

2023年&#xff0c;AWS出了个mountpoint的工具&#xff1a; https://github.com/awslabs/mountpoint-s3 如下是另外一种方式&#xff0c;通过s3fs-fuse 这个工具 sudo apt-get install automake autotools-dev \fuse g git libcurl4-gnutls-dev libfuse-dev \libssl-dev libx…

CSS3的aria-hidden学习

前言 aria-hidden 属性可用于隐藏非交互内容&#xff0c;使其在无障碍 API 中不可见。即当aria-hidden"true" 添加到一个元素会将该元素及其所有子元素从无障碍树中移除&#xff0c;这可以通过隐藏来改善辅助技术用户的体验&#xff1a; 纯装饰性内容&#xff0c;如…

nvm use使用nodejs版本时报错

文章目录 报错原因分析解决方法 报错 nvm use报错出现乱码&#xff1a; 比如nvm use 22.12.0&#xff0c;出现下面报错&#xff1a; exit status 1: ‘D:\Program’ &#xfffd;&#xfffd;&#xfffd;&#xfffd;&#xfffd;ڲ&#xfffd;&#xfffd;&#xfffd;&…

C++中线程同步与互斥的4种方式介绍、对比、场景举例

在C中&#xff0c;当两个或更多的线程需要访问共享数据时&#xff0c;就会出现线程安全问题。这是因为&#xff0c;如果没有适当的同步机制&#xff0c;一个线程可能在另一个线程还没有完成对数据的修改就开始访问数据&#xff0c;这将导致数据的不一致性和程序的不可预测性。为…

1、docker概念和基本使用命令

docker概念 微服务&#xff1a;不再是以完整的物理机为基础的服务软件&#xff0c;而是借助于宿主机的性能。以小量的形式&#xff0c;单独部署的应用。 docker&#xff1a;是一个开源的应用容器引擎&#xff0c;基于go语言开发的&#xff0c;使用时apache2.0的协议。docker是…

信息安全、网络安全和数据安全的区别和联系

信息安全、网络安全和数据安全是信息安全领域的三大支柱&#xff0c;它们之间既存在区别又相互联系。以下是对这三者的详细比较&#xff1a; 一.区别 1.信息安全 定义 信息安全是指为数据处理系统建立和采用的技术和管理的安全保护&#xff0c;保护计算机硬件、软件和数据不…

Linux网络编程5——多路IO转接

一.TCP状态时序理解 1.TCP状态理解 **CLOSED&#xff1a;**表示初始状态。 **LISTEN&#xff1a;**该状态表示服务器端的某个SOCKET处于监听状态&#xff0c;可以接受连接。 **SYN_SENT&#xff1a;**这个状态与SYN_RCVD遥相呼应&#xff0c;当客户端SOCKET执行CONNECT连接时…

【Linux网络编程】数据链路层 | MAC帧 | ARP协议

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站 &#x1f308;个人主页&#xff1a; 南桥几晴秋 &#x1f308;C专栏&#xff1a; 南桥谈C &#x1f308;C语言专栏&#xff1a; C语言学习系…

React Fiber框架中的Render渲染阶段——workLoop(performUnitOfWork【beginWork与completeWork】)

触发渲染过程——renderRoot renderRoot 是一个函数&#xff0c;用于触发渲染工作。它通常会调用并递归地执行一系列的渲染任务&#xff0c;直到完成整个更新过程。这个过程包括执行 Fiber 树中的 beginWork 和 completeWork&#xff0c;以及渲染新状态或 DOM。 function ren…

STM32裸机开发转FreeRTOS教程

目录 1. 简介2. RTOS设置&#xff08;1&#xff09;分配内存&#xff08;2&#xff09;查看任务剩余空间&#xff08;3&#xff09;使用osDelay 3. 队列的使用&#xff08;1&#xff09;创建队列&#xff08;1&#xff09;直接传值和指针传值&#xff08;2&#xff09;发送/接收…

Elasticsearch快速入门

Elasticsearch是由elastic公司开发的一套搜索引擎技术&#xff0c;它是elastic技术栈中的一部分,提供核心的数据存储、搜索、分析功能 elasticsearch之所以有如此高性能的搜索表现&#xff0c;正是得益于底层的倒排索引技术。那么什么是倒排索引呢&#xff1f; Elasticsearch…

新版AndroidStudio通过系统快捷创建带BottomNavigationView的项目踩坑记录

选择上面这个玩意创建的项目 坑点1 &#xff1a;配置的写法和不一样了 镜像的写法&#xff1a; 新的settings.gradle.kts中配置镜像的代码&#xff1a; pluginManagement {repositories {mavenCentral()google {content {includeGroupByRegex("com\\.android.*")…

Unity 自定义批量打包工具

打包配置项 using UnityEngine; using System.Collections.Generic;namespace MYTOOL.Build {/// <summary>/// 批量打包配置文件/// </summary>[CreateAssetMenu]public class BatchBuildProfile : ScriptableObject{public List<BuildTask> tasks new Li…

【JVM-2.3】深入解析JVisualVM:Java性能监控与调优利器

在Java应用的开发和运维过程中&#xff0c;性能监控与调优是不可或缺的环节。无论是排查内存泄漏、分析CPU瓶颈&#xff0c;还是优化线程使用&#xff0c;开发者都需要借助一些强大的工具来辅助诊断。JVisualVM 正是这样一款由Oracle提供的免费工具&#xff0c;它集成了多种性能…

基于大语言模型的组合优化

摘要&#xff1a;组合优化&#xff08;Combinatorial Optimization, CO&#xff09;对于提高工程应用的效率和性能至关重要。随着问题规模的增大和依赖关系的复杂化&#xff0c;找到最优解变得极具挑战性。在处理现实世界的工程问题时&#xff0c;基于纯数学推理的算法存在局限…