handleReturnValue
中会有判断,如果异步的返回 Reactive 对象是同步完成态,比如 Mono 只有一个确定的结果,里面没有采用订阅的模式进行异步处理,或者 Future 是完结态,那么handleReturnValue
会直接同步返回结果,而不是异步处理。所以开发的时候要注意:
CompletableFuture.supplyAsync(()->{}, threadPool)
Publisher.subscribe
订阅的时候,启动异步线程threadPool.submit()->{ subscriber.onSubscribe(subscription); --> Subscription.request(x) --> subscriber.onNext(T); }
,在异步线程中onNext
时输出结果,才能确保在 Servlet 层面是异步的,非阻塞的。只有 Callable 用了简单线程池,存在线程问题,如果使用需要指定线程池,但也只有Callable使用最简单,return 即可。
另外,推荐采用DeferredResult和CompletableFuture类型的返回值,需要自己在线程池中处理业务并赋值结果。
package com.test;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.servlet.http.HttpServletResponse;
import lombok.extern.slf4j.Slf4j;
import org.reactivestreams.Publisher;
import org.reactivestreams.Subscriber;
import org.reactivestreams.Subscription;
import org.springframework.scheduling.annotation.EnableAsync;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.DeferredResult;
import reactor.core.publisher.Mono;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
@Slf4j
@RestController
@RequestMapping("/restapi/testAsync")
public class TestAsyncToolboxController {
private static final ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(10,
100,
60,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(10000),
new ThreadPoolExecutor.AbortPolicy());
@RequestMapping("/test")
public DeferredResult<Object> test(HttpServletRequest request, HttpServletResponse response) {
log.info("TestAsyncToolboxController.test 1 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
DeferredResult<Object> deferredResult = new DeferredResult<>();
threadPoolExecutor.submit(() -> {
try {
log.info("TestAsyncToolboxController.test 2 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
TimeUnit.SECONDS.sleep(5);
log.info("TestAsyncToolboxController.test 3 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
} catch (InterruptedException e) {
deferredResult.setResult("exception");
throw new RuntimeException(e);
}
log.info("TestAsyncToolboxController.test 4 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
deferredResult.setResult("success");
});
return deferredResult;
}
@RequestMapping("/test3")
public CompletableFuture<String> test3(HttpServletRequest request, HttpServletResponse response) {
log.info("TestAsyncToolboxController.test3 1 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
CompletableFuture<String> stringCompletableFuture = CompletableFuture.supplyAsync(() -> {
try {
log.info("TestAsyncToolboxController.test3 2 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
TimeUnit.SECONDS.sleep(5);
log.info("TestAsyncToolboxController.test3 3 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("TestAsyncToolboxController.test3 4 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
return "success";
}, threadPoolExecutor);
return stringCompletableFuture;
}
@RequestMapping("/test4")
public Mono<String> test4(HttpServletRequest request, HttpServletResponse response) {
log.info("TestAsyncToolboxController.test4 1 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
try {
return Mono.from(new Publisher<String>() {
@Override
public void subscribe(Subscriber<? super String> subscriber) {
threadPoolExecutor.submit(() -> {
try {
log.info("TestAsyncToolboxController.test4 2 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
TimeUnit.SECONDS.sleep(5);
log.info("TestAsyncToolboxController.test4 3 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
log.info("TestAsyncToolboxController.test4 4 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
Subscription subscription = new Subscription() {
@Override
public void request(long n) {
log.info("TestAsyncToolboxController.test4 7 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
subscriber.onNext("success");
}
@Override
public void cancel() {
log.info("TestAsyncToolboxController.test4 8 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
}
};
log.info("TestAsyncToolboxController.test4 10 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
subscriber.onSubscribe(subscription);
log.info("TestAsyncToolboxController.test4 9 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
});
log.info("TestAsyncToolboxController.test4 6 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
}
});
} finally {
log.info("TestAsyncToolboxController.test4 5 isAsyncStarted={}, isAsyncSupported={}", request.isAsyncStarted(), request.isAsyncSupported());
}
}
}
Servlet 的异步性能和 Reactor 的性能是否存在较大差异?为什么 Servlet 依然健在,且没有明显的被 Reactor 模式替换的迹象?
https://docs.spring.io/spring-framework/reference/web/webmvc/mvc-ann-async.html
https://blog.csdn.net/sinat_33543436/article/details/88971367