Java并发之美 记录一次业务优化 从9.5小时 到1小时 再到28分钟 并行执行任务

前情介绍
规则:
  • 同步自己系统中的11417条数据到友军系统中
  • 对方拒绝提供批量操作接口(必须一条一条通过网络进行传输 总共要进行11417个网络请求)
  • 对方接口每秒请求不能超过200次(实际测试超过10次就开始404)
  • 我方数据库要保证查库在2000次/秒这个范围内 防止操作先把自家数据库搞垮
    在这里插入图片描述
首次操作
流程:
  • 分页从库中读取11417条数据 读取6张关联表中的相关数据(每页200条 加上关联表总共7张表每次处理1400条数据)
  • 封装7张表的数据 转换为友军需要的数据结构
  • 进行网络请求 将数据发送给友军
  • 将操作记录写入日志表中 方便结算
实操后发现的问题
可想而知的问题:
  • 一条数据的流程走下来大概需要3秒中
  • 11417条数据总耗时:34,251秒、570分钟、9.5小时

怀着复杂的心情 跑了十分钟后 直接停掉了任务 赶紧和对方商量批量发送的接口
无情被拒绝 疲软的产品更是别人说什么就是什么
我极力推荐加个批量操作接口 最后对方就是不改 交涉无果还被定了个工作态度不好的名声
但 一切也阻挡不了我这颗优化的心!

优化过程

并行并行… 这个词无数次的在脑中回响

先计算下理想值:
  • 对面接口限制200次每秒 11417/200 = 57秒 乘以单次耗时3秒 171秒完事
  • 我方数据库限制2000次每秒 完全可以支持对面200次每秒的数据需求
初次优化
  • 分页从库中读取11417条数据 读取6张关联表中的相关数据(每页200条 加上关联表总共7张表每次处理1400条数据)
  • 封装7张表的数据 转换为友军需要的数据结构
  • 因为接口限制 先开启200个线程 并行进行网络请求 将数据发送给友军
  • 将操作记录写入日志表中 方便结算
    在这里插入图片描述
// 开启多个线程 并行执行同步任务
ExecutorService executor = Executors.newFixedThreadPool(10);
List<CompletableFuture<HqzcPolicyInspectorLog>> futures =
        list.stream().map(bean -> CompletableFuture.supplyAsync(() -> upLoadDataBean(bean.getInfo(), bean.getResult(), bean.getDataBean(), lastUpdateTime, type), executor)).collect(Collectors.toList());
inspectorLogs = futures.stream().map(s -> s.join()).collect(Collectors.toList());
// 关闭线程池
executor.shutdown();

怀着激动不已的心情开启了测试之路
感觉不太对 200个线程并没有真的发出200次请求 基本每秒请求数量在5-8次
怀疑是每次执行任务 网络请求等耗时3秒导致的
于是加线程到600 600/3 = 200 希望可以达到200次每秒的峰值
结果 对方开始疯狂404……
改低400个线程 还是404
最终测试 并发200个线程下耗时:58分钟

二次优化
  • 计算出200条一页 总共有多少页数据
  • 开启多个线程 并行从数据库获取数据 分页从库中读取11417条数据 读取6张关联表中的相关数据(每页200条 加上关联表总共7张表每次处理1400条数据)
  • 封装7张表的数据 转换为友军需要的数据结构
  • 因为接口限制 先开启200个线程 并行进行网络请求 将数据发送给友军
  • 将操作记录写入日志表中 方便结算
    在这里插入图片描述

此次方案优化为两次并行 并行从数据库中取数据 并行将取出来的数据发送给友军 最终耗时29分钟
测试三次的战绩:27分钟、28分钟、29分钟
多线程并行下 一定要注意安全问题 不用拖垮自家服务器、数据库、第三方接口…
在这里插入图片描述

long startTime = System.currentTimeMillis();
int pageSize = 200;
Integer count = mPolicyInfoDao.getCount();
int totalPage = (count / pageSize) + 1;
// 限制最大20个线程 防止友军接口404
int maxPoolSize = Math.min(totalPage, 10);
ArrayList<Integer> pages = new ArrayList<>();
for (int i = 1; i <= totalPage; i++) {
    pages.add(i);
}
// 开启多个线程 并行执行同步任务
ExecutorService executor = Executors.newFixedThreadPool(maxPoolSize);
List<CompletableFuture<Integer>> futures =
        pages.stream().map(bean -> CompletableFuture.supplyAsync(() -> {
            // 切换到mysql数据源
            DataSourceContext.setMysqlHqzcDataSource();
            PageHelper.startPage(bean, pageSize);
            List<HqzcPolicyInfo> infos = mPolicyInfoDao.selectAll();
            // 同步数据到第三方平台
            syncData2ThirdPlant(infos, lastUpdateTime, type);
            return bean;
        }, executor)).collect(Collectors.toList());
List<Integer> integers = futures.stream().map(s -> s.join()).collect(Collectors.toList());
// 关闭线程池
executor.shutdown();
long endTime = System.currentTimeMillis();
System.out.println("使用多线程进行同步耗时:" + ((endTime - startTime) / 60 / 1000) + "分钟");
流程图

初代:
在这里插入图片描述
二代:
在这里插入图片描述
最终流程
在这里插入图片描述

结语:

技术外 有种东西 叫做 —— power
一次产品经理的妥协 就是一段烂代码的开始~

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值