Jhispter项目长耗时接口返回net::ERR_EMPTY_RESPONSE问题求助
net::ERR_EMPTY_RESPONSE问题 Hey there! Let's tackle this timeout issue you're facing with your JHipster app—super common when dealing with long-running backend tasks, so you're not alone here. The net::ERR_EMPTY_RESPONSE error happens because browsers and servers have default timeout limits; if your port-checking task takes too long to finish, the connection gets dropped before it can send a response. Even using basic async methods might not fix it if you're still making the frontend wait for the full task to complete.
Here are the most effective solutions, ordered by how robust they are:
1. 改用「异步任务+前端轮询」模式(最优方案)
Instead of making the frontend wait for the entire 1023-port check to finish, split the process into two parts:
- Backend: Accept the request, kick off the port-check task in the background, and immediately return a unique task ID.
- Frontend: Use this task ID to periodically poll the backend for the task's status/results until it's done.
后端示例代码(Spring Boot)
First, create a task manager to track ongoing tasks:
// 任务状态枚举 public enum TaskStatus { PENDING, RUNNING, COMPLETED, FAILED } // 任务结果封装类 public class PortCheckResult { private String taskId; private TaskStatus status; private List<Integer> openPorts; // 存储检测到的开放端口 private String errorMsg; // Getters & Setters } // 任务管理器组件 @Component public class TaskManager { private final Map<String, PortCheckResult> taskStore = new ConcurrentHashMap<>(); private final TaskExecutor taskExecutor; @Autowired public TaskManager(TaskExecutor taskExecutor) { this.taskExecutor = taskExecutor; } // 提交端口检测任务 public String submitPortCheckTask() { String taskId = UUID.randomUUID().toString(); PortCheckResult result = new PortCheckResult(); result.setTaskId(taskId); result.setStatus(TaskStatus.RUNNING); taskStore.put(taskId, result); // 异步执行端口检测 taskExecutor.execute(() -> { List<Integer> openPorts = new ArrayList<>(); try { // 优化:并行检测端口,大幅缩短耗时 IntStream.rangeClosed(1, 1023) .parallel() .forEach(port -> { if (isPortOpen("localhost", port)) { openPorts.add(port); } }); result.setOpenPorts(openPorts); result.setStatus(TaskStatus.COMPLETED); } catch (Exception e) { result.setErrorMsg(e.getMessage()); result.setStatus(TaskStatus.FAILED); } }); return taskId; } // 查询任务结果 public PortCheckResult getTaskResult(String taskId) { return taskStore.get(taskId); } // 端口检测逻辑 private boolean isPortOpen(String host, int port) { try (Socket socket = new Socket()) { socket.connect(new InetSocketAddress(host, port), 100); // 100ms超时 return true; } catch (IOException e) { return false; } } }
Then add the controller endpoints:
@RestController @RequestMapping("/api") public class PortCheckController { private final TaskManager taskManager; @Autowired public PortCheckController(TaskManager taskManager) { this.taskManager = taskManager; } // 启动端口检测任务 @GetMapping("/ping/start") public ResponseEntity<String> startPortCheck() { String taskId = taskManager.submitPortCheckTask(); return ResponseEntity.ok(taskId); } // 查询任务结果 @GetMapping("/ping/result/{taskId}") public ResponseEntity<PortCheckResult> getTaskResult(@PathVariable String taskId) { PortCheckResult result = taskManager.getTaskResult(taskId); if (result == null) { return ResponseEntity.notFound().build(); } return ResponseEntity.ok(result); } }
前端示例代码(Angular)
Use RxJS to handle polling:
import { Component } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { interval, Subscription } from 'rxjs'; import { switchMap } from 'rxjs/operators'; @Component({ selector: 'app-port-check', template: ` <button (click)="startCheck()" [disabled]="isChecking">开始端口检测</button> <div *ngIf="status">{{ status }}</div> <div *ngIf="openPorts">开放端口:{{ openPorts.join(', ') }}</div> ` }) export class PortCheckComponent { isChecking = false; status = ''; openPorts: number[]; private pollSub: Subscription; constructor(private http: HttpClient) {} startCheck() { this.isChecking = true; this.http.get<string>('/api/ping/start').subscribe(taskId => { this.status = '检测中...'; // 每2秒轮询一次结果 this.pollSub = interval(2000).pipe( switchMap(() => this.http.get(`/api/ping/result/${taskId}`)) ).subscribe(res => { this.status = res.status; if (res.status === 'COMPLETED') { this.openPorts = res.openPorts; this.pollSub.unsubscribe(); this.isChecking = false; } else if (res.status === 'FAILED') { this.status = `检测失败:${res.errorMsg}`; this.pollSub.unsubscribe(); this.isChecking = false; } }); }); } }
2. 临时调整超时配置(应急方案)
If you need a quick fix without changing the workflow, you can increase timeout limits on both ends—but note that browsers still have hard limits (usually around 5-10 minutes), so this isn't a long-term solution.
- Spring Boot: Add these to
application.properties:# Tomcat连接超时(毫秒) server.tomcat.connection-timeout=300000 # 请求超时 spring.mvc.async.request-timeout=300000 - Angular: Set timeout on HttpClient requests:
this.http.get('/api/ping/{a}/{b}', { timeout: 300000 }) // 5分钟超时 .subscribe(...);
3. 优化后端端口检测逻辑
Your 1023-port loop is slow because it's running sequentially. The parallel processing in the first solution already fixes this, but you can also tweak the port check itself:
- Reduce the connection timeout for each port (like 100ms instead of the default)
- Use a thread pool to handle multiple port checks at once
- Skip well-known closed ports to reduce unnecessary checks
内容的提问来源于stack exchange,提问作者user7199973

