1.发短信
发短信的场景有很多,比如手机号+验证码登录注册,电影票买完之后会发送取票码,发货之后会有物流信息,支付之后银行发的付款信息,电力系统的电费预警信息等等
在这些业务场景中,有一个特征,那就是主业务可以和短信业务割裂,比如手机号+验证码登陆,当我们点击获取验证码的时候,会连接短信业务平台发送短信,但是发短信这个业务受到短信平台的影响,可能会存在一定时间的延时,但是我们不一定非要等短信平台返回之后,再给用户返回,我们可以先返回获取验证码成功的提升样式,将发短信的业务放入到另外一个线程中执行,用户晚一会收到短信对整体的业务流程也不会受到影响,反而提升了用户体验
代码演示:
1.在springboot项目中导入依赖:
<dependency>
<groupId>org.apache.commons</groupId>
<artifactId>commons-lang3</artifactId>
</dependency>
<dependency>
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
</dependency>
- 编写自定义线程池配置
@Configuration
@EnableAsync // 开启异步注解支持
@Slf4j
public class ExecutorConfig {
@Bean("asyncServiceExecutor")
public Executor asyncServiceExecutor(){
log.info("start asyncServiceExecutor");
// 这里使用的是任务类型的线程池
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
// 配置核心线程数
executor.setCorePoolSize(5);
// 配置最大线程数
executor.setMaxPoolSize(5);
// 配置阻塞队列大小
executor.setQueueCapacity(5);
// 配置线程池中线程名称的前缀
executor.setThreadNamePrefix("async-service-");
// rejection-policy: 当线程池已经达到最大max-size时,如何处理新任务
// CALL-RUNS: 不在新线程中执行任务,而是在调用者所在的线程来执行
executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
// 执行初始化
executor.initialize();
return executor;
}
}
3.线程池要执行的任务
@Service
public class ThreadService {
@Async("asyncServiceExecutor") // 此处代表使用asyncServiceExecutor这个线程池来执行下面这个方法
public void send(String phone,int code){
// 此处模拟发短信业务耗时5秒
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("发短信成功:"+code);
}
}
4.测试代码
@Service
public class SmsService {
@Autowired
private ThreadService threadService;
public String send(String phone) {
/**
* 1.生成验证码
* 2.调用短信平台发送验证码(发入线程池)
* 3.将验证码放入redis
*/
int code = RandomUtils.nextInt(100000, 999999);
// 调用短信平台发送短信业务
// 放入到线程池中执行
threadService.send(phone,code); // 那摩此处就是异步执行的,主线程不会等这一步执行完再执行
System.out.println("主线程继续执行业务");
return "success";
}
}
2.推送
比如有一个业务场景:
有一个审核业务,当收到数据之后,需要将这些数据发送给第三方的监管系统进行审核,数据量有百万之多,一条数据按照一秒计算,那摩需要经过百万秒,200多个小时才能处理完
解决:
考虑引入多线程进行并发操作,降低数据推送时间,提供数据推送的实时性
要注意的问题:
- 防止重复推送
可以考虑将数据切分成不同的数据段,每一个线程负责一个 - 失败处理
推送失败后,进行失败推送的数据记录,用额外的程序处理失败数据(补偿措施
)
代码演示: