首先用一张图说明一下各个系统直接的调用关系
系统详细说明如下
系统A:框架使用springboot开发,对外提供http接口,数据通过RPC(这里指HSF,以下统一用HSF)调用系统B
系统B:框架使用springboot开发,对外暴露HSF接口,数据通过HSF访问系统C获取
系统C:框架不明,对外支持HSF调用,最终获取DB数据返回
问题描述
用户访问系统A中的一个订单下载功能,数据大概在600条左右,在系统A中原方式是通过分页查询(每次查询10条)查询60多次数据库在用户端出现的问题如下图
当时首先想到的问题是增加服务器的连接时间(连接时间改为3分钟),优化查询(每次改为查询50条)
然而改完之后速度提升不是很明显,而且还出现新的问题
1、超时问题依旧存在
2、有时文件可以下载,但是数据不全,有时少50条,有时少150条
出现这种情况的解决方案是在系统中加入大量的日志排查原因,最终发现系统A在访问系统B获取的分页数据,有时候有数据,有时候又没有数据,只能在系统B中继续跟踪日志排查,发现系统B在访问系统C时有超时情况,当超时时系统返回的空数据。
系统C属于第三方的系统,我们无法在其中做出优化,虽然系统C的负责人声称DB已经做了优化,性能较原来有十倍提升(反正我是不信的)
所有到目前为止该下载功能还存在两个问题
1、系统调用时间过长
2、系统可能数据不全
由于系统C没有优化空间,对于这两个问题我们分别给出了优化方案
方案一:使用多线程同时对分页进行数据查询
方案二:使用重试机制保证数据的完整(目前最多重试三次),当然这也没法完全保证数据完整,但也不能无限去重试,否则又会导致tomcat或者nginx连接超时
虽然这不是完整方案,但也是目前情况下给出的可以实施的方案了,具体实现代码如下
多线程并行使用的是java8提供的IntStream类实现,在系统A中实现
long start = System.currentTimeMillis();
final List<OrderVo> synchronizedResult =Collections.synchronizedList(Lists.newArrayList());
int totalPages = "获取总页码数";
IntStream.range(2, totalPages+1).parallel().forEach(i->{
orderQueryParam.setPageIndex(i);
PageableList<OrderVo> subResult = queryPage(orderQueryParam);
if (CollectionUtils.isNotEmpty(subResult.getList())) {
synchronizedResult.addAll(subResult.getList());
}
});
log.info("queryPage result use {}ms", System.currentTimeMillis()-start);
查询数据重试代码在系统B中实现,代码如下
//传入isRetry控制查询是否需要重试:DEFAULT_RETRY=3
int retry =isRetry ? DEFAULT_RETRY : 1;
PagerList<Order> pagerList =null;
while(retry-->0) {
try {
pagerList = systemCService.listOrders(params);
if(pagerList != null && CollectionUtils.isNotEmpty(pagerList.getDatas())) {
break;
}
}catch(Exception e) {
log.error("queryPage error :",e);
}
}
ok,代码设计完成,在预发测试,测试结果:600多条数据用时30多秒(优化前nginx设置3分钟依旧超时),数据与查询出来的数据条数一致,问题算是解决。
但这种方案又有新的问题产生
1、使用多线程访问系统C是否有风险,系统C是否会出现压力过大导致系统宕机等问题
2、多线程异步查询出的数据和在页面看到的数据顺序不一致
对于这两个问题暂时先保留,后续继续给出方案吧