183 lines
6.3 KiB
Markdown
183 lines
6.3 KiB
Markdown
|
|
# 用户导入504超时错误修复说明
|
|||
|
|
|
|||
|
|
## 问题描述
|
|||
|
|
在导入用户档案数据时,出现 **504 Gateway Time-out** 错误。这是因为:
|
|||
|
|
1. 数据量较大,导入处理时间超过了网关(Nginx)的超时限制
|
|||
|
|
2. 同步处理导致HTTP请求长时间等待响应
|
|||
|
|
|
|||
|
|
## 修复方案
|
|||
|
|
采用**异步导入**方式,将耗时的导入操作放到后台线程执行,HTTP请求立即返回,通过进度轮询查看导入状态。
|
|||
|
|
|
|||
|
|
## 修改内容
|
|||
|
|
|
|||
|
|
### 1. Controller层修改 ✅
|
|||
|
|
**文件**: `ry-xinli-admin/src/main/java/com/ddnai/web/controller/psychology/PsyUserProfileController.java`
|
|||
|
|
|
|||
|
|
**修改内容**:
|
|||
|
|
- 修改 `importData` 方法,调用异步导入方法 `importProfileAsync`
|
|||
|
|
- 立即返回响应,告知前端导入任务已启动
|
|||
|
|
- 前端通过 `/importProgress` 接口轮询获取进度
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
@PostMapping("/importData")
|
|||
|
|
public AjaxResult importData(MultipartFile file, boolean updateSupport) throws Exception
|
|||
|
|
{
|
|||
|
|
ExcelUtil<PsyUserProfile> util = new ExcelUtil<PsyUserProfile>(PsyUserProfile.class);
|
|||
|
|
List<PsyUserProfile> profileList = util.importExcel(file.getInputStream());
|
|||
|
|
String operName = getUsername();
|
|||
|
|
|
|||
|
|
// 异步执行导入任务,立即返回
|
|||
|
|
profileService.importProfileAsync(profileList, updateSupport, operName);
|
|||
|
|
|
|||
|
|
// 立即返回响应,告知前端导入任务已启动
|
|||
|
|
return success("导入任务已启动,共 " + profileList.size() + " 条数据。请稍候查看导入进度...");
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 2. Service接口层修改 ✅
|
|||
|
|
**文件**: `ry-xinli-system/src/main/java/com/ddnai/system/service/psychology/IPsyUserProfileService.java`
|
|||
|
|
|
|||
|
|
**修改内容**:
|
|||
|
|
- 添加异步导入方法接口 `importProfileAsync`
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
/**
|
|||
|
|
* 异步导入用户档案数据(避免超时)
|
|||
|
|
*/
|
|||
|
|
public void importProfileAsync(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName);
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 3. Service实现层修改 ✅
|
|||
|
|
**文件**: `ry-xinli-system/src/main/java/com/ddnai/system/service/impl/psychology/PsyUserProfileServiceImpl.java`
|
|||
|
|
|
|||
|
|
**修改内容**:
|
|||
|
|
- 添加 `@Async` 注解导入
|
|||
|
|
- 实现异步导入方法,使用 `@Async` 注解标记
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
import org.springframework.scheduling.annotation.Async;
|
|||
|
|
|
|||
|
|
@Override
|
|||
|
|
@Async
|
|||
|
|
public void importProfileAsync(List<PsyUserProfile> profileList, Boolean isUpdateSupport, String operName)
|
|||
|
|
{
|
|||
|
|
try
|
|||
|
|
{
|
|||
|
|
log.info("异步导入任务开始,操作人:{},数据量:{}", operName, profileList.size());
|
|||
|
|
// 调用同步导入方法执行实际导入逻辑
|
|||
|
|
importProfile(profileList, isUpdateSupport, operName);
|
|||
|
|
log.info("异步导入任务完成,操作人:{}", operName);
|
|||
|
|
}
|
|||
|
|
catch (Exception e)
|
|||
|
|
{
|
|||
|
|
log.error("异步导入任务失败,操作人:{},错误:{}", operName, e.getMessage(), e);
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 4. 启动类修改 ✅
|
|||
|
|
**文件**: `ry-xinli-admin/src/main/java/com/ddnai/XinliApplication.java`
|
|||
|
|
|
|||
|
|
**修改内容**:
|
|||
|
|
- 添加 `@EnableAsync` 注解启用异步支持
|
|||
|
|
- 配置异步任务线程池
|
|||
|
|
|
|||
|
|
```java
|
|||
|
|
import org.springframework.scheduling.annotation.EnableAsync;
|
|||
|
|
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
|
|||
|
|
import java.util.concurrent.Executor;
|
|||
|
|
|
|||
|
|
@SpringBootApplication(exclude = { DataSourceAutoConfiguration.class })
|
|||
|
|
@EnableAsync
|
|||
|
|
public class XinliApplication
|
|||
|
|
{
|
|||
|
|
@Bean(name = "taskExecutor")
|
|||
|
|
public Executor taskExecutor()
|
|||
|
|
{
|
|||
|
|
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
|
|||
|
|
executor.setCorePoolSize(5); // 核心线程数
|
|||
|
|
executor.setMaxPoolSize(10); // 最大线程数
|
|||
|
|
executor.setQueueCapacity(100); // 队列容量
|
|||
|
|
executor.setThreadNamePrefix("async-import-");
|
|||
|
|
executor.setWaitForTasksToCompleteOnShutdown(true);
|
|||
|
|
executor.setAwaitTerminationSeconds(60);
|
|||
|
|
executor.initialize();
|
|||
|
|
return executor;
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
### 5. 配置文件修改 ✅
|
|||
|
|
**文件**: `ry-xinli-admin/src/main/resources/application.yml`
|
|||
|
|
|
|||
|
|
**修改内容**:
|
|||
|
|
- 增加MVC异步请求超时配置(作为保险)
|
|||
|
|
|
|||
|
|
```yaml
|
|||
|
|
spring:
|
|||
|
|
mvc:
|
|||
|
|
async:
|
|||
|
|
# 异步请求超时时间(毫秒),设置为10分钟
|
|||
|
|
request-timeout: 600000
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
## 工作原理
|
|||
|
|
|
|||
|
|
1. **文件上传**: 用户上传Excel文件
|
|||
|
|
2. **解析数据**: Controller解析Excel文件为对象列表
|
|||
|
|
3. **启动异步任务**: 调用 `importProfileAsync` 方法,Spring会在独立线程中执行
|
|||
|
|
4. **立即返回**: HTTP请求立即返回成功响应
|
|||
|
|
5. **进度管理**: 后台线程执行导入,通过 `ImportProgressManager` 记录进度
|
|||
|
|
6. **前端轮询**: 前端通过 `/importProgress` 接口定期查询导入进度
|
|||
|
|
7. **完成通知**: 导入完成后,前端获取到最终结果并显示
|
|||
|
|
|
|||
|
|
## 优势
|
|||
|
|
|
|||
|
|
✅ **避免超时**: HTTP请求立即返回,不会因导入时间长而超时
|
|||
|
|
✅ **用户体验好**: 前端实时显示导入进度
|
|||
|
|
✅ **不阻塞服务器**: 导入任务在独立线程池中执行
|
|||
|
|
✅ **支持大批量**: 可以处理大量数据而不影响其他请求
|
|||
|
|
✅ **向后兼容**: 保留了同步导入方法,不影响其他功能
|
|||
|
|
|
|||
|
|
## 注意事项
|
|||
|
|
|
|||
|
|
⚠️ **重启应用**: 修改完成后需要重启Spring Boot应用才能生效
|
|||
|
|
⚠️ **数据库连接**: 异步线程需要数据库连接,确保连接池配置足够
|
|||
|
|
⚠️ **事务管理**: 异步方法中的事务与主线程独立,需注意事务边界
|
|||
|
|
⚠️ **线程池监控**: 生产环境建议监控线程池状态,避免资源耗尽
|
|||
|
|
|
|||
|
|
## 如何测试
|
|||
|
|
|
|||
|
|
1. 重启Spring Boot应用
|
|||
|
|
2. 准备一个包含较多数据的Excel文件(建议500+条)
|
|||
|
|
3. 在用户档案管理页面点击"导入"
|
|||
|
|
4. 上传文件后,应该立即收到"导入任务已启动"的提示
|
|||
|
|
5. 观察进度条实时更新
|
|||
|
|
6. 等待导入完成,查看结果统计
|
|||
|
|
|
|||
|
|
## 进一步优化建议(可选)
|
|||
|
|
|
|||
|
|
如果仍然遇到问题,可以考虑:
|
|||
|
|
|
|||
|
|
1. **增加Nginx超时配置** (如果使用了Nginx反向代理):
|
|||
|
|
```nginx
|
|||
|
|
proxy_connect_timeout 600s;
|
|||
|
|
proxy_send_timeout 600s;
|
|||
|
|
proxy_read_timeout 600s;
|
|||
|
|
```
|
|||
|
|
|
|||
|
|
2. **优化批量插入性能**:
|
|||
|
|
- 已实现分批处理(每批500条)
|
|||
|
|
- 可以根据实际情况调整批次大小
|
|||
|
|
|
|||
|
|
3. **数据库优化**:
|
|||
|
|
- 确保相关字段有索引
|
|||
|
|
- 检查批量插入SQL性能
|
|||
|
|
|
|||
|
|
---
|
|||
|
|
|
|||
|
|
**修复完成日期**: 2025-12-01
|
|||
|
|
**修复人员**: Cascade AI
|
|||
|
|
**影响范围**: 用户档案导入功能
|
|||
|
|
**风险评估**: 低(仅改变执行方式,核心逻辑未变)
|