一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第2天,点击查看活动详情
序言
涉及此文档的服务为:
服务A:对外提供接口,所有的核心请求都由它来校验权限以及转发调用核心组件的核心功能;
服务B:一套Web组件服务,提供一系列的基本操作,包含日志记录;
核心组件:跟后面底层硬件做交互,满足服务A的调用;
应用场景:
由于所有的外部接口都是首先调用了服务A,并且在该服务上还校验了一些权限以及记录日志的功能,所以对于服务A的TPS有要求,一开始编写的时候,权限校验、应用转发、日志记录都是单独分开的,导致性能比较低下,然后优化了校验的顺序以及规则,所有的必要信息存储在Redis中,这样性能提升了一下,然后日志记录还是比较慢,所以打算使用异步调用进行日志记录。
@Async
- 说明
注解@Async是Spring 3.X之后增加的内置注解,主要用来解决异步调用的问题。
- 使用
- 需要在启动类上增加开启异步注解:
@EnableAsync; - 自定义一个线程池;(可有可无)
- 在需要异步调用的方法上面增加注解:
@Async("taskExecutorName")。
- 注意问题
- 如果没有在注解上面没有标注
taskExecutorName,则使用的SpringBoot默认线程池; - 如果调用同一个类的异步方法,则会导致异步调用失效;原因是在Spring中像@Async、@Transactional和@Cache等注解本质使用的是动态代理,在Spring容器初始化的时候容器会将含有AOP注解的类对象“替换”为代理对象,所以调用本身的方法时,调用的是对象本身而不是代理对象,因为没有经过Spring容器。
- 异步调用的方法使用了
static修饰,也会导致异步调用失败;
- 代码演示
- 启动类上增加开启异步注解;
@EnableAsync
@SpringBootApplication
public class SpdApplication {
public static void main(String[] args) {
// 启动代码
}
}
- 自定义线程池
@Configuration
public class ThreadPoolConfig {
public static final String THREADPOOL_TASKEXECUTOR_NAME = "threadPoolTaskExecutor";
@Bean(name = THREADPOOL_TASKEXECUTOR_NAME)
public ThreadPoolTaskExecutor threadPoolTaskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 核心线程池大小
executor.setMaxPoolSize(100);
// 最大可创建的线程数
executor.setCorePoolSize(200);
// 队列最大长度
executor.setQueueCapacity(1000);
// 线程池维护线程所允许的空闲时间
executor.setKeepAliveSeconds(200);
// 线程池对拒绝任务(无线程可用)的处理策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
return executor;
}
}
- 代码调用
@Async(ThreadPoolConfig.THREADPOOL_TASKEXECUTOR_NAME)
public void insertLog() {
// 业务操作
}
- 后记
因为同步调用,需要考虑后续服务的性能并且会影响当前主程序,所以使用异步调用的方式;
当服务要求性能的时候,并且每个服务本身之间 不存在依赖关系,可以 并发执行 的话,可以考虑通过上面的方式来执行。