我有一个要求,当我收到请求时,我想立即发送 status=“in-progress”,然后调用另一个 API 来获取响应,这大约需要一分钟才能返回结果。我正在使用具有 30 秒硬超时的 AWS API Gateway。因此,我想每 10 秒调用一次 API 来轮询前端应用程序的结果,直到我们收到 API 的响应。我的下面的代码异步工作并获取记录,但它从未在 deferredResult 中设置响应。因此,即使我们收到第三方 API 的响应,异步调用也会继续进行,因为我们在前端显示超时之前设置了 10 次迭代。
哪里不对请指正。
@EnableAsync
@RestController
@Slf4j
@RequestMapping("/api")
public class MyController {
private final ThridPartyApi thridPartyApi;
private MyRequest asyncRequest;
@PostMapping("/big-data")
@ResponseStatus(HttpStatus.OK)
public DeferredResult<AsyncApiResponse<MyResponse>> getBigData(@RequestBody MyRequest request) {
if (asyncRequest == null) {
asyncRequest = request;
}
DeferredResult<AsyncApiResponse<MyResponse>> deferredResult = new DeferredResult<>(80000L);
RequestAttributes requestAttributes = RequestContextHolder.currentRequestAttributes();
CompletableFuture.supplyAsync(() -> {
RequestContextHolder.setRequestAttributes(requestAttributes);
try {
return thridPartyApi.getBigData(request);
} catch (IOException e) {
throw new RuntimeException(e);
}
}).thenAccept(response -> {
if (!deferredResult.isSetOrExpired()) {
deferredResult.setResult(AsyncApiResponse.createCompleted(response));
}
}).exceptionally(ex -> {
if (!deferredResult.isSetOrExpired()) {
deferredResult.setErrorResult(AsyncApiResponse.createCompleted(null));
}
return null;
});
if (!deferredResult.hasResult()) {
log.debug("Request processing is in progress >>>>>");
DeferredResult<AsyncApiResponse<MyResponse>> dr = new DeferredResult<>();
dr.setResult(AsyncApiResponse.createInProgress());
return dr;
}
log.debug("Request processing is done");
return deferredResult;
}
}
我在 10 次循环中多次调用 POST /api/big-data。调用后我立即收到“进行中”响应。但即使所有呼叫(计数 10)都用尽,也永远得不到响应。 看起来像
deferredResult.setResult(AsyncApiResponse.createCompleted(response))
设置了结果,但是当涉及到 if (!deferredResult.hasResult())
行时,我发现 deferredResult 未设置。
我希望我发现它已设置,因此
if (!deferredResult.hasResult())
被评估为 false,并且 return deferredResult;
被设置值调用。
通过使用 Spring
DeferredResult
,HTTP 连接将保持打开状态,直到结果可用。但是,由于处理过程大约需要一分钟,并且 API 网关有 30 秒的超时时间,因此在返回结果之前连接将被关闭。
一个潜在的解决方案是使用轮询 API 端点。当向长处理
/big-data
API 发出 API 请求时,数据库中会存储一条带有状态的请求记录,调用第三方 API 完成后,数据会存储到数据库中,状态标记为已完成。前端可以轮询另一个状态 API 以检查状态。由于数据很大,可能会有另一个具有分页功能的 API 端点来获取数据。
另一种可能的解决方案是使用服务器发送的事件来通知前端处理完成。这样就可以避免轮询,然后前端可以根据通知查询结果。