Implementing a FIFO Export Queue in Spring Boot for Large Data Exports
The article demonstrates how to implement a bounded FIFO export queue in a Spring Boot application, using synchronized wait/notify methods to limit concurrent large‑data exports to ten threads, integrating with an abstract EasyExcel exporter and a controller that spawns export tasks, while noting future enhancements such as persistent storage and Redis‑based queues.
This article describes a practical solution for handling large‑scale data export requests in a Spring Boot application by using a fixed‑size FIFO queue to limit concurrent exports and protect database and I/O performance.
ExportQueue – a component that maintains a bounded LinkedList<ExportUser> queue (max size 10). It provides synchronized add and getNextSysUser methods that block when the queue is full or empty, respectively, using wait() / notifyAll() .
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<>();
}
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;
}
}AbstractExport – an abstract class that leverages Alibaba EasyExcel to export data in pages. It defines abstract methods for export logic, counting total rows, and fetching page data, and provides a concrete export(HttpServletResponse, int, T, Class<K>, String) implementation that writes multiple sheets.
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);
}ExportImpl – concrete implementation that injects ExportQueue , logs the export process, simulates a 20‑second export delay, and removes the user from the queue after completion.
package com.example.system.service.impl;
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.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) {}
@Override
public int countExport(Object o) { return 0; }
@Override
public List getExportDetail(Object o) { return null; }
}ExportController – a REST controller exposing /export/exportFile . It starts a new thread that creates an ExportUser with the thread name and calls the export service, demonstrating concurrent requests.
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();
}
}The test results show that when the queue reaches its limit of 10, additional export requests are blocked until a slot frees up. Screenshots illustrate the request timing and queue behavior.
In conclusion, the article provides a basic FIFO queue implementation for export tasks, notes that further features such as file storage, OSS upload, and high‑concurrency considerations remain to be addressed, and suggests alternatives like Redis‑based queues.
Java Tech Enthusiast
Sharing computer programming language knowledge, focusing on Java fundamentals, data structures, related tools, Spring Cloud, IntelliJ IDEA... Book giveaways, red‑packet rewards and other perks await!
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.