常见的高并发方案
- 异步,削峰填谷
- 缓存,缓存相对稳定高频热点数据
- 并行,缩短业务响应时间
- 优化你的业务代码
- 限流和降级,保护你的核心服务在高并发下能正常工作
异步场景
关联业务的执行结果对主线程的返回结果没有直接影响或无影响。此时,能让主线程更顺畅的执行,并给客户带来好的客户体验,我们一般会将该关联业务做异步处理或类似的处理(如:消息队列)
@Async
该工具可以为你的应用提供方便快捷的异步化执行业务的能力,只需要添加一个注解@Async
既可以使你的业务异步执行,这里的异步执行,指的是新开一个线程执行你的业务;该注解可以使用到类上,也可以使用在方法上。
-
@EnableAsync
启用异步化能力注解,推荐该注解配置在springboot的Config类或者启动类上 -
@Async
开启异步化模式注解 -
AsyncConfigurer
全局配置接口,用于配置自定义异步线程池和异步线程执行异常捕获器 -
AsyncUncaughtExceptionHandler
异步化运行时全局异常捕获接口 -
AsyncExecutor
异步化执行线程池,自定义异步执行线程池
注意事项
- SpringBootApplication启动类添加@EnableAsync注解;
- @Async使用
- 类或者方法中使用@Async注解,类上标有该注解表示类中方法都是异步方法,方法上标有该注解表示方法是异步方法
- @Async(“threadPool”),threadPool为自定义线程池,这样可以保证主线程中调用多个异步任务时能更高效的执行
- 在定义异步方法的同一个类中,调用带有
@Async
注解方法,该方法则无法异步执行 - 注解的方法必须是public方法,不能是static
- @Async注解的实现和@Transactional一样,都是基于Spring的AOP,而AOP的实现是基于动态代理模式实现的,所以要经过Spring容器管理,否则调用方法的是对象本身而不是代理对象
无参数异步化接口示例
@Async
public void executeTask(){
//业务操作
}
带参数异步化接口
@Async
public Future<T> task2(){
//业务操作,T代指实体类/Object
//返回操作结果
return new AsyncResult<>(new User("zhangsan", 22));
}
自定义线程池
@Configuration
@EnableAsync
public class ExecutorConfig {
private int corePoolSize = 10;
private int maxPoolSize = 50;
private int queueCapacity = 10;
@Bean
public Executor testAsync() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(corePoolSize);
executor.setMaxPoolSize(maxPoolSize);
executor.setQueueCapacity(queueCapacity);
executor.setThreadNamePrefix("MgmtExecutor-");
// rejection-policy:当pool已经达到max size的时候,如何处理新任务
// CALLER_RUNS:不在新线程中执行任务,而是有调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
使用
@Async("testAsync") // 自定义执行线程池
测试
100次调用,循环加一百减一百,并对计算时间随机处理,a最终结果应为6
controller
@PostMapping("/test")
public CommonResult test(@RequestBody ThirdPayParam param) {
for (int i = 0; i < 100; i++) {
testUtil.executeTask(i%2 == 0 ?100 : -100);
}
return CommonResult.success(null);
}
TestUtil
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
@Component
public class TestUtil {
public static int a = 6;
@Async
public void executeTask(int b){
System.out.println(a);
try {
Thread.sleep((long) ((Math.random() + 1) * 1000));
} catch (InterruptedException e) {
e.printStackTrace();
}
a = a + b;
System.out.println("最终结果" + a);
}
}
嗯,后续再补充