双11临近,领导让我们排查负责项目往年双11流量情况,预估服务是否有扩容需求
打开grafana看到有一个服务cpu使用总是100%+,代码拉取到本地开始翻代码
主要排查并行计算:多线程、parallelStream、completeableFeature、ForkJoinPool
一顿搜索发现如下代码:
Map<String,String> coordinateMap = Maps.newHashMap();
bizOptionRoomFloorInfoDOList.parallelStream().forEach(f -> {
if (StringUtils.isNotBlank(f.getFloorCoordinate())) {
coordinateMap.put(StringUtils.join(f.getBuildingNo(), f.getFloorNo()), f.getFloorCoordinate());
}
if (StringUtils.isNotBlank(f.getRoomCoordinate())) {
coordinateMap.put(f.getRoomNo(), f.getRoomCoordinate());
}
});
List<PmsOptionalHallInfoResponseDTO> pmsHallInfoResponseDTOList = pms2020BaseResponse.getTarget();
pmsHallInfoResponseDTOList.parallelStream().forEach(h -> {
h.getFloorResponseDTOList().parallelStream().forEach(f -> {
if ((coordinateMap.containsKey(StringUtils.join(h.getHallId(),f.getFloorNo())))){
f.setCoordinate(coordinateMap.get(StringUtils.join(h.getHallId(), f.getFloorNo())));
}
f.getRoomResponseDTOList().parallelStream().forEach(r -> {
if (coordinateMap.containsKey(r.getRoomNo())){
r.setCoordinate(coordinateMap.get(r.getRoomNo()));
}
});
});
});
写这段代码的哥们已经离职了,所以放心的吐槽了一波;(本身多线程场景使用HashMap也存在线程安全问题)
lambda parallelStream只是用来做了数据封装的操作,其实效率是不如for循环的(4核8G);jdk本身对for循环做了优化;数据拼装类的代码jdk本身也会jit优化
由于parallelStream底层是基于ForkJoinPool,而ForkJoinPool默认的线程数为cpu核数(4线程);
解决思路:
1:将ForkJoinPool/多线程 线程数调低(这种方式不建议在数据拼装等场)
2:cpu密集性场景线程池数量尽量小(需要结合计算吞吐量进行调优)
3:修改为for循环
所以优化后上线cpu稳定在30左右;并且接口平均耗时也比原来低一些