每日一例 | 异步线程优化用户体验
背景
对于后端开发,接口响应时长是一个很关键的点,它不仅体现了你写的接口的性能,同时也代表着用户体验,如果你的内容响应时间过长,用户可能早就把网页关闭了。
互联网行业,有一个用户体验原则——2/5/10秒原则。
就是说,在2秒之内给客户响应被用户认为是“非常有吸引力”的用户体验。
在5秒之内响应客户被认为“比较不错”的用户体验,在10秒内给用户响应被认为“糟糕”的用户体验。
如果超过10秒还没有得到响应,那么大多用户会认为这次请求是失败的。
所以很多互联网企业,接口发布上线前,都有一个压测要求:特定并发量下,接口的响应时间不能大于200ms
。
在这样的要求之下,想要提升接口性能,减少用户等待时间,将部分非实时、不重要、确定无异常等交易(比如订单处理、办理完某个业务给用户发消息等),设计成异步处理的方式是一个不错的选择,今天我们就来通过一个简单的示例演示,来了解下异步交易如何实现。
异步交易演示
假设,我们有这样的业务需求:
场景一:我们有一个教务管理系统,讲师导入创建了一门新的课程,并制定了学员,处理课程创建的操作外,我们还需要在课程创建成功后,给学员发消息。但是发消息并非是特别重要的操作,这时候我们就可以通过异步交易来发送消息。
场景二:我们需要在用户注册成功后,给用户发送消息(邮件或短信)通知用户,和场景一类似,发送消息非必须业务,为了提升系统响应效率,这时候异步交易是个不错的选择。
场景说完了,下来看我们的简单应用:
我们先创建了一个线程池:
private static ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(5, 10, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));
然后,我们在核心交易的最后,通过线程池的submit
启动了一个线程:
threadPoolExecutor.submit(() -> this.sendMessage(messageList));
通过这个线程去执行发消息的操作,这里我们用到了lamubda
表达式,上面的代码等同于:
threadPoolExecutor.submit(new Runnable() {
@Override
public void run() {
sendMessage(messageList);
}
});
下面是完整代码:
/**
* @author syske
* @date 2021-05-01 9:34
*/
public class Example {
private static ThreadPoolExecutor threadPoolExecutor =
new ThreadPoolExecutor(5, 10, 1,
TimeUnit.SECONDS, new LinkedBlockingQueue<>(5));
/**
* 发送消息
* @param messageList 消息列表
* @return
*/
public AtomicInteger sendMessage(List<String> messageList) {
AtomicInteger successCount = new AtomicInteger();
messageList.forEach(m -> {
System.out.println("发送消息:" + m);
successCount.getAndIncrement();
});
return successCount;
}
/**
* 核心业务处理
* @return
*/
public String deal() {
List<String> messageList = new ArrayList<>();
// doSomeThing() 其他业务处理
System.out.println("开始组装消息~Start");
for (int i = 0; i < 1000; i++) {
int randomInt1 = new Random().nextInt();
messageList.add("发送数字信息:" + randomInt1);
}
System.out.println("组装消息完成~End");
System.out.println("开始发送消息~Start");
threadPoolExecutor.submit(() -> this.sendMessage(messageList));
System.out.println("发送消息完成~End");
return "业务处理完成";
}
public static void main(String[] args) {
Example example = new Example();
String result = example.deal();
System.out.println(result);
threadPoolExecutor.shutdown();
}
}
执行上面的方法,你会发现,核心业务方法在消息没发送就已经返回了:
开始组装消息~Start
组装消息完成~End
开始发送消息~Start
发送消息完成~End
业务处理完成
发送消息:发送数字信息:-123158837
……
……
发送消息:发送数字信息:-1354635036
这样我们的异步交易就实现了,是不是很简单呀,如果你的异步业务是有返回值的,那在启动线程的时候,你可以通过Callable
来实现,它和Runnable
没有本质区别,只是它是可以有返回值的。
总结
其实今天的内容很简单,实现过程也很容易,异步交易中真正难的是如何拆分你的业务,这就需要你自己多思考,多实践,多总结了,还是那句话会用这个工具很简单,但清楚什么时候用这个工具才更重要。好了,各位小伙伴节日快乐,好好享受假期,但也别忘了学习哦
项目路径:
https://github.com/Syske/example-everyday
本项目会持续每日更新,让我们一起学习,一起进步,遇见更好的自己,加油呀
- END -