消息队列面试解析系列(六)- 异步编程妙用(下)

简介: 消息队列面试解析系列(六)- 异步编程妙用

异步实现的性能

由于流程时序和同步一样,在少量请求场景下,平均响应时延一样100ms。在高请求数量场景下,异步不再需线程等待执行结果,只需个位数量的线程,即可实现同步场景大量线程一样的吞吐量。


由于没线程的数量的限制,总体吞吐量上限会大大超过同步实现,且在服务器CPU、网络带宽资源达到极限前,响应时延不会随请求数量增加而显著升高,几乎可一直保持约100ms的平均响应时延。


异步框架: CompletableFuture

开发时可使用异步框架和响应式框架,解决一些异步编程问题。

Java中比较常用的异步框架有Java8内置的CompletableFuture和ReactiveX的RxJava。


  • CompletableFuture简单实用易理解
  • RxJava功能更强大

Java 8中新增的异步编程类:CompletableFuture,几乎囊获了开发异步程序的大部分功能,使用CompletableFuture很容易编写优雅且易维护的异步代码。


接下来用CompletableFuture实现的转账服务。


CompletableFuture定义2个微服务的接口:

/**
 * 账户服务
 */
public interface AccountService {
    /**
     * 变更账户金额
     * @param account 账户ID
     * @param amount 增加的金额,负值为减少
     */
    CompletableFuture<Void> add(int account, int amount);
}
/**
 * 转账服务
 */
public interface TransferService {
    /**
     * 异步转账服务
     * @param fromAccount 转出账户
     * @param toAccount 转入账户
     * @param amount 转账金额,单位分
     */
    CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount);
}


接口中定义的方法的返回类型都是泛型CompletableFeture,泛型类型就是真正方法需要返回数据的类型,这俩服务无需返回数据,所以用Void。

实现转账服务:

/**
 * 转账服务的实现
 */
public class TransferServiceImpl implements TransferService {
    @Inject
    private  AccountService accountService; // 使用依赖注入获取账户服务的实例
    @Override
    public CompletableFuture<Void> transfer(int fromAccount, int toAccount, int amount) {
      // 异步调用add方法从fromAccount扣减相应金额
      return accountService.add(fromAccount, -1 * amount)
      // 然后调用add方法给toAccount增加相应金额
      .thenCompose(v -> accountService.add(toAccount, amount));    
    }
}

先定义一个AccountService实例,这个实例从外部注入进来,至于怎么注入不是我们关心的问题,就假设这个实例是可用的就好了。


看transfer()方法,先调用一次账户服务accountService.add()方法从fromAccount扣减相应金额,因add()方法返回的就是个CompletableFeture对象,可用CompletableFeture的thenCompose()方法将下一次调用accountService.add()串联起来,实现异步依次调用两次账户服务完整转账。


客户端使用CompletableFuture也灵活,既可同步调用,也可异步。

public class Client {
    @Inject
    private TransferService transferService; // 使用依赖注入获取转账服务的实例
    private final static int A = 1000;
    private final static int B = 1001;
    public void syncInvoke() throws ExecutionException, InterruptedException {
        // 同步调用
        transferService.transfer(A, B, 100).get();
        System.out.println("转账完成!");
    }
    public void asyncInvoke() {
        // 异步调用
        transferService.transfer(A, B, 100)
                .thenRun(() -> System.out.println("转账完成!"));
    }
}

调用异步方法获得返回值CompletableFuture对象后


既可调CompletableFuture#get,像调用同步方法样等待调用的方法执行结束并获得返回值

亦可如异步回调,调用CompletableFuture那些then开头方法,为CompletableFuture定义异步方法结束之后的后续操作

比如上例,调用thenRun()方法,参数就是将转账完成打印在控台上这个操作,这样就可以实现在转账完成后,在控制台打印“转账完成!”了。


总结

异步的思想就是,当要执行一项比较耗时的操作时,不去等待操作结束,而是给这个操作一个命令:“当操作完成后,接下来去执行什么。”


使用异步编程模型,虽并不能加快程序本身速度,但可减少或者避免线程等待,只用很少的线程即可得到超高吞吐。


同时我们也需注意异步模型问题:相比同步,异步实现的复杂度大很多,代码可读性和可维护性都显著下降。

虽然使用一些异步编程框架会在一定程度上简化异步开发,但是并不能解决异步模型高复杂度的问题。


异步性能虽好,勿滥用,只有类似MQ这种业务逻辑简单且需超高吞吐量场景,或须长时等待资源,才考虑使用异步模型。

如果系统的业务逻辑比较复杂,在性能足够满足业务需求的情况下,采用符合人类自然的思路且易于开发和维护的同步模型是更加明智的选择。


面试场景题快问快答

实现转账服务时,未考虑处理失败情况。异步实现中,若调用账户服务失败,如何将错误报告给客户端?在两次调用账户服务的Add方法时,如果某一次调用失败了,该如何处理才能保证账户数据是平的?


  1. 调用账户失败,可以在异步callBack里执行通知客户端的逻辑
  2. 若是第一次失败,后面那步就不用执行了,所以转账失败;如果是第一次成功但是第二次失败,首先考虑重试,如果转账服务是幂等的,可以考虑一定次数的重试,如果不能重试,可以考虑采用补偿机制,undo第一次的转账操作。


在异步实现中,回调方法OnComplete()是在什么线程中运行的?我们是否能控制回调方法的执行线程数

CompletableFuture默认是在ForkjoinPool commonpool里执行的,也可以指定一个Executor线程池执行,借鉴guava的ListenableFuture的时间,回调可以指定线程池执行,这样就能控制这个线程池的线程数目了。

在异步实现中,回调方法 OnComplete()在执行OnAllDone()回调方法的那个线程,可通过一个异步线程池控制回调方法的线程数,如Spring中的async就是通过结合线程池来实现异步。


目录
相关文章
|
4天前
|
Python
2024年最全用Python写了一个电子考勤系统_用python写一个宿舍考勤系统,2024年最新1307页阿里Python面试全套真题解析在互联网火了
2024年最全用Python写了一个电子考勤系统_用python写一个宿舍考勤系统,2024年最新1307页阿里Python面试全套真题解析在互联网火了
|
4天前
|
机器学习/深度学习 编解码 算法
算法工程师面试问题总结 | YOLOv5面试考点原理全解析
本文给大家带来的百面算法工程师是深度学习目标检测YOLOv5面试总结,文章内总结了常见的提问问题,旨在为广大学子模拟出更贴合实际的面试问答场景。在这篇文章中,我们还将介绍一些常见的深度学习目标检测面试问题,并提供参考的回答及其理论基础,以帮助求职者更好地准备面试。通过对这些问题的理解和回答,求职者可以展现出自己的深度学习目标检测领域的专业知识、解决问题的能力以及对实际应用场景的理解。同时,这也是为了帮助求职者更好地应对深度学习目标检测岗位的面试挑战,提升面试的成功率和竞争力。
|
7天前
|
前端开发 JavaScript
Web前端开发之面试题全解析 一,2024年最新面经牛客
Web前端开发之面试题全解析 一,2024年最新面经牛客
|
7天前
|
移动开发 前端开发 JavaScript
Web前端开发之面试题全解析 一(3),前端面试题背不下来怎么办
Web前端开发之面试题全解析 一(3),前端面试题背不下来怎么办
|
7天前
|
前端开发 算法 搜索推荐
Web前端开发之面试题全解析 一(1),2024年最新前端组件化面试
Web前端开发之面试题全解析 一(1),2024年最新前端组件化面试
|
9天前
|
消息中间件 存储 监控
RabbitMQ:分布式系统中的高效消息队列
RabbitMQ:分布式系统中的高效消息队列
|
9天前
|
消息中间件 分布式计算 监控
Python面试:消息队列(RabbitMQ、Kafka)基础知识与应用
【4月更文挑战第18天】本文探讨了Python面试中RabbitMQ与Kafka的常见问题和易错点,包括两者的基础概念、特性对比、Python客户端使用、消息队列应用场景及消息可靠性保证。重点讲解了消息丢失与重复的避免策略,并提供了实战代码示例,帮助读者提升在分布式系统中使用消息队列的能力。
43 2
|
9天前
|
消息中间件 Java
springboot整合消息队列——RabbitMQ
springboot整合消息队列——RabbitMQ
84 0
|
9天前
|
消息中间件 JSON Java
RabbitMQ消息队列
RabbitMQ消息队列
47 0

热门文章

最新文章

推荐镜像

更多