Implementing a FIFO Export Queue for Large Data Exports in Java
The article describes a Java‑based FIFO export queue that limits concurrent MySQL export operations, outlines the related classes (ExportQueue, AbstractExport, ExportImpl, and a test controller), provides full source code, and presents test results showing how the queue manages up to ten simultaneous export requests.
The business requirement is to handle growing database export volumes without degrading server performance; a fixed‑size FIFO queue is introduced to serialize export tasks, limiting concurrent MySQL I/O and file I/O.
Three main components are defined: ExportQueue for managing a bounded user queue, User information representing the export requester, and an Export class that performs the actual data export.
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 @Component public class ExportQueue { private final int MAX_CAPACITY = 10; // 队列最大容量 private LinkedList queue; // 用户队列 public ExportQueue(LinkedList queue) { this.queue = new LinkedList<>(); } /** * 排队队列添加 * @param sysUser */ public synchronized LinkedList 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; } }
The AbstractExport class defines a generic export framework using EasyExcel, handling pagination, file naming, and error handling while leaving data‑specific methods abstract.
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; @Slf4j public abstract class AbstractExport { public abstract void export(ExportUser sysUser) throws InterruptedException; /** * 导出 */ public void export(HttpServletResponse response, int pageSize, T t, Class 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 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 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 getExportDetail(T t); }
The ExportImpl class extends AbstractExport , injects the ExportQueue , and implements the export logic, including simulated processing delay and queue management.
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 @Slf4j public class ExportImpl extends AbstractExport { @Autowired private ExportQueue exportQueue; @Override public void export(ExportUser sysUser) throws InterruptedException { log.info("导出文件方法执行~~~~~~~~~"); LinkedList 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) { // not implemented } @Override public int countExport(Object o) { return 0; } @Override public List getExportDetail(Object o) { return null; } }
A simple ExportController demonstrates how multiple threads can invoke the export service, each creating an ExportUser and adding it to the queue.
package com.example.system.controller; import com.example.system.api.domain.ExportUser; 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.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController @RequestMapping("/export") @Slf4j public 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(); } }
Test results confirm that the queue enforces a maximum of ten concurrent export requests; when the limit is reached, additional requests wait until a slot becomes free, demonstrating proper FIFO behavior.
The article concludes with remarks about unfinished features (file table design, OSS upload, download, high‑concurrency considerations) and suggests that a Redis‑based queue could be an alternative implementation.
IT Architects Alliance
Discussion and exchange on system, internet, large‑scale distributed, high‑availability, and high‑performance architectures, as well as big data, machine learning, AI, and architecture adjustments with internet technologies. Includes real‑world large‑scale architecture case studies. Open to architects who have ideas and enjoy sharing.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.