2021.04.30 异步线程优化用户体验

背景

对于后端开发,接口响应时长是一个很关键的点,它不仅体现了你写的接口的性能,同时也代表着用户体验,如果你的内容响应时间过长,用户可能早就把网页关闭了。

互联网行业,有一个用户体验原则——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

本项目会持续每日更新,让我们一起学习,一起进步,遇见更好的自己,加油呀

原文地址:https://www.cnblogs.com/caoleiCoding/p/14723959.html