方式一、使用异步注解@aysnc
用法1、
1.在启动类或者Controller类加上@EnableAsync注解
/**
* @EnableAsync注解可以开启多线程,
* 可标注在方法、类上
*/
@SpringBootApplication
@EnableAsync
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.在需要进行异步处理的方法上加@Async注解
/**
* 无参返回方法
* 使用@Async注解的类需要是spring管理的类
*/
@Async
public void testAsync() {
long startTime = System.currentTimeMillis();
try {
//模拟耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
System.out.println(Thread.currentThread().getName() + ":void testAsync1(),耗时:" +
(endTime - startTime));
}
/**
* 有参返回方法
* 使用@Async注解的类需要是spring管理的类
*/
@Override
@Async
public Future<String> testAsync2() {
long startTime = System.currentTimeMillis();
try {
//模拟耗时
Thread.sleep(3000);
} catch (InterruptedException e) {
e.printStackTrace();
}
long endTime = System.currentTimeMillis();
return new AsyncResult<>("testAsync4:耗时:" + (endTime - startTime));
}
用法2
通用配置类
import java.util.concurrent.ThreadPoolExecutor;
/**
* 异步任务通用配置类
*/
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 设置核心线程数
executor.setCorePoolSize(5);
// 设置最大线程数
executor.setMaxPoolSize(10);
// 设置队列容量
executor.setQueueCapacity(20);
// 设置线程活跃时间(秒)
executor.setKeepAliveSeconds(60);
// 设置默认线程名称
executor.setThreadNamePrefix("Test-Async");
// 设置拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 等待所有任务结束后再关闭线程池
executor.setWaitForTasksToCompleteOnShutdown(true);
return executor;
}
}
方法上添加注解
@Component
public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {
private static final Logger logger = LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) throws Exception {
// HandlerMethod handlerMethod = (HandlerMethod) handler;
logger.info(Thread.currentThread().getName()+ "服务调用完成,返回结果给客户端");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex)
throws Exception {
if(null != ex){
System.out.println("发生异常:"+ex.getMessage());
}
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request, HttpServletResponse response, Object handler)
throws Exception {
// 拦截之后,重新写回数据,将原来的hello world换成如下字符串
String resp = "my name is chhliu!";
response.setContentLength(resp.length());
response.getOutputStream().write(resp.getBytes());
logger.info(Thread.currentThread().getName() + " 进入afterConcurrentHandlingStarted方法");
}
}
方式二、采用缓存方式存储部分固定数据
将部分热点数据或者静态数据放到本地缓存或者Redis中,如果有需要可以定时更新缓存数据
方式三、业务拆分
将比较耗时或者不同的业务拆分出来提供单节点的吞吐量
方式四、增加内嵌 Tomcat 的最大连接数
@Configuration
public class TomcatConfig {
@Bean
public ConfigurableServletWebServerFactory webServerFactory() {
TomcatServletWebServerFactory tomcatFactory = new TomcatServletWebServerFactory();
tomcatFactory.addConnectorCustomizers(new MyTomcatConnectorCustomizer());
tomcatFactory.setPort(8005);
tomcatFactory.setContextPath("/api-g");
return tomcatFactory;
}
class MyTomcatConnectorCustomizer implements TomcatConnectorCustomizer {
public void customize(Connector connector) {
Http11NioProtocol protocol = (Http11NioProtocol)
connector.getProtocolHandler();
//设置最大连接数
protocol.setMaxConnections(20000);
//设置最大线程数
protocol.setMaxThreads(2000);
protocol.setConnectionTimeout(30000);
}
}
}
方式五、使用 @ComponentScan()
使用 @ComponentScan() 定位扫包比 @SpringBootApplication 扫包更快
方式六、默认 Tomcat 容器改为 Undertow
默认 Tomcat 容器改为 Undertow。
Undertow是Jboss 下的服务器,Tomcat 吞吐量 5000,Undertow 吞吐量 8000。
<!-- tomcat容器 -->
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
<!-- undertow容器 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
方式七、Deferred 方式实现异步调用
@RestController
public class AsyncDeferredController {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
private final LongTimeTask taskService;
@Autowired
public AsyncDeferredController(LongTimeTask taskService) {
this.taskService = taskService;
}
@GetMapping("/deferred")
public DeferredResult<String> executeSlowTask() {
logger.info(Thread.currentThread().getName() + "进入executeSlowTask方法");
DeferredResult<String> deferredResult = new DeferredResult<>();
// 调用长时间执行任务
taskService.execute(deferredResult);
// 当长时间任务中使用deferred.setResult("world");
// 这个方法时,会从长时间任务中返回,继续controller里面的流程
logger.info(Thread.currentThread().getName() + "从executeSlowTask方法返回");
// 超时的回调方法
deferredResult.onTimeout(new Runnable(){
@Override
public void run() {
logger.info(Thread.currentThread().getName() + " onTimeout");
// 返回超时信息
deferredResult.setErrorResult("time out!");
}
});
// 处理完成的回调方法,无论是超时还是处理成功,都会进入这个回调方法
deferredResult.onCompletion(new Runnable(){
@Override
public void run() {
logger.info(Thread.currentThread().getName() + " onCompletion");
}
});
return deferredResult;
}
}
方式八、异步调用可以使用 AsyncHandlerInterceptor 进行拦截
新建拦截器类,代码如下
@Component
public class MyAsyncHandlerInterceptor implements AsyncHandlerInterceptor {
private static final Logger logger =
LoggerFactory.getLogger(MyAsyncHandlerInterceptor.class);
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response,
Object handler) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response,
Object handler,ModelAndView modelAndView) throws Exception {
// HandlerMethod handlerMethod = (HandlerMethod) handler;
logger.info(Thread.currentThread().getName()+ "服务调用完成,返回结果给客户端");
}
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
if(null != ex){
System.out.println("发生异常:"+ex.getMessage());
}
}
@Override
public void afterConcurrentHandlingStarted(HttpServletRequest request,
HttpServletResponse response, Object handler) throws Exception {
// 拦截之后,重新写回数据,将原来的hello world换成如下字符串
String resp = "my name is chhliu!";
response.setContentLength(resp.length());
response.getOutputStream().write(resp.getBytes());
logger.info(Thread.currentThread().getName() + " 进入afterConcurrentHandlingStarted方法");
}
}