最近写的业务接口由于底层再次调了数仓的接口(DataWorks),接口很慢。而对于同个浏览器和同个请求地址来说,多次访问接口,SpringBoot也只会在单线程执行接口的业务逻辑(当然,用别的接口或者换个地址去调,Controller是多线程去处理的)。因此,这边准备改造成异步接口
异步接口模块
这边创建了一个子模块rycem-common_async_api,主要定义了一些异步注解和配置类
pom
<dependencies>
<!-- SpringBoot Web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- lombak-->
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
</dependency>
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-core</artifactId>
<version>3.0.0</version>
<scope>compile</scope>
</dependency>
</dependencies>
注解
@AsyncRycemApi
到时候要用在Service层的方法上,本质上就是包装了 @Async
package com.rycem.async_api.annotation;
import org.springframework.scheduling.annotation.Async;
import java.lang.annotation.*;
/**
* @ClassName AsyncRycemApi
* @Author 陈铭
* @Date 16:12 2023/4/21
* @Version 1.0
**/
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Async
public @interface AsyncRycemApi {
}
@EnableAsyncRycemApi
包装了 @EnableAsync
package com.rycem.async_api.annotation;
import org.springframework.scheduling.annotation.EnableAsync;
import java.lang.annotation.*;
/**
* @ClassName EnableAsyncRycemApi
* @Author 陈铭
* @Date 16:12 2023/4/21
* @Version 1.0
**/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@EnableAsync
public @interface EnableAsyncRycemApi {
}
配置类
package com.rycem.async_api.config;
import com.rycem.async_api.annotation.EnableAsyncRycemApi;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
/**
* @ClassName ExecutorPoolConfig
* @Author 陈铭
* @Date 16:11 2023/4/21
* @Version 1.0
**/
@Slf4j
@EnableAsyncRycemApi
@Configuration
public class ExecutorPoolConfig {
public static final Integer CORE_THREAD_NUM = 15;
public static final Integer MAX_THREAD_NUM = 100;
public static final Integer MAX_QUEUE_NUM = 1000;
@Bean
public TaskExecutor getTaskExecutor() {
ThreadPoolTaskExecutor pool = new ThreadPoolTaskExecutor();
pool.setCorePoolSize(CORE_THREAD_NUM); //核心线程数
pool.setMaxPoolSize(MAX_THREAD_NUM); //最大线程数
pool.setQueueCapacity(MAX_QUEUE_NUM); //线程队列
pool.initialize(); //线程初始化
log.info("【异步API线程池加载完毕】");
return pool;
}
}
使用异步接口注解
pom
在需要使用异步接口的模块pom中,加入依赖
<dependency>
<groupId>cn.com.ruijie.rycem</groupId>
<artifactId>rycem-common_async_api</artifactId>
</dependency>
使用@AsyncRycemApi
在service层的对应方法加上注解即可
@Service
@RequiredArgsConstructor
public class CasesysServiceImpl implements CasesysService {
@AsyncRycemApi
public Future<ResultBody<CustomerOrderListBody>> getCustomerOrderList(CustomerOrderListApiQuery customerOrderListQuery) {
ResultBody<CustomerOrderListBody> body=xxx;
return AsyncResult.forValue(body);
}
}
而Futrue包装的结果可以在Controller层拿出
@PrometheusMetrics、@RycemApiExceptionListen是我自己实现得AOP,输出Prometheus监控和处理RycemApiException异常得
@GetMapping("xxxx")
@PrometheusMetrics
@RycemApiExceptionListen
public ResultBody<CustomerOrderListBody> getCustomerOrderList(@Validated CustomerOrderListApiQuery customerOrderListQuery) {
try {
return casesysOrderService.getCustomerOrderList(customerOrderListQuery).get();
} catch (InterruptedException | ExecutionException e) {
throw RycemApiException.getAsyncException();
}
}
评论区