Posted in

多人同时导出 Excel 干崩服务器?新来的大佬优雅解决_AI阅读总结 — 包阅AI

包阅导读总结

1. `导出队列`、`EasyExcel`、`ExportUser`、`高并发`、`Redis`

2. 本文介绍了一个使用 Java 实现的导出队列功能,通过 `ExportQueue` 类控制队列长度,`AbstractExport` 类实现导出的基础逻辑,`ExportImpl` 类进行具体处理,`ExportController` 进行请求控制。目前仅实现基本功能,如限制队列最大 10 次,高并发场景等问题未深入考虑,还提到 Redis 队列也可实现,仅提供思路。

3.

– 导出队列配置

– `ExportQueue` 类设置最大容量为 10,实现添加和获取队列元素的同步方法。

– 导出功能实现

– `AbstractExport` 类提供导出的基础框架,包括导出方法、获取 ExcelWriter 等。

– `ExportImpl` 类继承 `AbstractExport` 类,实现具体的导出逻辑。

– 测试控制

– `ExportController` 类通过线程启动导出操作。

– 未完善与思考

– 导出文件的表设计、OSS 文件上传、用户下载等未实现。

– 高并发场景问题未考虑,提及 Redis 队列也可实现,仅作思路提供。

思维导图:

文章地址:https://mp.weixin.qq.com/s/HodQWYhumvlZjWFkiPhU8w

文章来源:mp.weixin.qq.com

作者:Lxlxxx

发布时间:2024/7/24 11:07

语言:中文

总字数:1607字

预计阅读时间:7分钟

评分:89分

标签:导出优化,服务器性能,队列管理,异步处理,Java


以下为原文内容

本内容来源于用户推荐转载,旨在分享知识与观点,如有侵权请联系删除 联系邮箱 media@ilingban.com

ExportQueue队列

package com.example.system.config;
import com.example.system.api.domain.ExportUser;import lombok.extern.slf4j.Slf4j;import org.springframework.stereotype.Component;
import java.util.LinkedList;
@Slf4j@Componentpublic class ExportQueue {

private final int MAX_CAPACITY = 10; private LinkedList<ExportUser> queue;
public ExportQueue(LinkedList<ExportUser> queue) { this.queue = new LinkedList<>(); } public synchronized LinkedList<ExportUser> add(ExportUser sysUser) { while (queue.size() >= MAX_CAPACITY) { try { log.info("当前排队人已满,请等待"); wait(); } catch (InterruptedException e) { e.getMessage(); } } queue.add(sysUser); log.info("目前导出队列排队人数:" + queue.size()); notifyAll(); return queue; }
public synchronized ExportUser getNextSysUser() { while (queue.isEmpty()) { try { wait(); } catch (InterruptedException e) { e.printStackTrace(); } } ExportUser sysUser = queue.remove(); notifyAll(); return sysUser; }}


AbstractExport导出类

引入EasyExcel百万级别的导出功能。

package com.example.system.config;

import cn.hutool.core.bean.BeanUtil;import cn.hutool.core.util.PageUtil;import com.alibaba.excel.EasyExcel;import com.alibaba.excel.ExcelWriter;import com.alibaba.excel.write.metadata.WriteSheet;import com.example.system.api.domain.ExportUser;import lombok.extern.slf4j.Slf4j;
import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.net.URLEncoder;import java.util.List;
@Slf4jpublic abstract class AbstractExport<T, K> {

public abstract void export(ExportUser sysUser) throws InterruptedException; public void export(HttpServletResponse response, int pageSize, T t, Class<K> k, String fileName) throws Exception { ExcelWriter writer = null; try { writer = getExcelWriter(response, fileName); int total = this.countExport(t); int loopCount = PageUtil.totalPage(total, pageSize); BeanUtil.setProperty(t, "pageSize", pageSize); for (int i = 0; i < loopCount; i++) { BeanUtil.setProperty(t, "pageNum", PageUtil.getStart(i + 1, pageSize)); List<K> kList = this.getExportDetail(t); WriteSheet writeSheet = EasyExcel.writerSheet(fileName).head(k).build(); writer.write(kList, writeSheet); } } catch (Exception e) { String msg = "导出" + fileName + "异常"; log.error(msg, e); throw new Exception(msg + e); } finally { if (writer != null) { writer.finish(); } } }

public com.alibaba.excel.ExcelWriter getExcelWriter(HttpServletResponse response, String fileName) throws IOException { response.setContentType("application/vnd.ms-excel"); response.setCharacterEncoding("utf-8"); String fileNameUtf = URLEncoder.encode(fileName, "UTF-8").replaceAll("\\+", "%20"); response.setHeader("Content-disposition", "attachment;filename*=utf-8''" + fileNameUtf + ".xlsx"); return EasyExcel.write(response.getOutputStream()).build(); }
public abstract void complexFillWithTable(T t, String fileName, HttpServletResponse response); public abstract int countExport(T t); public abstract List<K> getExportDetail(T t);}


ExportImpl导出实现方法

package com.example.system.service.impl;
import com.alibaba.excel.ExcelWriter;import com.example.system.api.domain.ExportUser;import com.example.system.config.AbstractExport;import com.example.system.config.ExportQueue;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.stereotype.Service;
import javax.servlet.http.HttpServletResponse;import java.io.IOException;import java.util.LinkedList;import java.util.List;
@Service@Slf4jpublic class ExportImpl extends AbstractExport {
@Autowired private ExportQueue exportQueue;

@Override public void export(ExportUser sysUser) throws InterruptedException {
log.info("导出文件方法执行~~~~~~~~~"); LinkedList<ExportUser> queue = exportQueue.add(sysUser); log.info("导出队列:" + queue); Thread.sleep(20000); ExportUser nextSysUser = exportQueue.getNextSysUser(); log.info("移除后获取下一个排队的用户: " + nextSysUser.getUserName());
}

@Override public void export(HttpServletResponse response, int pageSize, Object o, Class k, String fileName) throws Exception { super.export(response, pageSize, o, k, fileName); }
@Override public ExcelWriter getExcelWriter(HttpServletResponse response, String fileName) throws IOException { return super.getExcelWriter(response, fileName); }
@Override public void complexFillWithTable(Object o, String fileName, HttpServletResponse response) {
}
@Override public int countExport(Object o) { return 0; }
@Override public List getExportDetail(Object o) { return null; }}


测试controller

package com.example.system.controller;
import com.example.system.api.domain.ExportUser;import com.example.system.api.domain.SysUser;import com.example.system.service.impl.ExportImpl;import lombok.SneakyThrows;import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.web.bind.annotation.PostMapping;import org.springframework.web.bind.annotation.RequestBody;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RestController;
@RestController@RequestMapping("/export")@Slf4jpublic class ExportController {
@Autowired private ExportImpl export;

@PostMapping("/exportFile") public void exportFile() { new Thread(new Runnable() { @SneakyThrows @Override public void run() { Thread thread1 = Thread.currentThread(); ExportUser sysUser =new ExportUser(); sysUser.setUserName(thread1.getName());
export.export(sysUser); } }).start(); }}


测试结果

通过请求测试方法,限制了我们导出队列最大限制10次,队列场长度超过10次则无法进行继续提交。

第一次请求和第二次请求,间隔10秒,第一个用户导出完成后出列,下一个排队用户在队列首位,在进行导出请求排在上一个用户后面。

其余的还未实现,导出文件的表的设计、oss文件上传、用户导出文件下载,还有高并发的场景下会不会出现什么问题,这些都还没有太考虑进去;实现的方式应该挺多的,Redis的队列应该也是可以的,这里仅仅提供一个实现思路。

来源丨https://juejin.cn/post/7259249904777838629
dbaplus社群欢迎广大技术人员投稿,投稿邮箱:editor@dbaplus.cn