一、前言
在我们的实际生产中,常常会遇到下面的这种情况(例如JMS,定时任务,队列、下载等),某个请求非常耗时(大约5s返回),当大量的访问该请求的时候,再请求其他服务时,会造成没有连接使用的情况,造成这种现象的主要原因是,我们的容器(tomcat)中线程的数量是一定的,例如500个,当这500个线程都用来请求服务的时候,再有请求进来,就没有多余的连接可用了,只能拒绝连接。
二、环境要求
servlet3.0(Tomcat7及以上)
三、响应步骤
- 客户端发送一个请求
- Servlet容器分配一个线程来处理容器中的一个servlet
- Servlet调用request.startAsync(),保存AsyncContext, 然后返回
- 任何方式存在的容器线程都将退出,但是response仍然保持开放
- 其他线程使用保存的AsyncContext来完成响应
- 客户端收到响应
四、配置类
package com.example.demo.config;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.AsyncTaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
@Configuration
@EnableAsync
public class AsyncExecutorPoolConfiguration {
private static final int MAX_POOL_SIZE = 50;
private static final int CORE_POOL_SIZE = 10;
@Bean("asyncTaskExecutor")
public AsyncTaskExecutor asyncTaskExecutor() {
ThreadPoolTaskExecutor asyncTaskExecutor = new ThreadPoolTaskExecutor();
asyncTaskExecutor.setMaxPoolSize(MAX_POOL_SIZE);
asyncTaskExecutor.setCorePoolSize(CORE_POOL_SIZE);
asyncTaskExecutor.setQueueCapacity(Integer.MAX_VALUE);
asyncTaskExecutor.setAllowCoreThreadTimeOut(true);
asyncTaskExecutor.setKeepAliveSeconds(60);
asyncTaskExecutor.setThreadNamePrefix("async-task-thread-pool-");
asyncTaskExecutor.initialize();
}
}
五、示例
package com.example.demo.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.context.request.async.WebAsyncTask;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;
@RestController
public class AsyncController {
@RequestMapping("/asyncUpload")
public WebAsyncTask<String> asyncUpload(){
// 模拟上传文件
WebAsyncTask result = new WebAsyncTask(30000L, "asyncTaskExecutor" , () -> {
System.out.println(Thread.currentThread().getName());
try {
TimeUnit.SECONDS.sleep(20);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "success";
});
result.onTimeout(() -> {
throw new TimeoutException("调用超时!");
});
return result;
}
}
六、典型应用场景
使用方法:
Callable<String>、DefferredResult<String>、WebAsyncTask<String>及手动创建线程
1.使用场景:导出报表使用独立线程,不影响业务系统正常使用
// springMVC开启一个Callable新线程,利用TaskExecutor管理新产生的线程。
@RequestMapping(method=RequestMethod.POST)
public Callable<String> processUpload(final MultipartFile file) {
return new Callable<String>() {
public Object call() throws Exception {
// ...
return "someView";
}
};
}
2.订单创建:调用其它模块任务,使用中间件消息队列。
@RequestMapping("/quotes")
@ResponseBody
public DeferredResult quotes() {
DeferredResult deferredResult = new DeferredResult();
// Add deferredResult to a Queue or a Map...
return deferredResult;
}
// In some other thread..
// Set the return value on the deferredResult
deferredResult.set(data);
七、与shiro认证整合
@Bean
public FilterRegistrationBean shiroFilterRegistration() {
FilterRegistrationBean registration = new FilterRegistrationBean();
registration.setFilter(new DelegatingFilterProxy("shiroFilter"));
//该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由ServletContainer管理
registration.addInitParameter("targetFilterLifecycle", "true");
registration.setEnabled(true);
registration.setOrder(Integer.MAX_VALUE - 1);
registration.addUrlPatterns("/*");
//支持异步
registration.setAsyncSupported(true);
registration.setDispatcherTypes(DispatcherType.REQUEST,DispatcherType.ASYNC);
return registration;
}