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

本文涉及的产品
注册配置 MSE Nacos/ZooKeeper,118元/月
服务治理 MSE Sentinel/OpenSergo,Agent数量 不受限
任务调度 XXL-JOB 版免费试用,400 元额度,开发版规格
简介: 我们先来设想一个场景。有一个 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
目录
打赏
0
0
0
0
6
分享
相关文章
|
26天前
|
利用Spring Cloud Gateway Predicate优化微服务路由策略
Spring Cloud Gateway 的路由配置中,`predicates`​(断言)用于定义哪些请求应该匹配特定的路由规则。 断言是Gateway在进行路由时,根据具体的请求信息如请求路径、请求方法、请求参数等进行匹配的规则。当一个请求的信息符合断言设置的条件时,Gateway就会将该请求路由到对应的服务上。
138 69
利用Spring Cloud Gateway Predicate优化微服务路由策略
|
1月前
|
Java虚拟机(JVM)垃圾回收机制深度剖析与优化策略####
本文作为一篇技术性文章,深入探讨了Java虚拟机(JVM)中垃圾回收的工作原理,详细分析了标记-清除、复制算法、标记-压缩及分代收集等主流垃圾回收算法的特点和适用场景。通过实际案例,展示了不同GC(Garbage Collector)算法在应用中的表现差异,并针对大型应用提出了一系列优化策略,包括选择合适的GC算法、调整堆内存大小、并行与并发GC调优等,旨在帮助开发者更好地理解和优化Java应用的性能。 ####
56 0
|
15天前
|
java异步判断线程池所有任务是否执行完
通过上述步骤,您可以在Java中实现异步判断线程池所有任务是否执行完毕。这种方法使用了 `CompletionService`来监控任务的完成情况,并通过一个独立线程异步检查所有任务的执行状态。这种设计不仅简洁高效,还能确保在大量任务处理时程序的稳定性和可维护性。希望本文能为您的开发工作提供实用的指导和帮助。
72 17
Java中的算法优化与复杂度分析
在Java开发中,理解和优化算法的时间复杂度和空间复杂度是提升程序性能的关键。通过合理选择数据结构、避免重复计算、应用分治法等策略,可以显著提高算法效率。在实际开发中,应该根据具体需求和场景,选择合适的优化方法,从而编写出高效、可靠的代码。
35 6
Java中的线程池优化实践####
本文深入探讨了Java中线程池的工作原理,分析了常见的线程池类型及其适用场景,并通过实际案例展示了如何根据应用需求进行线程池的优化配置。文章首先介绍了线程池的基本概念和核心参数,随后详细阐述了几种常见的线程池实现(如FixedThreadPool、CachedThreadPool、ScheduledThreadPool等)的特点及使用场景。接着,通过一个电商系统订单处理的实际案例,分析了线程池参数设置不当导致的性能问题,并提出了相应的优化策略。最终,总结了线程池优化的最佳实践,旨在帮助开发者更好地利用Java线程池提升应用性能和稳定性。 ####
Java 11 的String是如何优化存储的?
本文介绍了Java中字符串存储优化的原理和实现。通过判断字符串是否全为拉丁字符,使用`byte`代替`char`存储,以节省空间。具体实现涉及`compress`和`toBytes`方法,前者用于尝试压缩字符串,后者则按常规方式存储。代码示例展示了如何根据配置决定使用哪种存储方式。
Java 内存管理与优化:掌控堆与栈,雕琢高效代码
Java内存管理与优化是提升程序性能的关键。掌握堆与栈的运作机制,学习如何有效管理内存资源,雕琢出更加高效的代码,是每个Java开发者必备的技能。
72 5
Java虚拟机(JVM)垃圾回收机制深度解析与优化策略####
本文旨在深入探讨Java虚拟机(JVM)的垃圾回收机制,揭示其工作原理、常见算法及参数调优方法。通过剖析垃圾回收的生命周期、内存区域划分以及GC日志分析,为开发者提供一套实用的JVM垃圾回收优化指南,助力提升Java应用的性能与稳定性。 ####
Java 集合框架优化:从基础到高级应用
《Java集合框架优化:从基础到高级应用》深入解析Java集合框架的核心原理与优化技巧,涵盖列表、集合、映射等常用数据结构,结合实际案例,指导开发者高效使用和优化Java集合。
56 4
|
2月前
|
Java虚拟机垃圾回收机制深度剖析与优化策略####
【10月更文挑战第21天】 本文旨在深入探讨Java虚拟机(JVM)中的垃圾回收机制,揭示其工作原理、常见算法及参数调优技巧。通过案例分析,展示如何根据应用特性调整GC策略,以提升Java应用的性能和稳定性,为开发者提供实战中的优化指南。 ####
49 5
AI助理

你好,我是AI助理

可以解答问题、推荐解决方案等