Springboot + DeferredResult
说明: DeferredResult内容网上资料很多,自己也可看源码分析,本篇仅记录个人在项目中使用DeferredResult的心得体会,以及它基本的使用情况。
博客内容精选:
1、Servlet请求体重复读&修改新姿势
2、根据请求获取后端接口详情
3、封装Springboot项目的starter-sdk新方式
4、Springboot全局处理完整版
5、itextpdf读取文本时上下行位置错乱
6、JAVA读取PDF表格内容
7、JAVA读取PDF出现内容错乱
背景: 项目需求需要接口同时支持同步、异步方式,且项目因模块化拆分,API接口端和实际处理端分开部署,中间以MQ作为传输通道,如下面所示:
因处理流程不在一个进程内,且同时支持同/异步,如果采用传统方法,适配代码及逻辑较为繁琐,代码也不美观,可读性较差。采用DeferredResult工具后,所有场景都可适配,仅简单判断分情况封装即可。
1、全局封装每个请求的DeferredResult
DeferredResult<ResponseEntity<Result>> deferredResult = new DeferredResult<>(60000, () -> {
// Result为个人定制类,按需自定义
Result timeoutRes = Result.newErrorResponse(requestId, "execute millisecond takes more than 60s");
// 超时状态码及请求体设置
return ResponseEntity.status(HttpStatus.SC_REQUEST_TIMEOUT).body(timeoutRes);
});
2、异步请求直接确认
deferredResult.setResult(ResponseEntity.ok(Result.newOkResponse(requestId, "")));
3、同步请求需要在内存中记录,以requestId为key,deferredResult为value
// Map缓存
HTTP_DEFERRED_RESULTS.put(requestId, deferredResult);
// 同步处理
try {
//此块为与其他模块对接内容,对接端默认是IO耗时操作,需要较多资源,此处直接封装后推送至队列即可
// 发送至队列后设置超时回调任务,推送失败不存在超时场景
deferredResult.onTimeout(() -> {
//因有内存记录,业务超时后清除所作必须存在,否则造成内除泄露,超时响应体前部已设置
HTTP_DEFERRED_RESULTS.remove(requestId);
});
} catch (ConvertException e) {
// 中间件连接失败清理内存记录,避免内存泄露
HTTP_DEFERRED_RESULTS.remove(requestId);
deferredResult.setResult(ResponseEntity.status(HttpStatus.SC_INTERNAL_SERVER_ERROR).body(Result.newErrorResponse(requestId, e.getMessage())));
}
总结:DeferredResult帮助项目应对多种涉及需求,并提供友好API实现对各种场景响应的封装。同步方式中仅需记录请求对应的DeferredResult,后续不再占用线程资源,极高提升了系统的吞吐量,不会被后端长IO操作拖累,是解耦优化的利器。有需要根据自身项目调整,可做备用小知识。