EasyExcel实现百万数据批量导出
当数据量比较大时,例如数据量达到百万级,传统的一次读取到内存中在写入excel文件的方法便不再适用了,可能会导致内存溢出;而且一次性将数据写入一张sheet工作表也不太好。
但我们可以选择数据分片的方式批量写入多个工作表。
测试数据100w条,写入到Excel表中,分成5个sheet表,每个sheet表20w条。
1.引入依赖
pom.xml
<dependencies><!-- web --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-web</artifactId></dependency><!-- mybatis --><dependency><groupId>org.mybatis.spring.boot</groupId><artifactId>mybatis-spring-boot-starter</artifactId><version>2.2.2</version></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-test</artifactId><scope>test</scope></dependency><!-- alibaba easyexcel --><dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.0.5</version><exclusions><exclusion><groupId>org.apache.poi</groupId><artifactId>poi</artifactId></exclusion><exclusion><groupId>org.apache.poi</groupId><artifactId>poi-ooxml</artifactId></exclusion><exclusion><groupId>org.apache.poi</groupId><artifactId>poi-ooxml-schemas</artifactId></exclusion></exclusions></dependency><!-- lombok --><dependency><groupId>org.projectlombok</groupId><artifactId>lombok</artifactId><version>1.18.24</version></dependency><!-- mybatis-plus --><dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-boot-starter</artifactId><version>3.5.3</version></dependency><!-- jdbc --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-jdbc</artifactId></dependency><!--Mysql依赖包--><dependency><groupId>mysql</groupId><artifactId>mysql-connector-java</artifactId></dependency><!-- hutool --><dependency><groupId>cn.hutool</groupId><artifactId>hutool-all</artifactId><version>5.7.16</version></dependency></dependencies>
2. Controller/Service/Mapper
实体类User
import com.alibaba.excel.annotation.ExcelProperty;
import com.baomidou.mybatisplus.annotation.TableId;
import com.baomidou.mybatisplus.annotation.TableName;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.NoArgsConstructor;import java.io.Serializable;
import java.time.LocalDateTime;@NoArgsConstructor
@AllArgsConstructor
@Data
@TableName("user")
public class User implements Serializable {@TableId("id")@ExcelProperty("id")private Long id;@ExcelProperty("username")private String username;@ExcelProperty("password")private String password;@ExcelProperty("email")private String email;@ExcelProperty("phoneNumber")private String phoneNumber;@ExcelProperty("createdAt")private LocalDateTime createdAt;@ExcelProperty(value = "updatedAt")private LocalDateTime updatedAt;}
UserCtrl
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.UUID;
import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.style.column.LongestMatchColumnWidthStyleStrategy;
import com.example.excel.config.LocalDateTimeConverter;
import com.example.excel.domain.User;
import com.example.excel.mapper.UserMapper;
import com.example.excel.util.ExcelConstants;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Executor;/*** @author: hong.jian* 09-28 11:37*/
@RestController
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/export")
public class UserCtrl {private final UserMapper userMapper;private final Executor exportThreadPoolExecutor;
}
UserService 和 UserServiceImpl
public interface UserService extends IService<User> {}@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {private final UserMapper userMapper;}
UserMapper
@Mapper
public interface UserMapper extends BaseMapper<User> {List<User> selfSelectList(@Param("currentPage") int currentPage, @Param("pageSize") int pageSize);}
UserMapper.xml
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN""http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="com.example.excel.mapper.UserMapper"><!-- 使用覆盖索引处理深度分页 --><select id="seleSelectList" resultType="com.example.excel.domain.User">SELECTu.id,u.username,u.PASSWORD,u.email,u.phone_number,u.created_at,u.updated_atFROMUSER u,( SELECT t.id FROM USER t ORDER BY t.id LIMIT #{currentPage}, #{pageSize} ) AS dWHEREu.id = d.id;</select></mapper>
LocalDateTime时间字段转换器
import com.alibaba.excel.converters.Converter;
import com.alibaba.excel.metadata.GlobalConfiguration;
import com.alibaba.excel.metadata.data.WriteCellData;
import com.alibaba.excel.metadata.data.ReadCellData;
import com.alibaba.excel.metadata.property.ExcelContentProperty;import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;/*** 自定义LocalDateTime时间字段类型转换器*/
public class LocalDateTimeConverter implements Converter<LocalDateTime> {private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");@Overridepublic Class<LocalDateTime> supportJavaTypeKey() {return LocalDateTime.class;}@Overridepublic WriteCellData<?> convertToExcelData(LocalDateTime value, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {if (value != null) {return new WriteCellData<>(value.format(FORMATTER));}return new WriteCellData<>("");}@Overridepublic LocalDateTime convertToJavaData(ReadCellData<?> cellData, ExcelContentProperty contentProperty, GlobalConfiguration globalConfiguration) {if (cellData.getStringValue() != null) {return LocalDateTime.parse(cellData.getStringValue(), FORMATTER);}return null;}
}
3. 编写Controller方法
@RequestMapping("/test")public void export(HttpServletResponse response) throws IOException {log.info("*********统计查询列表导出开始!**************");long start = System.currentTimeMillis();// 文件名String fileName = String.valueOf(UUID.randomUUID());OutputStream outputStream = null;try {// 总记录数:实际中需要根据查询条件进行统计即可:一共多少条long totalCount = userMapper.selectCount(null);// 每一个Sheet存放20w条数据Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;// 每次写入的数据量10w,每页查询10WInteger writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;// 计算需要的Sheet数量long sheetNum = totalCount % sheetDataRows == 0 ? (totalCount / sheetDataRows) : (totalCount / sheetDataRows + 1);// 计算一般情况下每一个Sheet需要写入的次数(一般情况不包含最后一个sheet,因为最后一个sheet不确定会写入多少条数据)Integer oneSheetWriteCount = sheetDataRows / writeDataRows;// 计算最后一个sheet需要写入的次数long lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount : (totalCount % sheetDataRows % writeDataRows == 0 ?((totalCount % sheetDataRows) / writeDataRows) : ((totalCount % sheetDataRows) / writeDataRows + 1));outputStream = response.getOutputStream();// 必须放到循环外,否则会刷新流ExcelWriter excelWriter = EasyExcel.write(outputStream).build();// 开始分批查询分次写入for (int i = 0; i < sheetNum; i++) {log.info("*********统计查询列表第" + (i + 1) + "页导出开始!**************");// 循环写入次数: j的自增条件是当不是最后一个Sheet的时候写入次数为正常的每个Sheet写入的次数,如果是最后一个就需要使用计算的次数lastSheetWriteCountfor (int j = 0; j < (i != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {// 分页查询一次10w// 设置偏移量int currentPage = (j + oneSheetWriteCount * i) * writeDataRows;// 设置每页大小int pageSize = writeDataRows;// 使用覆盖索引避免深度分页List<User> userList = userMapper.selfSelectList(currentPage, pageSize);if (CollUtil.isEmpty(userList)) { // 记录集合判空continue;}// 写入到excel:// 这里可以通过设置includeColumnFiledNames、excludeColumnFiledNames导出什么字段,可以动态配置,前端传过来那些列,就导出那些列WriteSheet writeSheet = EasyExcel.writerSheet(i, "Sheet" + (i + 1)).head(User.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()) // 自动调整列宽.registerConverter(new LocalDateTimeConverter()) // 时间字段转换器// .includeColumnFiledNames() // 只导出指定字段集合// .excludeColumnFiledNames() // 排除指定字段集合.build();excelWriter.write(userList, writeSheet);}log.info("*********统计查询列表第" + (i + 1) + "页导出结束!**************");}// 下载EXCEL,返回给前端stream流response.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");excelWriter.finish();outputStream.flush();log.info("*********统计查询列表导出结束!**************");log.info("耗时:" + (System.currentTimeMillis() - start));} catch (Exception e) {e.printStackTrace();} finally {if (outputStream != null) {outputStream.close();}}}
4. 测试
100w条数据导出约27s,还是有点慢。
9-28 17:49:27.847 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表导出开始!**************
2024-09-28 17:49:28.744 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第1页导出开始!**************
2024-09-28 17:49:32.857 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第1页导出结束!**************
2024-09-28 17:49:32.857 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第2页导出开始!**************
2024-09-28 17:49:36.955 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第2页导出结束!**************
2024-09-28 17:49:36.955 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第3页导出开始!**************
2024-09-28 17:49:41.007 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第3页导出结束!**************
2024-09-28 17:49:41.007 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第4页导出开始!**************
2024-09-28 17:49:45.109 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第4页导出结束!**************
2024-09-28 17:49:45.109 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第5页导出开始!**************
2024-09-28 17:49:49.272 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表第5页导出结束!**************
2024-09-28 17:49:55.630 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表导出结束!**************
2024-09-28 17:49:55.631 INFO 120052 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : 耗时:27784
5. 思考
引入线程池
@Beanpublic Executor exportThreadPoolExecutor(){return new ThreadPoolExecutor(5,10,10,TimeUnit.SECONDS,new ArrayBlockingQueue<>(100),new ThreadPoolExecutor.CallerRunsPolicy());}
并在UserController中注入
使用线程池优化代码
@RequestMapping("/test2")public void export2(HttpServletResponse response) throws IOException {log.info("*********统计查询列表导出开始!**************");long start = System.currentTimeMillis();String fileName = String.valueOf(UUID.randomUUID());OutputStream outputStream = null;try {long totalCount = userMapper.selectCount(null);Integer sheetDataRows = ExcelConstants.PER_SHEET_ROW_COUNT;Integer writeDataRows = ExcelConstants.PER_WRITE_ROW_COUNT;long sheetNum = (totalCount + sheetDataRows - 1) / sheetDataRows;Integer oneSheetWriteCount = sheetDataRows / writeDataRows;long lastSheetWriteCount = totalCount % sheetDataRows == 0 ? oneSheetWriteCount :(totalCount % sheetDataRows + writeDataRows - 1) / writeDataRows;outputStream = response.getOutputStream();ExcelWriter excelWriter = EasyExcel.write(outputStream).build();// 信号枪,配合多线程实现并发编程CountDownLatch latch = new CountDownLatch((int) sheetNum);for (int i = 0; i < sheetNum; i++) {int finalI = i;CompletableFuture.runAsync(() -> {log.info("*********统计查询列表第" + (finalI + 1) + "页导出开始!**************");WriteSheet writeSheet = EasyExcel.writerSheet(finalI, "Sheet" + (finalI + 1)).head(User.class).registerWriteHandler(new LongestMatchColumnWidthStyleStrategy()).registerConverter(new LocalDateTimeConverter()).build();try {for (int j = 0; j < (finalI != sheetNum - 1 ? oneSheetWriteCount : lastSheetWriteCount); j++) {int currentPage = (j + oneSheetWriteCount * finalI) * writeDataRows;List<User> userList = userMapper.selfSelectList(currentPage, writeDataRows);if (CollUtil.isEmpty(userList)) {continue;}// 加锁实现互斥写入,确保线程安全synchronized (excelWriter) {excelWriter.write(userList, writeSheet);}}} finally {latch.countDown();log.info("*********统计查询列表第" + (finalI + 1) + "页导出结束!**************");}}, exportThreadPoolExecutor); // 尝试引入多线程}latch.await();response.setContentType("application/octet-stream");response.setCharacterEncoding("utf-8");response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileName + ".xlsx");excelWriter.finish();outputStream.flush();} catch (Exception e) {e.printStackTrace();} finally {if (outputStream != null) {outputStream.close();}}log.info("*********统计查询列表导出结束!**************");log.info("耗时:" + (System.currentTimeMillis() - start));}
再次测试,耗时约20s
2024-09-28 18:05:54.239 INFO 53952 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表导出开始!**************
2024-09-28 18:05:55.132 INFO 53952 --- [pool-1-thread-2] com.example.excel.controller.UserCtrl : *********统计查询列表第2页导出开始!**************
2024-09-28 18:05:55.132 INFO 53952 --- [pool-1-thread-3] com.example.excel.controller.UserCtrl : *********统计查询列表第3页导出开始!**************
2024-09-28 18:05:55.132 INFO 53952 --- [pool-1-thread-4] com.example.excel.controller.UserCtrl : *********统计查询列表第4页导出开始!**************
2024-09-28 18:05:55.132 INFO 53952 --- [pool-1-thread-5] com.example.excel.controller.UserCtrl : *********统计查询列表第5页导出开始!**************
2024-09-28 18:05:55.132 INFO 53952 --- [pool-1-thread-1] com.example.excel.controller.UserCtrl : *********统计查询列表第1页导出开始!**************
2024-09-28 18:06:04.341 INFO 53952 --- [pool-1-thread-3] com.example.excel.controller.UserCtrl : *********统计查询列表第3页导出结束!**************
2024-09-28 18:06:05.483 INFO 53952 --- [pool-1-thread-4] com.example.excel.controller.UserCtrl : *********统计查询列表第4页导出结束!**************
2024-09-28 18:06:06.591 INFO 53952 --- [pool-1-thread-5] com.example.excel.controller.UserCtrl : *********统计查询列表第5页导出结束!**************
2024-09-28 18:06:07.795 INFO 53952 --- [pool-1-thread-1] com.example.excel.controller.UserCtrl : *********统计查询列表第1页导出结束!**************
2024-09-28 18:06:08.871 INFO 53952 --- [pool-1-thread-2] com.example.excel.controller.UserCtrl : *********统计查询列表第2页导出结束!**************
2024-09-28 18:06:14.907 INFO 53952 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : *********统计查询列表导出结束!**************
2024-09-28 18:06:14.907 INFO 53952 --- [nio-8080-exec-1] com.example.excel.controller.UserCtrl : 耗时:20667
可以发现,性能有提升,但提升不是很明显。我个人理解是为了避免多个线程同时写入excel产生的线程安全问题,采用了加锁的方式,确保线程间使用excelwriter写入excel文件的操作是互斥的,这导致性能收到了影响。
参考链接:使用EasyExcel实现excel导出,支持百万大数据量导出
相关文章:
EasyExcel实现百万数据批量导出
当数据量比较大时,例如数据量达到百万级,传统的一次读取到内存中在写入excel文件的方法便不再适用了,可能会导致内存溢出;而且一次性将数据写入一张sheet工作表也不太好。 但我们可以选择数据分片的方式批量写入多个工作表。 测试…...
兆易GD32E508的SHRTIM配置 主从定时器 产生2对相位可调互补PWM 带死区
如有技术问题及技术需求请加作者微信! GD32E5系列MCU是基于Arm Cortex-M33处理器的32位通用微控制器。Cortex-M33处理器基于Armv8架构,处理器主频最高可达180MHz,支持强大的可扩展指令集,包括通用数据处理I/O控制任务、增强的数据处理位域操作、DSP和浮点运算器(FPU)。 GD…...
数据归组工具
利用C#将数据 [ {"name":"A","fzh":1}, {"name":"A","fzh":2}, {"name":"A","fzh":3}, {"name":"B","fzh":4}, {"name":"B",&…...
JavaScript 中的闭包的形成及使用场景
JavaScript 中的闭包 闭包(Closure) 是 JavaScript 中一个非常重要且独特的概念,它指的是 函数能够记住并访问其词法作用域内的变量,即使这个函数在其词法作用域之外执行。 通俗地说,闭包是 一个函数可以“记住”它在…...

后端返回内容有换行标识,前端如何识别换行
<br/>的话 用 v-html \n 可以用css样式 white-space: pre-wrap 后端返回结果 前端...
服务器被挂马,导致网站首页被更改怎么解决
当服务器被挂马并导致网站首页被篡改时,说明服务器或网站的安全性遭到破坏。为了修复并防止未来的攻击,你可以按照以下步骤进行操作: 1. 立即下线网站 目的:防止恶意软件进一步传播,保护用户数据和防止攻击者继续对网…...

Android 利用OSMdroid开发GIS
1、地址 Github地址:https://gitee.com/mirrors/osmdroid Git地址: GitCode - 全球开发者的开源社区,开源代码托管平台 Git下载包地址:Releases osmdroid/osmdroid GitHub 新建项目 osmdroid在线: (1)…...

一文上手skywalking【上】
一、skywalking预览 1.1 skywalking 概述 Apache SkyWalking, 适用于分布式系统的应用程序性能监控工具,专为微服务、云原生和基于容器的 (Kubernetes) 架构而设计。官方地址: https://skywalking.apache.org/ 适用于分布式系统的应用程…...

【JavaScript】JQuery基础知识及应用
一、JQuery的导入方法 https://editor.csdn.net/md/?articleId132214798 二、JQuery介绍 JQuery(JQ):JS的一个类库(方法库:包含了大量的、有助于项目开发的属性和方法) 第一代版本1.xx.xx: 1.11.3 兼容所有浏览器的࿰…...

初始爬虫9
1.元素定位后的操作 “find_element“仅仅能够获取元素,不能够直接获取其中的数据,如果需要获取数据需要使用以下方法”。下面列出了两个方法: 获取文本 element.text 通过定位获取的标签对象的 text 属性,获取文本内容 获取属性…...
从细胞到临床:表观组学分析技术在精准医疗中的角色
中国科学院等科研院所的顶尖人才发起,专注于多组学、互作组、生物医学等领域的研究与服务。在Nature等国际知名期刊发表多篇论文,提供实验整体打包、免费SCI论文润色等四大优势服务。在表观组学分析技术方面,提供DAP-seq、ATAC-seq、H3K4me3 …...

带你0到1之QT编程:二十、QT与MySQL喜结连理,构建数据库应用开发
此为QT编程的第二十谈!关注我,带你快速学习QT编程的学习路线! 每一篇的技术点都是很很重要!很重要!很重要!但不冗余! 我们通常采取总-分-总和生活化的讲解方式来阐述一个知识点! …...
梯度下降法及其性能评估
梯度下降法 梯度下降法是一种一阶迭代优化算法,用于寻找函数的局部最小值。在机器学习中,它通常用来最小化损失函数(也称为成本函数或误差函数),以提高模型对数据的拟合程度。梯度下降法的基本思想是沿着目标函数当前…...

906. 超级回文数
1. 题目 906. 超级回文数 2. 解题思路 题目意思很简单,在给定范围中找到所有满足,它本身是回文,且它的平方也是回文的数字个数。 这题需要注意题目给定的范围,后面很有用: 因为回文范围是有限的,那么我…...
代码随想录算法训练营||二叉树
前/中/后序遍历 递归方式 参考文章 题目 思路:其实递归方式的前中后序遍历的方式都差不多,区别是在父节点的遍历时间。 前序代码 class Solution {public List<Integer> preorderTraversal(TreeNode root) {List<Integer> result new…...

线上报名小程序怎么做
在这个数字化、智能化的时代,信息技术的发展正以前所未有的速度改变着我们的生活。无论是学习、工作还是娱乐,互联网都成为了我们不可或缺的一部分。而在线上报名这一领域,小程序的出现更是为广大用户带来了前所未有的便捷与高效。今天&#…...
【测试岗】手撕代码 - 零钱兑换
322. 零钱兑换 题目描述 给你一个整数数组 coins ,表示不同面额的硬币;以及一个整数 amount ,表示总金额。 计算并返回可以凑成总金额所需的 最少的硬币个数 。如果没有任何一种硬币组合能组成总金额,返回 -1 。 你可以认为每种…...

菱形继承的类对父类的初始化、组合、多态、多态的原理等的介绍
文章目录 前言一、菱形继承的类对父类的初始化二、组合三、 多态1. 构成多态2. 虚函数3. 虚函数的重写4. 虚函数重写的两个例外1. 协变2. 析构函数的重写 5. C11 final 和 override1. final2. override 6. 设计不想被继承的类7. 重载、覆盖(重写)、 隐藏…...
React Native 在 build 的时候如果出现 `babel.config.js` 配置文件的错误
React Native 在 build 的时候如果出现以下错误, 就是 babel.config.js 配置文件的错误. Showing Recent Issues node:internal/process/promises:289triggerUncaughtException(err, true /* fromPromise */);^Error: .plugins[0][1] must be an object, false, or undefineda…...

【Linux】包管理器、vim详解及简单配置
🚀个人主页:小羊 🚀所属专栏:Linux 很荣幸您能阅读我的文章,诚请评论指点,欢迎欢迎 ~ 目录 前言一、包管理器1.1 apt1.2 yum 二、Linux编辑器——vim2.1 vim的三种模式2.2 vim普通模式常用命令2.2.1 移动…...
[特殊字符] 智能合约中的数据是如何在区块链中保持一致的?
🧠 智能合约中的数据是如何在区块链中保持一致的? 为什么所有区块链节点都能得出相同结果?合约调用这么复杂,状态真能保持一致吗?本篇带你从底层视角理解“状态一致性”的真相。 一、智能合约的数据存储在哪里…...

Linux应用开发之网络套接字编程(实例篇)
服务端与客户端单连接 服务端代码 #include <sys/socket.h> #include <sys/types.h> #include <netinet/in.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <arpa/inet.h> #include <pthread.h> …...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...
SkyWalking 10.2.0 SWCK 配置过程
SkyWalking 10.2.0 & SWCK 配置过程 skywalking oap-server & ui 使用Docker安装在K8S集群以外,K8S集群中的微服务使用initContainer按命名空间将skywalking-java-agent注入到业务容器中。 SWCK有整套的解决方案,全安装在K8S群集中。 具体可参…...

stm32G473的flash模式是单bank还是双bank?
今天突然有人stm32G473的flash模式是单bank还是双bank?由于时间太久,我真忘记了。搜搜发现,还真有人和我一样。见下面的链接:https://shequ.stmicroelectronics.cn/forum.php?modviewthread&tid644563 根据STM32G4系列参考手…...

【Oracle APEX开发小技巧12】
有如下需求: 有一个问题反馈页面,要实现在apex页面展示能直观看到反馈时间超过7天未处理的数据,方便管理员及时处理反馈。 我的方法:直接将逻辑写在SQL中,这样可以直接在页面展示 完整代码: SELECTSF.FE…...
Admin.Net中的消息通信SignalR解释
定义集线器接口 IOnlineUserHub public interface IOnlineUserHub {/// 在线用户列表Task OnlineUserList(OnlineUserList context);/// 强制下线Task ForceOffline(object context);/// 发布站内消息Task PublicNotice(SysNotice context);/// 接收消息Task ReceiveMessage(…...

抖音增长新引擎:品融电商,一站式全案代运营领跑者
抖音增长新引擎:品融电商,一站式全案代运营领跑者 在抖音这个日活超7亿的流量汪洋中,品牌如何破浪前行?自建团队成本高、效果难控;碎片化运营又难成合力——这正是许多企业面临的增长困局。品融电商以「抖音全案代运营…...

基于当前项目通过npm包形式暴露公共组件
1.package.sjon文件配置 其中xh-flowable就是暴露出去的npm包名 2.创建tpyes文件夹,并新增内容 3.创建package文件夹...

相机从app启动流程
一、流程框架图 二、具体流程分析 1、得到cameralist和对应的静态信息 目录如下: 重点代码分析: 启动相机前,先要通过getCameraIdList获取camera的个数以及id,然后可以通过getCameraCharacteristics获取对应id camera的capabilities(静态信息)进行一些openCamera前的…...