Java 微服务异步并行调用优化

本文涉及的产品
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
简介: 我们先来设想一个场景。有一个 http 的接口 A,该接口内部实际上是由另外三个接口 B、C、D 返回结果的组合,这三个接口不存在相互依赖。
img_3d09cbce5e9a8232923e5c82f157b3db.jpe

我们先来设想一个场景。

有一个 http 的接口 A,该接口内部实际上是由另外三个接口 B、C、D 返回结果的组合,这三个接口不存在相互依赖。我们一般的写法就是 B、C、D 同步顺序执行,依次拿到结果后组装在一起。那么假如这三个接口分别耗时 2 秒,那么 A 接口就要耗时 6 秒。如果可以让 B、C、D 同时执行的话,那么 A 接口理论上只要耗时 2 秒。

当然实际情况肯定复杂的多,如果一个接口内部存在不相互依赖的耗时调用的话,那么我们可以做这样的合并,响应时间上的减少还是非常明显的。整个接口的响应时间取决于最长的那个内部接口。

那么我们来看看在 Java 中有哪些方法可以达到这样的目的。认真思考下你会发现,如果要并行处理的话,在 Java 中只能用多线程来做。实际情况中每个线程处理完的时间肯定不一样,那么如何让线程先处理完的停下来等最后那个处理完的呢。如果经常用多线程的小伙伴肯定能想到 CountDownLatch 工具类。当然也有直接简单暴力的方法,在空循环里轮询每个线程是否执行完,但是这样做肯定不优雅。

那下面就直接上代码了: 假设有个学生服务提供查询学生名字,年龄和家庭信息,每个服务之间没有相互依赖。 我们就简单模拟下来获取学生信息的一个接口。

常规方法

@RequestMapping("/getStudentInfo")

public Object getStudentInfo() {

long start = System.currentTimeMillis();

Map resultMap = new HashMap<>(10);

try {

resultMap.put("studentName", studentService.getStudentName());

resultMap.put("studentAge", studentService.getSutdentAge());

resultMap.put("studentFamilyInfo", studentService.getSutdentFamilyInfo());

} catch (Exception e) {

resultMap.put("errMsg", e.getMessage());

}

resultMap.put("total cost", System.currentTimeMillis() - start);

return resultMap;

}

顺序同步执行,耗时 6 秒。

1. Future

@RequestMapping("/getStudentInfoWithFuture")

public Object testWhitCallable() {

long start = System.currentTimeMillis();

Map resultMap = new HashMap<>(10);

try {

CountDownLatch countDownLatch = new CountDownLatch(3);

Future futureStudentName = es.submit(() -> {

Object studentName = studentService.getStudentName();

countDownLatch.countDown();

return studentName;

});

Future futureStudentAge = es.submit(() -> {

Object studentAge = studentService.getSutdentAge();

countDownLatch.countDown();

return studentAge;

});

Future futureStudentFamilyInfo = es.submit(() -> {

Object studentFamilyInfo = studentService.getSutdentFamilyInfo();

countDownLatch.countDown();

return studentFamilyInfo;

});

//同步等待所有线程执行完之后再继续

countDownLatch.await();

resultMap.put("studentName", futureStudentName.get());

resultMap.put("studentAge", futureStudentAge.get());

resultMap.put("studentFamilyInfo", futureStudentFamilyInfo.get());

} catch (Exception e) {

resultMap.put("errMsg", e.getMessage());

}

resultMap.put("total cost", System.currentTimeMillis() - start);

return resultMap;

}

2.RxJava

@RequestMapping("/getStudentInfoWithRxJava")

public Object testWithRxJava() {

long start = System.currentTimeMillis();

Map resultMap = new HashMap<>(10);

try {

CountDownLatch countDownLatch = new CountDownLatch(1);

Observable studentNameObservable = Observable.create(observableEmitter -> {

resultMap.put("studentName", studentService.getStudentName());

observableEmitter.onComplete();

}).subscribeOn(Schedulers.io());

Observable studentAgeObservable = Observable.create(observableEmitter -> {

resultMap.put("studentAge", studentService.getSutdentAge());

observableEmitter.onComplete();

}).subscribeOn(Schedulers.io());

Observable familyInfoObservable = Observable.create(observableEmitter -> {

resultMap.put("studentFamilyInfo", studentService.getSutdentFamilyInfo());

observableEmitter.onComplete();

}).subscribeOn(Schedulers.io());

//创建一个下游 Observer

Observer observer = new Observer() {

@Override

public void onSubscribe(Disposable d) {

}

@Override

public void onNext(Object o) {

}

@Override

public void onError(Throwable e) {

}

@Override

public void onComplete() {

//因为后面用了 merge 操作符,所以会合并后发射,那么只要 countdown 一次就行了。

countDownLatch.countDown();

}

};

//建立连接,

Observable.merge(studentNameObservable, studentAgeObservable, familyInfoObservable).subscribe(observer);

//等待异步线程完成

countDownLatch.await();

} catch (Exception e) {

resultMap.put("errMsg", e.getMessage());

}

resultMap.put("total cost", System.currentTimeMillis() - start);

return resultMap;

}

对于 RxJava 我不熟,我也是临时学习的,不知道这种写法是不是最佳的。

3.CompletableFutures

@RequestMapping("/getStudentInfoWithCompletableFuture")

public Object getStudentInfoWithCompletableFuture() {

long start = System.currentTimeMillis();

Map resultMap = new HashMap<>(10);

try {

CompletableFuture completableFutureStudentName = CompletableFuture.supplyAsync(() -> {

try {

return studentService.getStudentName();

} catch (InterruptedException e) {

e.printStackTrace();

}

return null;

});

CompletableFuture completableFutureSutdentAge = CompletableFuture.supplyAsync(() -> {

try {

return studentService.getSutdentAge();

} catch (InterruptedException e) {

e.printStackTrace();

}

return null;

});

CompletableFuture completableFutureFamilyInfo = CompletableFuture.supplyAsync(() -> {

try {

return studentService.getSutdentFamilyInfo();

} catch (InterruptedException e) {

e.printStackTrace();

}

return null;

});

CompletableFuture.allOf(completableFutureStudentName, completableFutureSutdentAge, completableFutureFamilyInfo).join();

resultMap.put("studentName", completableFutureStudentName.get());

resultMap.put("studentAge", completableFutureSutdentAge.get());

resultMap.put("studentFamilyInfo", completableFutureFamilyInfo.get());

} catch (Exception e) {

resultMap.put("errMsg", e.getMessage());

}

resultMap.put("total cost", System.currentTimeMillis() - start);

return resultMap;

}

自带最后的同步等待,不需要 CountDownLatch。CompletableFuture 还有很多其他好用的方法。

有兴趣的可以自己来实验下。 github 项目地址 reactive-programming-sample。

Java程序员如何学习才能快速入门并精通呢?

当真正开始学习的时候难免不知道从哪入手,导致效率低下影响继续学习的信心。

但最重要的是不知道哪些技术需要重点掌握,学习时频繁踩坑,最终浪费大量时间,所以有一套实用的视频课程用来跟着学习是非常有必要的。

为了让学习变得轻松、高效,今天给大家免费分享一套阿里架构师传授的一套教学资源。帮助大家在成为架构师的道路上披荆斩棘。这套视频课程详细讲解了(Spring,MyBatis,Netty源码分析,高并发、高性能、分布式、微服务架构的原理,JVM性能优化、分布式架构)等这些成为架构师必备的内容!而且还把框架需要用到的各种程序进行了打包,根据基础视频可以让你轻松搭建分布式框架环境,像在企业生产环境一样进行学习和实践。

img_f793477d300677b850142a345b7f86da.png

如果想提升自己的,看看上图大纲能知道你现在还处于什么阶段要向那些方面发展?

同时小编已将上图知识大纲里面的内容打包好了......

想要资料的朋友,可以直接加群960439918获取免费架构资料(包括高可用,高并发,spring源码,mybatis源码,JVM,大数据,Netty等多个技术知识的架构视频资料和各种电子书籍阅读)

加入群聊【java高级架构交流群】

img_d8dffde0a77193324beaa168bc56d709.png
目录
相关文章
|
13天前
|
存储 缓存 算法
优化 Java 后台代码的关键要点
【4月更文挑战第5天】本文探讨了优化 Java 后台代码的关键点,包括选用合适的数据结构与算法、减少不必要的对象创建、利用 Java 8 新特性、并发与多线程处理、数据库和缓存优化、代码分析与性能调优、避免阻塞调用、JVM 调优以及精简第三方库。通过这些方法,开发者可以提高系统性能、降低资源消耗,提升用户体验并减少运营成本。
|
15天前
|
Java
深入理解Java并发编程:线程池的应用与优化
【4月更文挑战第3天】 在Java并发编程中,线程池是一种重要的资源管理工具,它能有效地控制和管理线程的数量,提高系统性能。本文将深入探讨Java线程池的工作原理、应用场景以及优化策略,帮助读者更好地理解和应用线程池。
|
1天前
|
负载均衡 Java 开发者
细解微服务架构实践:如何使用Spring Cloud进行Java微服务治理
【4月更文挑战第17天】Spring Cloud是Java微服务治理的首选框架,整合了Eureka(服务发现)、Ribbon(客户端负载均衡)、Hystrix(熔断器)、Zuul(API网关)和Config Server(配置中心)。通过Eureka实现服务注册与发现,Ribbon提供负载均衡,Hystrix实现熔断保护,Zuul作为API网关,Config Server集中管理配置。理解并运用Spring Cloud进行微服务治理是现代Java开发者的关键技能。
|
2天前
|
SQL 缓存 Java
Java数据库连接池:优化数据库访问性能
【4月更文挑战第16天】本文探讨了Java数据库连接池的重要性和优势,它能减少延迟、提高效率并增强系统的可伸缩性和稳定性。通过选择如Apache DBCP、C3P0或HikariCP等连接池技术,并进行正确配置和集成,开发者可以优化数据库访问性能。此外,批处理、缓存、索引优化和SQL调整也是提升性能的有效手段。掌握数据库连接池的使用是优化Java企业级应用的关键。
|
4天前
|
Java 程序员 编译器
Java中的线程同步与锁优化策略
【4月更文挑战第14天】在多线程编程中,线程同步是确保数据一致性和程序正确性的关键。Java提供了多种机制来实现线程同步,其中最常用的是synchronized关键字和Lock接口。本文将深入探讨Java中的线程同步问题,并分析如何通过锁优化策略提高程序性能。我们将首先介绍线程同步的基本概念,然后详细讨论synchronized和Lock的使用及优缺点,最后探讨一些锁优化技巧,如锁粗化、锁消除和读写锁等。
|
5天前
|
Java 编译器
Java并发编程中的锁优化策略
【4月更文挑战第13天】 在Java并发编程中,锁是一种常见的同步机制,用于保证多个线程之间的数据一致性。然而,不当的锁使用可能导致性能下降,甚至死锁。本文将探讨Java并发编程中的锁优化策略,包括锁粗化、锁消除、锁降级等方法,以提高程序的执行效率。
12 4
|
11天前
|
设计模式 缓存 安全
分析设计模式对Java应用性能的影响,并提供优化策略
【4月更文挑战第7天】本文分析了7种常见设计模式对Java应用性能的影响及优化策略:单例模式可采用双重检查锁定、枚举实现或对象池优化;工厂方法和抽象工厂模式可通过对象池和缓存减少对象创建开销;建造者模式应减少构建步骤,简化复杂对象;原型模式优化克隆方法或使用序列化提高复制效率;适配器模式尽量减少使用,或合并多个适配器;观察者模式限制观察者数量并使用异步通知。设计模式需根据应用场景谨慎选用,兼顾代码质量和性能。
|
13天前
|
Java 编译器
Java并发编程中的锁优化策略
【4月更文挑战第5天】随着多核处理器的普及,并发编程在提高程序性能方面发挥着越来越重要的作用。在Java中,锁是实现并发控制的关键机制。本文将探讨Java并发编程中的锁优化策略,包括锁粗化、锁消除、锁排序等技术,以提高程序的执行效率和降低资源争用。
|
23天前
|
算法 安全 Java
Java中的并发编程优化探究
本文深入探讨了Java中的并发编程优化策略,从锁机制到线程池的使用,以及常见的并发问题与解决方案。通过分析不同的优化方法,帮助开发者更好地应对并发环境下的挑战,提高程序性能和稳定性。
11 0
|
24天前
|
安全 Java
Java中的并发编程优化技巧
在当今软件开发领域中,多线程并发编程已经成为不可或缺的一部分。本文将探讨在Java编程中如何优化并发操作,提高程序性能和可靠性的关键技巧。
11 0