Posted in

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

包阅导读总结

1. `ExportQueue`、`EasyExcel`、`导出`、`队列`、`AbstractExport`

2. 本文主要介绍了一个使用`ExportQueue`队列解决多人同时导出`Excel`可能导致服务器崩溃的问题,并结合`EasyExcel`实现导出功能,还包括了相关的抽象类和实现类,目前部分功能未完善,如文件表设计、oss文件上传等,且未考虑高并发场景。

3.

– 项目配置

– `ExportQueue`类:定义最大容量,实现添加和获取队列元素的方法,处理队列满和空的情况。

– 导出功能

– `AbstractExport`抽象类:定义导出相关的方法,如`export`、获取`ExcelWriter`、复杂填充表格等,还有计数和获取导出详情的抽象方法。

– `ExportImpl`实现类:继承`AbstractExport`,实现具体的`export`方法和其他方法。

– 测试部分

– `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