当前位置: 首页 > news >正文

【性能优化专题系列】利用CompletableFuture优化多接口调用场景下的性能

背景说明

在实际的软件开发中,我们经常会遇到需要批量调用接口的场景。例如,电商系统在生成商品详情页时,需要同时调用多个服务接口来获取商品的基本信息、库存信息、价格信息、用户评价等。

传统的依次调用方式存在性能问题

面对上述场景,传统的做法是依次调用这些接口,等待每个接口返回结果后再进行下一步操作。

面对这种方式会导致整体性能低下,因为每个接口调用都需要等待上一个接口调用完成,假设x方法内部要调用a、b、c、d四个接口,那么x方法执行的耗时=a耗时+b耗时+c耗时+d耗时,这样消耗的时间会比较长。

采用批量调用的方式进行优化

可以注意到,这些接口调用之间可能并没有严格的先后顺序,完全可以并行执行,我们可以采用CompletableFuture类来实现接口调用的并行执行。

CompletableFuture介绍

CompletableFuture 是 Java 8 引入的一个强大的异步编程工具,它实现了 Future 和 CompletionStage 接口,提供了丰富的方法来处理异步任务的完成、组合和异常处理。

在批量调用接口的场景中,CompletableFuture 的主要原理如下:

异步执行:CompletableFuture.supplyAsync() 方法可以将一个任务提交到线程池中异步执行,而不会阻塞当前线程。在上述代码中,每个接口调用都被封装成一个 CompletableFuture 对象,并通过 supplyAsync() 方法异步执行。

并行处理:由于每个接口调用都是异步执行的,它们可以在不同的线程中并行处理,从而充分利用多核 CPU 的性能,减少整体的执行时间。

组合操作:CompletableFuture.allOf() 方法可以将多个 CompletableFuture 对象组合成一个新的 CompletableFuture 对象,该对象在所有子任务都完成后才会完成。通过这种方式,我们可以等待所有接口调用都完成后再进行后续的处理。

结果获取:CompletableFuture.join() 方法用于获取异步任务的结果,如果任务还未完成,该方法会阻塞当前线程,直到任务完成。在上述代码中,我们使用 join() 方法获取每个接口调用的结果,并将它们收集到一个列表中。

优化实践

首先我们模拟一个接口调用的服务类,命名为InfoServiceFeignMock,用于模拟调用接口的场景。

package org.example.Scene;/*** @Author xu* @Version 1.0* @Description 模拟接口调用**/
public class InfoServiceFeignMock {/*** 模拟调用获取商品基本信息的接口* @param productId 商品 ID* @return 商品基本信息*/public String getProductBasicInfo(String productId) {try {// 模拟接口调用耗时,例如网络延迟等Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return "Basic info for product " + productId;}/*** 模拟调用获取商品库存信息的接口* @param productId 商品 ID* @return 商品库存信息*/public String getProductInventoryInfo(String productId) {try {// 模拟接口调用耗时Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return "Inventory info for product " + productId;}/*** 模拟调用获取商品价格信息的接口* @param productId 商品 ID* @return 商品价格信息*/public String getProductPriceInfo(String productId) {try {// 模拟接口调用耗时Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return "Price info for product " + productId;}/*** 模拟调用获取商品用户评价信息的接口* @param productId 商品 ID* @return 商品用户评价信息*/public String getProductReviewInfo(String productId) {try {// 模拟接口调用耗时Thread.sleep(200);} catch (InterruptedException e) {e.printStackTrace();}return "Review info for product " + productId;}}

接下来我们再新建一个类,叫做SceneMock,用来比对原始顺序调用和使用CompletableFuture批量调用情况下的耗时情况。

package org.example.Scene;import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;/*** @Author xu* @Version 1.0* @Description 模拟接口调用的场景**/
public class SceneMock {/*** 程序的入口点* 本方法演示了两种处理产品ID的方法* @param args 命令行参数,本示例中未使用*/public static void main(String[] args) {// 定义一个产品ID,用于后续的方法调用和处理String productId = "12345";// 调用默认方法处理产品IDdefaultMethod(productId);// 调用改进方法处理产品IDbetterMethod(productId);}/*** 默认方法,用于演示如何调用信息服务获取产品相关信息* 该方法将模拟通过Feign客户端调用远程服务来获取产品的基本信息、库存信息、价格信息和评论信息** @param productId 产品ID,用于查询产品信息*/public static void defaultMethod(String productId){// 创建模拟接口调用的实例InfoServiceFeignMock infoService = new InfoServiceFeignMock();// 记录开始时间long startTime = System.currentTimeMillis();// 初始化结果列表,用于存储从各服务获取的信息List<String> results = new ArrayList<>();// 调用模拟的服务获取产品基本信息并添加到结果列表results.add(infoService.getProductBasicInfo(productId));// 调用模拟的服务获取产品库存信息并添加到结果列表results.add(infoService.getProductInventoryInfo(productId));// 调用模拟的服务获取产品价格信息并添加到结果列表results.add(infoService.getProductPriceInfo(productId));// 调用模拟的服务获取产品评论信息并添加到结果列表results.add(infoService.getProductReviewInfo(productId));// 记录结束时间long endTime = System.currentTimeMillis();// 输出结果System.out.println("All results: " + results);// 输出总耗时System.out.println("defaultMethod time cost: " + (endTime - startTime) + " ms");}/*** 异步调用产品信息的方法* 该方法通过异步调用模拟获取产品的基本信息、库存信息、价格信息和评论信息* 使用 CompletableFuture 来并行处理多个异步任务,并收集结果** @param productId 产品ID,用于查询产品信息*/public static void betterMethod(String productId){// 创建模拟接口调用的实例InfoServiceFeignMock infoService = new InfoServiceFeignMock();// 记录开始时间long startTime = System.currentTimeMillis();// 使用 CompletableFuture 异步调用各个接口CompletableFuture<String> basicInfoFuture = CompletableFuture.supplyAsync(() ->infoService.getProductBasicInfo(productId));CompletableFuture<String> inventoryInfoFuture = CompletableFuture.supplyAsync(() ->infoService.getProductInventoryInfo(productId));CompletableFuture<String> priceInfoFuture = CompletableFuture.supplyAsync(() ->infoService.getProductPriceInfo(productId));CompletableFuture<String> reviewInfoFuture = CompletableFuture.supplyAsync(() ->infoService.getProductReviewInfo(productId));// 将所有的 CompletableFuture 收集到一个列表中List<CompletableFuture<String>> futures = new ArrayList<>();futures.add(basicInfoFuture);futures.add(inventoryInfoFuture);futures.add(priceInfoFuture);futures.add(reviewInfoFuture);// 使用 allOf 方法组合所有的 CompletableFuture,等待所有任务完成CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));// 当所有任务完成后,将结果收集到一个列表中CompletableFuture<List<String>> allResults = allFutures.thenApply(v ->futures.stream().map(CompletableFuture::join).collect(Collectors.toList()));try {// 获取所有结果List<String> results = allResults.get();// 记录结束时间long endTime = System.currentTimeMillis();// 输出结果System.out.println("All results: " + results);System.out.println("betterMethod time cost: " + (endTime - startTime) + " ms");} catch (Exception e) {// 处理异常e.printStackTrace();}}
}

我们接下来执行SceneMock类中的main方法,查看执行结果。

All results: [Basic info for product 12345, Inventory info for product 12345, Price info for product 12345, Review info for product 12345]
defaultMethod time cost: 810 ms
All results: [Basic info for product 12345, Inventory info for product 12345, Price info for product 12345, Review info for product 12345]
betterMethod time cost: 253 ms

可以看出,使用CompletableFuture进行优化后,消耗时间大幅度缩短。

扩展阅读

感兴趣的读者可以阅读下面这个链接,看下美团技术团队是如何利用CompletableFuture优化外卖商家端API这个核心API的。

美团技术团队-外卖商家端API的异步化

总结

除了上述的实例,实际上CompletableFuture还有更多种多样的用法,比如说实现接口的多阶段批量调用等,因此我们在实际使用中可以更加灵活地使用CompletableFuture进行优化。

我后续还会更新【性能优化专题系列】,计划会涵盖前端、后端、网络、操作系统、数据库等一系列内容,希望大家方便的话给我提供一些阅读上的感受和建议,我会根据建议不断优化自己的写作方式,写出更好的博客。

相关文章:

【性能优化专题系列】利用CompletableFuture优化多接口调用场景下的性能

背景说明 在实际的软件开发中&#xff0c;我们经常会遇到需要批量调用接口的场景。例如&#xff0c;电商系统在生成商品详情页时&#xff0c;需要同时调用多个服务接口来获取商品的基本信息、库存信息、价格信息、用户评价等。 传统的依次调用方式存在性能问题 面对上述场景…...

docker安装emqx

emqx安装 拉取emqx镜像 docker pull emqx/emqx:v4.1.0 运行docker容器 docker run -tid --name emqx -p 1883:1883 -p 8083:8083 -p 8081:8081 -p 8883:8883 -p 8084:8084 -p 18083:18083 emqx/emqx:v4.1.0 放行端口 1、如果要是自己的虚拟机&#xff0c;并且关闭了防火墙&a…...

DeepSeek超越ChatGPT的能力及部分核心原理

DeepSeek超越ChatGPT的能力及部分核心原理 目录 DeepSeek超越ChatGPT的能力及部分核心原理超越ChatGPT的能力核心原理超越ChatGPT的能力 推理计算能力更强:在复杂的数学计算、法律文件审查等任务中,DeepSeek的推理能力可媲美甚至超越部分国际顶尖AI模型,包括ChatGPT。例如在…...

Leetcode 3434. Maximum Frequency After Subarray Operation

Leetcode 3434. Maximum Frequency After Subarray Operation 1. 解题思路2. 代码实现 题目链接&#xff1a;3434. Maximum Frequency After Subarray Operation 1. 解题思路 这一题的话我们只需要考察所有的数 i i i转换为 k k k时所能够形成的最大的值。 而对于这个问题&…...

《DeepSeek-R1 问世,智能搜索领域迎来新变革》

DeepSeek-R1是由DeepSeek公司开发的一款创新型人工智能模型&#xff0c;自2024年5月7日发布以来&#xff0c;迅速在AI领域引起广泛关注。该模型凭借其卓越的语言理解能力、高效的数据处理能力、自适应学习能力、高安全性与可靠性以及广泛的应用场景与拓展性&#xff0c;在众多人…...

GEE | 植被总初级生产力GPP的时间变化特征

同学们好&#xff0c;这期我们分享的是植被总初级生产力GPP的日、月、生长季和年变化趋势代码。我们选用的数据集是MODIS/061/MOD17A2HGF&#xff0c;该产品时间跨度为2000-至今&#xff0c;空间分辨率500米&#xff0c;时间分辨率8天。 其中我们把生长季时间设置为了5-9月份&…...

安卓(android)饭堂广播【Android移动开发基础案例教程(第2版)黑马程序员】

一、实验目的&#xff08;如果代码有错漏&#xff0c;可查看源码&#xff09; 1.熟悉广播机制的实现流程。 2.掌握广播接收者的创建方式。 3.掌握广播的类型以及自定义官博的创建。 二、实验条件 熟悉广播机制、广播接收者的概念、广播接收者的创建方式、自定广播实现方式以及有…...

本地部署DeepSeek

1、打开ollama,点击“Download” Ollamahttps://ollama.com/ 2、下载完成后&#xff0c;安装ollama.exe 3、安装完成后&#xff0c;按"windowsR",输入"cmd” 4、输入“ollama -v”&#xff0c;查看版本&#xff0c;表示安装成功 5、返回ollama网页&#xff0c…...

赛博算卦之周易六十四卦JAVA实现:六幺算尽天下事,梅花化解天下苦。

佬们过年好呀~新年第一篇博客让我们来场赛博算命吧&#xff01; 更多文章&#xff1a;个人主页 系列文章&#xff1a;JAVA专栏 欢迎各位大佬来访哦~互三必回&#xff01;&#xff01;&#xff01; 文章目录 #一、文化背景概述1.文化起源2.起卦步骤 #二、卦象解读#三、just do i…...

Hive:窗口函数(1)

窗口函数 窗口函数OVER()用于定义一个窗口&#xff0c;该窗口指定了函数应用的数据范围 对窗口数据进行分区 partition by 必须和over () 一起使用, distribute by经常和sort by 一起使用,可以不和over() 一起使用.DISTRIBUTE BY决定了数据如何分布到不同的Reducer上&#xf…...

docker安装nacos2.2.4详解(含:nacos容器启动参数、环境变量、常见问题整理)

一、镜像下载 1、在线下载 在一台能连外网的linux上执行docker镜像拉取命令 docker pull nacos:2.2.4 2、离线包下载 两种方式&#xff1a; 方式一&#xff1a; -&#xff09;在一台能连外网的linux上安装docker执行第一步的命令下载镜像 -&#xff09;导出 # 导出镜像到…...

基于PLC的变频调速系统设计

摘要 现代科技发展迅速&#xff0c;特别是通讯技术的发展&#xff0c;工业现场提供了便捷的数据交互和控制的手段&#xff0c;将工业现场的仪表、驱动器、控制器以及上位机之间进行通讯连接&#xff0c;进行相互信息交互&#xff0c;数据准确高效的传送&#xff0c;并且对现场的…...

鸿蒙开发在onPageShow中数据加载不完整的问题分析与解决

API Version 12 1、onPageShow()作什么的 首先说明下几个前端接口的区别&#xff1a; ArkUI-X的aboutToAppear()接口是一个生命周期接口&#xff0c;用于在页面即将显示之前调用。 在ArkUI-X中&#xff0c;aboutToAppear()接口是一个重要的生命周期接口&#xff0c;它会在页…...

本地搭建deepseek-r1

一、下载ollama(官网下载比较慢&#xff0c;可以找个网盘资源下) 二、安装ollama 三、打开cmd&#xff0c;拉取模型deepseek-r1:14b(根据显存大小选择模型大小&#xff09; ollama pull deepseek-r1:14b 四、运行模型 ollama run deepseek-r1:14b 五、使用网页api访问&#x…...

【数据结构与算法】AVL树的插入与删除实现详解

文章目录 前言Ⅰ. AVL树的定义Ⅱ. AVL树节点的定义Ⅲ. AVL树的插入Insert一、节点的插入二、插入的旋转① 新节点插入较高左子树的左侧&#xff08;左左&#xff09;&#xff1a;右单旋② 新节点插入较高右子树的右侧&#xff08;右右&#xff09;&#xff1a;左单旋③ 新节点插…...

【机器学习】自定义数据集 使用pytorch框架实现逻辑回归并保存模型,然后保存模型后再加载模型进行预测,对预测结果计算精确度和召回率及F1分数

一、使用pytorch框架实现逻辑回归 1. 数据部分&#xff1a; 首先自定义了一个简单的数据集&#xff0c;特征 X 是 100 个随机样本&#xff0c;每个样本一个特征&#xff0c;目标值 y 基于线性关系并添加了噪声。将 numpy 数组转换为 PyTorch 张量&#xff0c;方便后续在模型中…...

unity学习23:场景scene相关,场景信息,场景跳转

目录 1 默认场景和Assets里的场景 1.1 scene的作用 1.2 scene作为project的入口 1.3 默认场景 2 场景scene相关 2.1 创建scene 2.2 切换场景 2.3 build中的场景&#xff0c;在构建中包含的场景 &#xff08;否则会认为是失效的Scene&#xff09; 2.4 Scenes in Bui…...

AI(计算机视觉)自学路线

本文仅用来记录一下自学路线方便日后复习&#xff0c;如果对你自学有帮助的话也很开心o(*&#xffe3;▽&#xffe3;*)ブ B站吴恩达机器学习->B站小土堆pytorch基础学习->opencv相关知识&#xff08;Halcon或者opencv库&#xff09;->四类神经网络&#xff08;这里跟…...

Linux第104步_基于AP3216C之I2C实验

Linux之I2C实验是在AP3216C的基础上实现的&#xff0c;进一步熟悉修改设备树和编译设备树&#xff0c;以及学习如何编写I2C驱动和APP测试程序。 1、AP3216C的原理图 AP3216C集成了一个光强传感器ALS&#xff0c;一个接近传感器PS和一个红外LED&#xff0c;为三合一的环境传感…...

常用Android模拟器(雷电 MuMu 夜神 Genymotion 蓝叠) - 20250131

常用Android模拟器(雷电 MuMu 夜神 Genymotion 蓝叠) - 20250131 Android模拟器概述 Android 模拟器是一种软件工具&#xff0c;允许用户在 Windows、Linux 或 macOS 电脑上运行 Android 操作系统&#xff0c;以模拟 Android 设备的行为。它广泛用于 开发测试、应用运行、游戏…...

网络六边形受到攻击

大家读完觉得有帮助记得关注和点赞&#xff01;&#xff01;&#xff01; 抽象 现代智能交通系统 &#xff08;ITS&#xff09; 的一个关键要求是能够以安全、可靠和匿名的方式从互联车辆和移动设备收集地理参考数据。Nexagon 协议建立在 IETF 定位器/ID 分离协议 &#xff08;…...

Xshell远程连接Kali(默认 | 私钥)Note版

前言:xshell远程连接&#xff0c;私钥连接和常规默认连接 任务一 开启ssh服务 service ssh status //查看ssh服务状态 service ssh start //开启ssh服务 update-rc.d ssh enable //开启自启动ssh服务 任务二 修改配置文件 vi /etc/ssh/ssh_config //第一…...

UR 协作机器人「三剑客」:精密轻量担当(UR7e)、全能协作主力(UR12e)、重型任务专家(UR15)

UR协作机器人正以其卓越性能在现代制造业自动化中扮演重要角色。UR7e、UR12e和UR15通过创新技术和精准设计满足了不同行业的多样化需求。其中&#xff0c;UR15以其速度、精度及人工智能准备能力成为自动化领域的重要突破。UR7e和UR12e则在负载规格和市场定位上不断优化&#xf…...

分布式增量爬虫实现方案

之前我们在讨论的是分布式爬虫如何实现增量爬取。增量爬虫的目标是只爬取新产生或发生变化的页面&#xff0c;避免重复抓取&#xff0c;以节省资源和时间。 在分布式环境下&#xff0c;增量爬虫的实现需要考虑多个爬虫节点之间的协调和去重。 另一种思路&#xff1a;将增量判…...

均衡后的SNRSINR

本文主要摘自参考文献中的前两篇&#xff0c;相关文献中经常会出现MIMO检测后的SINR不过一直没有找到相关数学推到过程&#xff0c;其中文献[1]中给出了相关原理在此仅做记录。 1. 系统模型 复信道模型 n t n_t nt​ 根发送天线&#xff0c; n r n_r nr​ 根接收天线的 MIMO 系…...

Java线上CPU飙高问题排查全指南

一、引言 在Java应用的线上运行环境中&#xff0c;CPU飙高是一个常见且棘手的性能问题。当系统出现CPU飙高时&#xff0c;通常会导致应用响应缓慢&#xff0c;甚至服务不可用&#xff0c;严重影响用户体验和业务运行。因此&#xff0c;掌握一套科学有效的CPU飙高问题排查方法&…...

Linux C语言网络编程详细入门教程:如何一步步实现TCP服务端与客户端通信

文章目录 Linux C语言网络编程详细入门教程&#xff1a;如何一步步实现TCP服务端与客户端通信前言一、网络通信基础概念二、服务端与客户端的完整流程图解三、每一步的详细讲解和代码示例1. 创建Socket&#xff08;服务端和客户端都要&#xff09;2. 绑定本地地址和端口&#x…...

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数

高效线程安全的单例模式:Python 中的懒加载与自定义初始化参数 在软件开发中,单例模式(Singleton Pattern)是一种常见的设计模式,确保一个类仅有一个实例,并提供一个全局访问点。在多线程环境下,实现单例模式时需要注意线程安全问题,以防止多个线程同时创建实例,导致…...

JVM虚拟机:内存结构、垃圾回收、性能优化

1、JVM虚拟机的简介 Java 虚拟机(Java Virtual Machine 简称:JVM)是运行所有 Java 程序的抽象计算机,是 Java 语言的运行环境,实现了 Java 程序的跨平台特性。JVM 屏蔽了与具体操作系统平台相关的信息,使得 Java 程序只需生成在 JVM 上运行的目标代码(字节码),就可以…...

并发编程 - go版

1.并发编程基础概念 进程和线程 A. 进程是程序在操作系统中的一次执行过程&#xff0c;系统进行资源分配和调度的一个独立单位。B. 线程是进程的一个执行实体,是CPU调度和分派的基本单位,它是比进程更小的能独立运行的基本单位。C.一个进程可以创建和撤销多个线程;同一个进程中…...