由于备份的文件都比较大,开始就碰到了问题,前端报错了 504 Gateway Time-out
1.增加nginx 超时时间:增加 proxy_read_timeout
和 proxy_connect_timeout
的值,以允许更多的时间来等待后端服务的响应
2.分块传输
3.流式处理
部分优化后代码:
/**
* @author xyt
*/
@RestController
@RequestMapping("/system")
@Slf4j
public class GlobalSystemController {
@Resource
private ISystemService systemService;
@ApiOperation(value = "全量备份系统,带历史数据")
@GetMapping("/backup")
@OpLog(outputExpression = "{#code}")
public void backupSystem(HttpServletResponse response){
// 异步备份
CompletableFuture<File> backupFuture = systemService.backupSystemAsync();
// 等待备份完成并获取文件
File file = backupFuture.join();
if (file == null) return;
// 设置响应头
response.setContentType("application/zip");
response.setHeader("Content-Disposition", "attachment; filename=\"" + file.getName() + "\"");
response.setHeader("Transfer-Encoding", "chunked");
// 使用流式处理下载文件
try (FileInputStream fileInputStream = new FileInputStream(file);
OutputStream outputStream = response.getOutputStream()) {
//从输入流到输出流进行拷贝,适用于大文件
IOUtils.copyLarge(fileInputStream, outputStream);
log.info("系统文件{}已下载!", file.getName());
} catch (IOException e) {
log.error("下载失败: {}", e.getMessage());
} finally {
// 异步删除文件
CompletableFuture.runAsync(() -> FileOperationUtil.deleteFile(file.getAbsolutePath()));
}
}
}
public File backupSystem() throws Exception {
String project = workSpaceConfig.getConfig();
SystemInfoDTO systemInfo = systemInfoService.getSystemInfo();
String name = systemInfo.getName();
//压缩文件临时存放地址
String destPath = new File(project).getParent() + File.separator + name + "配置备份(window).zip";
File destFile = new File(destPath);
File srcFile = new File(project);
try {
//存放导出mysql 脚本目录
String backDBConfigShellPath = project + File.separator + "shell" + File.separator + "backup_db.bat";
// 备份出的sql
RunTimeUtil.runCmd(new String[]{backDBConfigShellPath});
} catch (Exception e) {
e.printStackTrace();
log.error("备份sql文件失败:{}", e.getMessage());
} finally {
Thread.sleep(500);
}
// 创建压缩文件
ZipUtil.zip(destFile, Charset.forName("UTF8"), false, item -> true, srcFile);
return destFile;
}
@Override
public CompletableFuture<File> backupSystemAsync() {
return CompletableFuture.supplyAsync(() -> {
try {
return backupSystem();
} catch (Exception e) {
log.error("全量备份失败: {}", e.getMessage());
return null;
}
});
}
对于处理大文件(例如2GB的ZIP文件)时,超时和资源管理是常见的问题:
-
分块传输:使用分块传输(chunked transfer encoding)来处理大文件,可以避免一次性加载整个文件到内存中。
-
流式处理:确保文件处理是流式的,而不是一次性读取整个文件到内存中。
-
异步处理:确保文件压缩和下载过程都是异步的,避免阻塞主线程。
-
增加超时时间:调整服务器和客户端的超时时间设置,以应对大文件传输所需的时间。
-
使用更高效的I/O库:例如NIO(非阻塞I/O)可以提高文件读写的效率。
导出mysql脚本:
backup_db.bat(username , pwd, port, dbName 修改为自己的数据库信息)
@echo off
set currPath=%~dp0
set split=''
:begin
FOR /F "tokens=1,* delims=\" %%i IN ("%currPath%") DO (set split=%%i)
FOR /F "tokens=1,* delims=\" %%i IN ("%currPath%") DO (set currPath=%%j)
if not "%parentPath%" == "" goto gotJpdaOpts
set parentPath=%split%\
goto begin
:gotJpdaOpts
if %parentPath%%split%\==%~dp0 goto end
set parentPath=%parentPath%%split%\
goto begin
:end
set username=数据库用户
set pwd=数据库密码
set port=3306
set dbName=数据库名
set sqlPath=%parentPath%%dbName%.sql
mysql -u%username% -p%pwd% %dbName% -s -e "call clean_all_data()"
mysqldump -u%username% -p%pwd% -P%port% --default-character-set=utf8mb4 --lock-tables --extended-insert=false --triggers --events -R --hex-blob --single-transaction %dbName% > %sqlPath%
echo sql save path %sqlPath%
接口地址 : "/project/system/backup"
Nginx优化(调整服务器和客户端的超时时间设置,以应对大文件传输所需的时间):
server {
listen 80; # 或者你需要的端口
server_name 127.0.0.1; # 替换为你的域名
client_max_body_size 500m;
client_header_timeout 30;
client_body_timeout 30;
location /api/ {
proxy_pass http://localhost:9527/;
proxy_connect_timeout 60s;
proxy_read_timeout 120s;
proxy_send_timeout 120s;
}
location /project/system/backup {
proxy_pass http://localhost:9527; # 确保目标地址是正确的
proxy_read_timeout 300; # 从后端服务器读取响应的超时时间
proxy_connect_timeout 300; # 与后端服务器建立连接的超时时间
proxy_send_timeout 300; # 向后端服务器发送请求的超时时间
}
...
}
说明:
-
proxy_pass: 指定要将请求转发到的后端服务地址。
-
proxy_read_timeout: 设置从后端服务器读取响应的超时时间。
-
proxy_connect_timeout: 设置与后端服务器建立连接的超时时间。
-
proxy_send_timeout: 设置向后端服务器发送请求的超时时间。
-
proxy_set_header: 这些设置用于传递请求的头部信息,确保后端能够获取到客户端的真实信息。