1.场景分析
一个商城项目,发放优惠券时,进行判断条件合格后。根据设定好的参数去批量生成优惠券兑换码。此需要注意,生成的兑换码是可能非常多的,有可能上万。这个时候就需要去异步生成了。有以下四种方式:
方式 | 执行单元 | 资源占用 | 系统依赖 | 任务可追溯性 | 适用场景 |
---|---|---|---|---|---|
线程池异步 | 应用内线程 | 中等 | 无 | 困难 | 单服务,万级数据 |
消息队列 | 独立消费者服务 | 低(解耦) | 消息中间件 | 中等 | 分布式系统,十万级以上数据 |
独立脚本/服务 | 外部进程 | 高(IO优化) | 脚本语言环境 | 困难 | 超大规模,资源受限环境 |
分布式任务调度 | 集群节点 | 高 | 调度中心 | 优秀 | 集群环境,需动态扩缩容 |
在四种方式中,第一种方式足够我们场景的使用了。
2.基于线程池的异步任务(Spring @Async)
技术实现:
- 配置专用线程池:
通过自定义ThreadPoolTaskExecutor
,设定:- 核心线程数(
corePoolSize
) - 最大线程数(
maxPoolSize
) - 队列容量(
queueCapacity
) - 拒绝策略(如
CallerRunsPolicy
避免任务丢失)
- 核心线程数(
- 异步方法调用:
在生成兑换码的服务方法上添加@Async("XXXXXX")
注解,触发后台线程执行
示例代码
线程池配置代码
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;
@Slf4j
@Configuration
public class PromotionConfig {
@Bean
public Executor generateExchangeCodeExecutor(){
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 1.核心线程池大小
executor.setCorePoolSize(2);
// 2.最大线程池大小
executor.setMaxPoolSize(5);
// 3.队列大小
executor.setQueueCapacity(200);
// 4.线程名称
executor.setThreadNamePrefix("exchange-code-handler-");
// 5.拒绝策略
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
executor.initialize();
return executor;
}
}
同时,在启动类上添加@EnableAsync
注解,开启异步功能
业务代码
private final IExchangeCodeService codeService;
@Transactional
@Override
public void beginIssue(CouponIssueFormDTO dto) {
// 1.查询优惠券
// 2.判断优惠券状态,是否是暂停或待发放
// 3.判断是否是立刻发放
// 4.更新优惠券
// 4.1.拷贝属性到PO
// 4.2.更新状态
// 4.3.写入数据库
// 5.判断是否需要生成兑换码,优惠券类型必须是兑换码,优惠券状态必须是待发放
if(coupon.getObtainWay() == ObtainType.ISSUE && coupon.getStatus() == CouponStatus.DRAFT){
coupon.setIssueEndTime(c.getIssueEndTime());
// 调用异步方法,生成优惠券兑换码
codeService.asyncGenerateCode(coupon);
}
}
import org.springframework.scheduling.annotation.Async;
@Service
public class ExchangeCodeServiceImpl extends ServiceImpl<ExchangeCodeMapper, ExchangeCode> implements IExchangeCodeService {
@Override
@Async("generateExchangeCodeExecutor") // 这里面填的就是配置类中配置线程池的方法名称
public void asyncGenerateCode(Coupon coupon) {
// 发放数量
// 1.获取Redis自增序列号
// 2.生成兑换码
// 3.保存数据库
// 4.写入Redis缓存
}
}