马上注册,结交更多好友,享用更多功能,让你轻松玩转社区。
您需要 登录 才可以下载或查看,没有账号?立即注册
×
配景
迩来在一个汗青遗留项目标重构工作中,遇到如许的一个需求“部分处置惩罚耗时高的接口必要做线程池隔离,制止大量耗时高的哀求影响现有线程池工作”;大抵结果如下
条件
SpringBoot 中现在支持 webflux 同样可以接口哀求异步处置惩罚,但是这些都不是基于 servlet 实现,原有业务代码中有较多利用 servlet 的地方,改为 webflux 必要做较大业务代码改动,临时不思量这个方案,而是接纳 SpringBoot 内嵌 tomcat 开启异步的方案;
改造方法
着实比力简单,比方按照以下写法即可- // 原接口
- @PostMapping(value = "/mass/asycTest")
- public Resp asycTest() {
- return new Resp(UUID.randomUUID().toString(), ResponseCode.SUCCESS);
- }
- // 改造后接口
- @PostMapping(value = "/mass/asycTest")
- public WebAsyncTask<Resp> asycTest() {
- System.out.println("1-thread name: " + Thread.currentThread().getName());
- return new WebAsyncTask<>(() -> {
- System.out.println("2-thread name: " + Thread.currentThread().getName());
- return new Resp(UUID.randomUUID().toString(), ResponseCode.SUCCESS);
- });
- }
复制代码 必要注意的是,如果有利用过滤器,那么必要给过滤器也启用异步,比方下方代码的 asyncSupported = true- @WebFilter(urlPatterns = "/*", asyncSupported = true)
- @Order(1)
- public class HttpServletRequestWrapperFilter implements Filter {
- ......
- }
复制代码 否则有大概会出现如下非常- java.lang.IllegalStateException: Async support must be enabled on a servlet and for all filters involved in async request processing. This is done in Java code using the Servlet API or by adding "<async-supported>true</async-supported>" to servlet and filter declarations in web.xml.
- at org.springframework.util.Assert.state(Assert.java:79) ~[spring-core-6.2.2.jar:6.2.2]
- at org.springframework.web.context.request.async.StandardServletAsyncWebRequest.startAsync(StandardServletAsyncWebRequest.java:143) ~[spring-web-6.2.2.jar:6.2.2]
- at org.springframework.web.context.request.async.WebAsyncManager.startAsyncProcessing(WebAsyncManager.java:487) ~[spring-web-6.2.2.jar:6.2.2]
复制代码 这里的 WebAsyncTask 内里有其他实现接口异步的方法 onTimeout()、onError()、onCompletion() 这些回调方法可以供我们做其他事变,比方接口哀求返反相应后再删数据库数据等,如果 WebAsyncTask 内里提供的方法还是无法满意,可以试试以下几个类
异步相应类用法Callable比力纯粹的异步处置惩罚方式;接口返回 Callable 时,则会在异步线程池中调用这个 CallableListenableFuture在 spring 6.0 已经启用,保举利用 CompletableFuture,用于支持处置惩罚相应结果回调的场景CompletionStage和 ListenableFuture、CompletableFuture 类似,着实都支持回调,区别在于支持多重异步;WebAsyncTask在 Callable 的根本上支持超时时间、非常处置惩罚更多场景DeferredResult 必要等待得到结果的场景,比方第一次发哀求时,哀求到的接口相应结果是 DeferredResult,谁人这个哀求会不停等待,然后其他线程给这个 DeferredResult 设置真实的相应结果后才会把这个结果写归去给客户端;
比方我先买一张交通卡,内里没钱,必要等充钱进去这张交通卡才华用的有一个等待的过程;
ResponseBodyEmitter支持写多个相应结果给客户端如果上述异步相应对象都无法满意我们的场景,我们可以实现 HandlerMethodReturnValueHandler 接口来做自界说的相应结果处置惩罚;
线程池界说
在 SpringBoot 中,异步接口默认实验的线程池是 MvcSimpleAsyncTaskExecutor,这个类继承于 SimpleAsyncTaskExecutor,每次实验任务都会创建一个新的线程,开销非常大,这个类的内部着实也有提示我们不要用这个类。
如果利用的 JDK 版本是 21 或以上,可以设置 spring.threads.virtual.enabled = true 来开启捏造线程,如许利用默认的线程池也不会导致由于线程频仍创建烧毁带来的性能开销;如果利用 JDK 21 以下,可以利用以下方式自界说线程池;- @Configuration
- public class WebInterceptorConfig implements WebMvcConfigurer {
- @Override
- public void addInterceptors(InterceptorRegistry registry) {
- ......
- }
- @Override
- public void configureAsyncSupport(AsyncSupportConfigurer configurer) {
- configurer.setTaskExecutor(restMoExecutor());
- }
- /**
- * 线程池的配置参数需要另外更具实际情况配置
- **/
- public AsyncTaskExecutor restMoExecutor() {
- ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
- executor.setCorePoolSize(5);
- executor.setMaxPoolSize(10);
- executor.setQueueCapacity(25);
- executor.setKeepAliveSeconds(60);
- executor.setThreadNamePrefix("rest-mo-executor");
- executor.initialize();
- return executor;
- }
- }
复制代码
源码分析
在实现接口异步功能的时间,通过 spring 的源码可以看到,着实是在处置惩罚返回值时对返回的范例做了判断,比方上方边表中提的 Callable、WebAsyncTask 等都有对应的 HandlerMethodReturnValueHandler 来处置惩罚;
我们进入到 startCallableProcessing 中可以看到提交了一个任务给线程池来实验。
|