Java 微服务异步并行调用优化
我们先来设想一个场景。
有一个 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性能优化、分布式架构)等这些成为架构师必备的内容!而且还把框架需要用到的各种程序进行了打包,根据基础视频可以让你轻松搭建分布式框架环境,像在企业生产环境一样进行学习和实践。
如果想提升自己的,看看上图大纲能知道你现在还处于什么阶段要向那些方面发展?
同时小编已将上图知识大纲里面的内容打包好了......
想要资料的朋友,可以直接加群960439918获取免费架构资料(包括高可用,高并发,spring源码,mybatis源码,JVM,大数据,Netty等多个技术知识的架构视频资料和各种电子书籍阅读)
加入群聊【java高级架构交流群】
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
蚂蚁金服自主研发的三地五中心异地多活解决方案获金融科技创新大奖
小蚂蚁说: 2018年9月20日,在杭州云栖蚂蚁金服 ATEC 科技大会主论坛上,蚂蚁金服副CTO胡喜正式宣布,蚂蚁金服的金融科技正式全面开放,为行业提供完整的数字金融解决方案。包括支付宝自主研发的容灾系统在内的多项核心技术和解决方案,如金融安全、区块链等都将对合作伙伴开放。 “三地五中心”即在三座城市部署五个机房,一旦其中一个或两个机房发生故障,支付宝的底层技术系统会将故障城市的流量全部切换到运行正常的机房,并且能做到数据保持一致且零丢失。 12月20日,在北京举行的“2018中国金融科技年会暨金融科技及服务优秀奖颁奖典礼”正式宣布蚂蚁金服自主研发的三地五中心异地多活解决方案获“2018年度金融科技优秀解决方案创新奖”。 12月20日,由《金融电子化》杂志社举办的“2018中国金融科技年会暨金融科技及服务优秀奖颁奖典礼”在北京举行。来
- 下一篇
如何为嵌入式应用选择合适的微控制器
为嵌入式应用选择微控制器有几个原因,即低成本,高集成度,增加可靠性,节省空间等。 准备所需硬件接口列表使用微控制器的基本硬件框图,准备一份微控制器需要支持的所有外设接口的列表。微控制器中有两种常见的接口类型需要列出。第一种是通信接口,这些是外围设备,如USB,SPI,I2C,UART等。这些都极大地扰乱了微控制器需要支持多少程序空间。第二种接口是“数字输入和输出”,(A到D)模拟到数字输入,脉冲宽度调制等。这两种类型的接口将控制微控制器必不可少的引脚数。 选择架构架构的选择可以极大地影响嵌入式应用的微控制器。根据以上信息,工程师应该能够开始了解所需的微控制器架构。不要忘记记住可能的未来需求和功能蠕变。仅仅因为你现在可以使用8位微控制器并不意味着你不应该为即将推出的功能研究16位微控制器,甚至不能轻松使用。不要忘记选择微控制器选择可以是迭代过程。您可以在此步骤中选择16位部分,但稍后在步骤中发现32位ARM部分运行良好。这个阶段只是让工程师开始寻找正确的方向。 认识到内存要求微控制器的两个非常关键的存储器组件是RAM和闪存。确保变量空间不足,程序绝对是最重要的。选择具有太多这些功能的...
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- CentOS7设置SWAP分区,小内存服务器的救世主
- Docker安装Oracle12C,快速搭建Oracle学习环境
- Docker快速安装Oracle11G,搭建oracle11g学习环境
- CentOS7编译安装Gcc9.2.0,解决mysql等软件编译问题
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- CentOS8安装MyCat,轻松搞定数据库的读写分离、垂直分库、水平分库
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Mario游戏-低调大师作品
- 2048小游戏-低调大师作品
- SpringBoot2初体验,简单认识spring boot2并且搭建基础工程