记录一次大数据量接口优化过程

news2024/12/22 20:51:35

问题描述

记录一次大数据量接口优化过程。最近在优化一个大数据量的接口,是提供给安卓端APP调用的,因为安卓端没做分批次获取,接口的数据量也比较大,因为加载速度超过一两分钟,所以导致接口超时的异常,要让安卓APP分批次调用需要收取费用,所以只能先优化一下接口的速度。

分析问题

先使用阿里开源的监控工具Arthas来分析接口,Arthas 是一款线上监控诊断平台,可以实时查看应用 load、内存、gc、线程的状态信息,可以在不修改代码的情况,定位问题,分析接口耗时、传参、异常等情况,提高线上问题排查效率
在这里插入图片描述
找到对应的接口代码,使用Arthas的trace命令跟踪一下接口耗时情况,listOrder是对应方法名,skipJDKMethod是不打印jdk里面的方法

trace com.sample.order.orderServiceImpl listOrder -n 1  --skipJDKMethod

要筛选出响应时间大于1000毫秒的,可以使用如下命令

trace com.sample.order.orderServiceImpl listOrder '#cost > 1000' -n 1

通过Arthas就可以分析出一个接口方法里具体那个调用耗时,然后一层层分析即可,Arthas分析的接口大致如下,仅供参考

--[100.00% 3434.755668ms ] org.springframework.cglib.proxy.MethodInterceptor:intercept()
            `---[100.00% 3434.620187ms ] cn.test.server.business.VisitorBusiness:getStaffList()
                +---[0.00% 0.045431ms ] cn.test.client.dto.StaffListReqDto:getComId() #188
                +---[0.00% 0.019135ms ] cn.core.common.utils.CoreUtils:isEmpty() #188
                +---[0.00% 0.021816ms ] cn.hutool.core.date.DateUtil:timer() #192
                +---[0.00% 0.019987ms ] com.google.common.base.Splitter:on() #194
                +---[0.00% 0.005501ms ] cn.test.client.dto.StaffListReqDto:getComId() #194
                +---[0.00% 0.026292ms ] com.google.common.base.Splitter:splitToList() #194
                +---[0.00% 0.131507ms ] cn.hutool.core.convert.Convert:toInt() #195
                +---[0.34% 11.668407ms ] cn.core.user.client.querier.SchoolQuerierService:findBySchoolId() #198
                +---[0.00% 0.024199ms ] cn.hutool.core.date.TimeInterval:intervalRestart() #203
                +---[0.02% 0.535107ms ] org.slf4j.Logger:info() #203
                +---[2.66% 91.504718ms ] cn.core.user.client.querier.RelationQuerierService:queryUserByIdsAndRoleType() #206
                +---[0.00% 0.019208ms ] com.google.common.collect.Lists:newArrayList() #206
                +---[0.00% 0.01107ms ] cn.core.common.utils.CoreUtils:isEmpty() #207
                +---[0.00% 0.013517ms ] cn.hutool.core.date.TimeInterval:intervalRestart() #211
                +---[0.02% 0.532242ms ] org.slf4j.Logger:info() #211
                +---[0.00% 0.016216ms ] cn.hutool.core.date.TimeInterval:intervalRestart() #218
                +---[0.01% 0.435469ms ] org.slf4j.Logger:info() #218
                +---[0.00% 0.009024ms ] cn.test.client.dto.StaffListReqDto:getComId() #221
                +---[0.53% 18.336268ms ]cn.test.persistence.dao.LogMapper:listStaffSyncRecordByComId() #221
                +---[0.00% 0.009043ms ] com.google.common.collect.Lists:newArrayList() #221
                +---[0.00% 0.019237ms ] cn.hutool.core.date.TimeInterval:intervalRestart() #223
                +---[0.01% 0.279834ms ] org.slf4j.Logger:info() #223
                +---[0.00% 0.014057ms ] cn.hutool.core.date.TimeInterval:intervalRestart() #227
                +---[0.01% 0.270155ms ] org.slf4j.Logger:info() #227
                +---[0.00% 0.006338ms ] com.google.common.collect.Lists:newArrayList() #231
                +---[0.00% 0.019707ms ] cn.hutool.core.date.TimeInterval:intervalRestart() #263
                +---[0.02% 0.548875ms ] org.slf4j.Logger:info() #263
                `---[0.00% 0.027475ms ] cn.test.client.dto.Result:success() #265

通过Arthas分析问题,定位到接口里,是因为查询出所有的数据后,再循环这些数据,在循环里又调了API去做业务处理,针对这种情况,怎么做调优?

处理问题

针对这种情况,我想到了分批次,分页来获取接口比较好,但是安卓端要改,需要收费额外的费用,所以我只能用多线程来做分批次处理了,JDK8里提供了CompletableFuture这个api来处理多任务,多线程,所以使用这个API加上线程池来处理接口

public OrderResult<List<OrderListDto>> getOrderList(ListReqDto reqDto) throws ExecutionException, InterruptedException {
        if (CoreUtils.isEmpty(reqDto.getComId())) {
            return OrderResult.fail("comId can not be null");
        }
		// Hutool计时器
        TimeInterval timer = DateUtil.timer();

       
        EmsReqVo reqVo = new EmsReqVo();
        reqVo.setComId(reqDto.getComId());
        List<OrderRecVo> orderRecList = Optional.ofNullable(orderMapper.queryOrderRecVo(reqVo )).orElse(Lists.newArrayList());
        LOG.info("查询所有预约订单:{}", timer.intervalRestart());
        if (CollUtil.isEmpty(orderRecList )) {
            return OrderResult.success(Lists.newArrayList());
        }
		
		// 任务列表
        List<CompletableFuture<OrderListDto>> fList = new ArrayList<>();
        // 自定义线程池
        ExecutorService executor = new ThreadPoolExecutor(
                10, 100, 5,
                TimeUnit.MINUTES,
                new ArrayBlockingQueue<>(10000)
        );
        List<OrderListDto> orderDtoList= Lists.newArrayList();
        orderRecList.stream().forEach(v -> {
            CompletableFuture<OrderListDto> f = CompletableFuture.supplyAsync(
                    ()-> {
                        Long userId = v.getUserId;
						// 根据userId去调用户数据,比较耗时
						UserDto userDto = userService.selectOne(userId);
						
                        // 封装参数返回
                        OrderListDto orderListDto = OrderListDto.builder()
                                .code(generator(v.getId())) // 流水号
                                .name(v.getOwnerName()) // 预约人姓名
                                .sex(gender)           // 预约人性别
                                .identity(v.getIdentityNum()) // 证件号码
                                .addr(v.getAddress()) // 证件地址
                                .tel(v.getMobile()) // 联系电话
                                .build();
                        return orderListDto;
                    },
                    executor
            );
            fList.add(f);
        });
        // 获取所有的任务
        CompletableFuture<Void> all= CompletableFuture.allOf(fList.toArray(new CompletableFuture[0]));
        CompletableFuture<List<OrderListDto>> allInfo = all.thenApply(v-> fList.stream().map(a-> {
            try {
                return a.get();
            } catch (InterruptedException | ExecutionException e) {
                e.printStackTrace();
            }
            return null;
        }).collect(Collectors.toList()));
        // 返回list
        orderDtoList= allInfo.get();
        // 关闭线程池
        executor.shutdown();

        LOG.info("查询预约订单记录 use:{}", timer.intervalRestart());

        return OrderResult.success(orderDtoList);
    }

通过CompletableFuture多任务处理,接口速度提高都十几秒
在这里插入图片描述

所以需要继续调优,在循环里调api会有网络带宽的影响,所以改成通过userId的集合去获取所有数据,封装成一个map集合,在循环里调用

 List<Integer> userIds = orderRecList.stream().map(OrderRecVo::getReceiveId).collect(Collectors.toList());
 List<UserDto> usersList = Optional.ofNullable(userService.getUsersByIds(userIds)).orElse(Lists.newArrayList());
 Map<Integer, UserDto> usersMap = usersList.stream().collect(Collectors.toMap(UserDto::getId, u -> u));

在循环里再通过map获取即可

UserDto userDto = Optional.ofNullable(usersMap.get(v.getUserId())).orElse(new TinyUserDto());

通过所有id集合获取总的所有数据,再通过map去获取的方式,接口速度快了很多,大概到毫秒级别
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1634593.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【论文阅读】IPT:Pre-TrainedImageProcessingTransformer

Pre-TrainedImageProcessingTransformer 论文地址摘要1. 简介2.相关作品2.1。图像处理2.2。 Transformer 3. 图像处理3.1. IPT 架构3.2 在 ImageNet 上进行预训练 4. 实验4.1. 超分辨率4.2. Denoising 5. 结论与讨论 论文地址 1、论文地址 2、源码 摘要 随着现代硬件的计算能…

python数据可视化:雷达图

【小白从小学Python、C、Java】 【计算机等考500强证书考研】 【Python-数据分析】 python数据可视化&#xff1a; 雷达图 选择题 关于以下代码输出的雷达图中&#xff0c;以下说法正确的是&#xff1f; import numpy as np import matplotlib.pyplot as plt from pylab impor…

【酱浦菌-爬虫项目】爬取学术堂宏观经济学论文原文

前言 首先给大家放出完整代码&#xff0c;然后下面就是用jupyter写的代码。实际上在写的时候用的是jupyter写的&#xff0c;因为感觉jupyter写的时候更加的流畅&#xff0c;每一步运行的细节都能保存下来&#xff0c;更方便学习理解。 完整代码&#xff1a; import os impo…

智能售货机:塑造未来零售新貌

智能售货机&#xff1a;塑造未来零售新貌 随着科技的飞速跃进&#xff0c;零售业态经历了一场深刻的转型&#xff0c;其中&#xff0c;智能售货机凭借其创新技术和灵活应用&#xff0c;正逐步成为新零售领域的焦点。本文旨在探讨智能售货机的市场演进路径、最新趋势&#xff0…

【AIGC调研系列】LLaVA++整合Phi-3和Llama-3能够实现什么

LLaVA能够为Phi-3和Llama-3带来的主要好处包括&#xff1a; 视觉处理能力的增强&#xff1a;通过整合Phi-3和Llama-3模型&#xff0c;创建了具备视觉处理能力的Phi-3-V和Llama-3-V版本&#xff0c;这意味着这些模型现在能够理解和生成与图像相关的内容[1]。这种能力的增加&…

智慧旅游驱动行业革新:智能技术引领服务全面升级,匠心打造高品质、个性化旅游新体验

一、引言 随着科技的飞速发展和信息化程度的不断提高&#xff0c;智慧旅游正逐渐成为旅游业发展的新趋势。智慧旅游&#xff0c;顾名思义&#xff0c;是以智能化技术为支撑&#xff0c;通过大数据、云计算、物联网、人工智能等先进技术的应用&#xff0c;实现旅游服务的全面升…

Web前端一套全部清晰 ⑤ day3 列表 表格 表单标签 综合案例

人生是一直向前无法倒退的旅程&#xff0c;所以可以偶尔回头&#xff0c;但一定要往前看 —— 24.4.29 一、综合案例1-体育新闻列表 <!DOCTYPE html> <html lang"zh-CN"> <head><meta charset"UTF-8"><meta name"viewport…

2024-04学习笔记

1.sql优化-子查询改为外连接 1.改之前 改之前是这样&#xff0c;那针对查出来的每一条数据&#xff0c;都要执行一次箭头所指的函数 执行的sql很慢 2.改之后 改之后是这样&#xff0c;整体做外连接&#xff0c;不用每一条都再执行一次查询 执行时间缩短了好几倍 2.Mybatis中…

21.Nacos集群搭建

模拟Nacos三个节点&#xff0c;同一个ip,启动三个不同的端口&#xff1a; 节点 nacos1, 端口&#xff1a;8845 节点 nacos2, 端口&#xff1a;8846 节点 nacos3, 端口&#xff1a;8847 1.搭建数据库&#xff0c;初始化数据库表结构 这里我们以单点的数据库为例 首先新建一…

Facebook全攻略:从注册到养号再到防封,一篇搞定!

作为海外热门的社交媒体平台之一&#xff0c;Facebook已经成为品牌营销的重要渠道。很多新手小白在拿到Facebook账号后还是不知道如何操作&#xff0c;今天为大家准备了一份Facebook操作全攻略&#xff0c;从注册、养号到防封号&#xff0c;让你的Facebook跨境之旅更加顺畅&…

小程序地理位置接口怎么开通?

小程序地理位置接口有什么功能&#xff1f; 如果我们提审后驳回理由写了“当前提审小程序代码包中地理位置相关接口( chooseAddress、getLocation )暂未开通&#xff0c;建议完成接口开通后或移除接口相关内容后再进行后续版本提审”&#xff0c;如果你也碰到类似问题&#xf…

Ansys Speos|进行智能手机镜头杂散光分析

本例的目的是研究智能手机Camera系统的杂散光。杂散光是指光向相机传感器不需要的散光光或镜面光&#xff0c;是在光学设计中无意产生的&#xff0c;会降低相机系统的光学性能。 在本例中&#xff0c;光学透镜系统使用Ansys Zemax OpticStudio (ZOS)进行设计&#xff0c;并使用…

使用 GitHub Actions 实现项目的持续集成(CI)

目录 什么是 GitHub Actions 基础概念 Workflow 文件 Workflow 语法 实例&#xff1a;编译 OpenWrt 什么是 GitHub Actions GitHub Actions 是 GitHub 推出的持续集成&#xff08;Continuous Integration&#xff0c;简称 CI&#xff09;服务它允许你创建自定义工作流&am…

源码编译framework.jar 并成功导入android studio 开发

一、不同安卓版本对应路径 Android N/O: 7 和 8 out/target/common/obj/JAVA_LIBRARIES/framework_intermediates/classes.jar Android P/Q: 9 和 10 out/soong/.intermediates/frameworks/base/framework/android_common/combined/framework.jar Android R: 11以上 out/so…

Qt下使用7Z源码进行压缩和解压缩

7Z压缩是一款常用的压缩算法和工具&#xff0c;本文主要介绍一款在qt环境下进行编译的压缩方法。 本人测试是可以正常跑通的&#xff0c;具体代码部分请下载&#xff1a;下载链接&#xff0c;提取码&#xff1a;ev9t 7z源码网址&#xff1a;7-Zip 7z简介&#xff1a; 7z 是…

有趣的大模型之我见 | Llama AI Model

Llama 开源吗&#xff1f; 我在写《有趣的大模型之我见 | Mistral 7B 和 Mixtral 8x7B》时曾犹豫&#xff0c;在开源这个事儿上&#xff0c;到底哪个大模型算鼻祖&#xff1f;2023 年 7 月 18 日&#xff0c;Meta 推出了最受欢迎的大型语言模型&#xff08;LLM&#xff09;的第…

opencv_23_高斯模糊

void ColorInvert::gaussian_blur(Mat& image) { Mat dst; GaussianBlur(image, dst, Size(0, 0), 15); // Size(2, 2), imshow("图像模糊2", dst); }

MySQL中SELECT语句的执行过程

2.1.1. 一条SELECT语句的执行过程 MySQL 的架构共分为两层&#xff1a;Server 层和存储引擎层 Server层负责建立连接、分析和执行SQL存储引擎层负责数据的存储和提取&#xff0c;支持 InnoDB、MyISAM、Memory 等多个存储引擎&#xff0c;MySQL5.5以后默认使用InnoDB&#xff0…

什么是DDoS攻击?怎么防御DDoS攻击?

在网络安全领域&#xff0c;DDoS攻击一直是热门话题&#xff0c;随着网络技术的不断发展和网络环境的复杂化演变&#xff0c;DDoS攻击变得愈加频繁、更具破坏性。根据2023年网络安全态势研判分析年度综合报告&#xff0c;全年全网网络层的DDoS攻击次数达2.51亿次&#xff01;本…

五一前的最后一个工作日

最近在学习 Elasticsearch 的使用&#xff0c;也更了几篇文章了&#xff0c;后续的话应该要等到节后再说了&#xff08;因为真的背不动电脑回家&#xff09; 再来看下这次五一假期的组成&#xff0c;1 号到 5 号&#xff0c;共五天&#xff0c;其中 2 号是 28 号周日调休来的&a…