SpringBoot异步实现DeferredResult实战与原理分析

黑白搬砖工 正在上传…重新上传取消

2021年03月02日 16:02 ·  阅读 3540

关注

1.DeferredResult示例

1.1 创建实例对象

DeferredResult<ResponseEntity<List<User>>> deferredResult =
                new DeferredResult<>(20000L, new ResponseEntity<>(HttpStatus.NOT_MODIFIED));
复制代码

1.2 设置回调

deferredResult.onTimeout(() -> {
    log.info("调用超时");
});

deferredResult.onCompletion(() -> {
    log.info("调用完成");
});
复制代码

1.3 设置结果

new Thread(() -> {
    try {
        TimeUnit.SECONDS.sleep(10);
        deferredResult.setResult(new ResponseEntity<>(userService.listUser(), HttpStatus.OK));
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
}).start();
复制代码

1.4 完整示例

@GetMapping("/deferredResultUser")
public DeferredResult<ResponseEntity<List<User>>> deferredResultListUser() {
    DeferredResult<ResponseEntity<List<User>>> deferredResult =
        new DeferredResult<>(20000L, new ResponseEntity<>(HttpStatus.NOT_MODIFIED));
    deferredResult.onTimeout(() -> {
        log.info("调用超时");
    });

    deferredResult.onCompletion(() -> {
        log.info("调用完成");
    });

    new Thread(() -> {
        try {
            TimeUnit.SECONDS.sleep(10);
            deferredResult.setResult(new ResponseEntity<>(userService.listUser(), HttpStatus.OK));
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }).start();
    return deferredResult;
}
复制代码

客户端请求映射到控制器方法返回值为DeferredResult时,会立即释放Tomcat线程并将请求挂起,直到调用setResult()方法或者超时,才会响应客户端请求。

2.DeferredResult原理分析

2.1 返回值处理器处理返回值

控制器方法的返回值都由对应的处理器进行处理,关于DeferredResult,自然由DeferredResultMethodReturnValueHandler进行处理。

@Override
public void handleReturnValue(@Nullable Object returnValue, MethodParameter returnType,
                              ModelAndViewContainer mavContainer, NativeWebRequest webRequest) throws Exception {

    DeferredResult<?> result;

    // 返回值类型为DeferredResult
    if (returnValue instanceof DeferredResult) {
        result = (DeferredResult<?>) returnValue;
    }
    
    // 返回值类型为ListenableFuture进行适配转换
    else if (returnValue instanceof ListenableFuture) {
        result = adaptListenableFuture((ListenableFuture<?>) returnValue);
    } 
    
    // 返回值类型为CompletionStage进行适配转换
    else if (returnValue instanceof CompletionStage) {
        result = adaptCompletionStage((CompletionStage<?>) returnValue);
    } else {
        // Should not happen...
        throw new IllegalStateException("Unexpected return value type: " + returnValue);
    }

    // 处理DeferredResult
    WebAsyncUtils.getAsyncManager(webRequest).startDeferredResultProcessing(result, mavContainer);
}
复制代码

2.2 设置DeferredResultHandler

public void startDeferredResultProcessing(
   final DeferredResult<?> deferredResult, Object... processingContext) throws Exception {
    // 1. 开启异步处理
    startAsyncProcessing(processingContext);

    try {
        // 2. 设置DeferredResultHandler
        deferredResult.setResultHandler(result -> {
            result = interceptorChain.applyPostProcess(this.asyncWebRequest, deferredResult, result);
            setConcurrentResultAndDispatch(result);
        });
    }
    catch (Throwable ex) {
        setConcurrentResultAndDispatch(ex);
    }
}
复制代码

2.3 调用setResult()

当我们的异步任务执行完成后,会调用DeferredResultsetResult()方法

public boolean setResult(T result) {
    return setResultInternal(result);
}
复制代码
private boolean setResultInternal(Object result) {
    DeferredResultHandler resultHandlerToUse;
    synchronized (this) {
        // At this point, we got a new result to process
        this.result = result;
        resultHandlerToUse = this.resultHandler;
        if (resultHandlerToUse == null) {
            return true;
        }
        this.resultHandler = null;
    }
    // 调用DeferredResultHandler的handleResult()方法
    resultHandlerToUse.handleResult(result);
    return true;
}
复制代码

2.2章节设置了DeferredResultHandler,因此会调用setConcurrentResultAndDispatch()

private void setConcurrentResultAndDispatch(Object result) {
    synchronized (WebAsyncManager.this) {
        if (this.concurrentResult != RESULT_NONE) {
            return;
        }
        // 1.设置结果
        this.concurrentResult = result;
        this.errorHandlingInProgress = (result instanceof Throwable);
    }

    if (this.asyncWebRequest.isAsyncComplete()) {
        if (logger.isDebugEnabled()) {
            logger.debug("Async result set but request already complete: " + formatRequestUri());
        }
        return;
    }

    if (logger.isDebugEnabled()) {
        boolean isError = result instanceof Throwable;
        logger.debug("Async " + (isError ? "error" : "result set") + ", dispatch to " + formatRequestUri());
    }
    // 2.请求调度,就是模拟客户端再次向服务器端发起请求
    this.asyncWebRequest.dispatch();
}
复制代码

2.4 调度处理

@Nullable
protected ModelAndView invokeHandlerMethod(HttpServletRequest request,
                                           HttpServletResponse response, HandlerMethod handlerMethod) throws Exception {

    ServletWebRequest webRequest = new ServletWebRequest(request, response);
    try {
        // 在2.3章节设置了结果,因此该逻辑返回true
        if (asyncManager.hasConcurrentResult()) {
            Object result = asyncManager.getConcurrentResult();
            mavContainer = (ModelAndViewContainer) asyncManager.getConcurrentResultContext()[0];
            asyncManager.clearConcurrentResult();
            LogFormatUtils.traceDebug(logger, traceOn -> {
                String formatted = LogFormatUtils.formatValue(result, !traceOn);
                return "Resume with async result [" + formatted + "]";
            });
            invocableMethod = invocableMethod.wrapConcurrentResult(result);
        }

        invocableMethod.invokeAndHandle(webRequest, mavContainer);
        if (asyncManager.isConcurrentHandlingStarted()) {
            return null;
        }

        return getModelAndView(mavContainer, modelFactory, webRequest);
    }
    finally {
        webRequest.requestCompleted();
    }
}
复制代码

2.5 构建ServletInvocableHandlerMethod

ServletInvocableHandlerMethod wrapConcurrentResult(Object result) {
    return new ConcurrentResultHandlerMethod(result, new ConcurrentResultMethodParameter(result));
}
复制代码
public ConcurrentResultHandlerMethod(final Object result, ConcurrentResultMethodParameter returnType) {
    // 创建Callable对象并指定调用call()方法
    super((Callable<Object>) () -> {
        if (result instanceof Exception) {
            throw (Exception) result;
        }
        else if (result instanceof Throwable) {
            throw new NestedServletException("Async processing failed", (Throwable) result);
        }
        return result;
    }, CALLABLE_METHOD);

    if (ServletInvocableHandlerMethod.this.returnValueHandlers != null) {
        setHandlerMethodReturnValueHandlers(ServletInvocableHandlerMethod.this.returnValueHandlers);
    }
    this.returnType = returnType;
}
复制代码

如上逻辑创建了Callable对象并指定调用call()方法

2.6 调用目标方法

调用Callable的call()方法,得到返回结果,再使用返回值处理器处理返回值

3. 总结

  • 控制器中定义方法返回值为DeferredResult,会立即释放Tomcat线程,使用业务线程处理业务

  • DeferredResultMethodReturnValueHandler处理返回结果,开启异步处理并设置DeferredResultHandler

  • 业务执行完成后调用setResult()方法,紧接着回调DeferredResultHandlerhandleResult()

  • 设置结果并调度请求

  • 创建Callable对象并设置调用方法为call()

  • 通过反射方式调用call()得到返回值

  • 使用返回值处理器处理返回值

     

  • 0
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值