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高级架构交流群】

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 215,384评论 6 497
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,845评论 3 391
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 161,148评论 0 351
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,640评论 1 290
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,731评论 6 388
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,712评论 1 294
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,703评论 3 415
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,473评论 0 270
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,915评论 1 307
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,227评论 2 331
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,384评论 1 345
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,063评论 5 340
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,706评论 3 324
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,302评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,531评论 1 268
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,321评论 2 368
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,248评论 2 352

推荐阅读更多精彩内容

  • 阿里巴巴 JAVA 开发手册 1 / 32 Java 开发手册 版本号 制定团队 更新日期 备 注 1.0.0 阿...
    糖宝_阅读 7,551评论 0 5
  • 一、编程规约 (一)命名规约 【强制】 代码中的命名均不能以下划线或美元符号开始,也不能以下划线或美元符号结束。反...
    喝咖啡的蚂蚁阅读 1,502评论 0 2
  • 对象的创建与销毁 Item 1: 使用static工厂方法,而不是构造函数创建对象:仅仅是创建对象的方法,并非Fa...
    孙小磊阅读 1,976评论 0 3
  • 作者: maplejaw本篇只解析标准包中的操作符。对于扩展包,由于使用率较低,如有需求,请读者自行查阅文档。 创...
    maplejaw_阅读 45,654评论 8 93
  • 高考第一天那晚,我与你,操场与两片光亮,六年彼此的改变和未来的打算,融在不凉不燥的风中。
    周淡皮阅读 92评论 0 0