1 背景
- 降低服务器和带宽等硬件成本:用更少的资源处理更多的请求
- 提高现实世界的运行效率:人机处理效率存在数量级的偏差,同样机器世界的效率提升能带来现实世界效率提升的方法效果
- 提高用户的体验:解决响应缓慢、宕机等问题
而并行优化在改善程序接口响应时间和吞吐量指标方面是个利器,所以本次结合前段时间做的一段长链路执行逻辑代码的优化,给大家讲讲程序并行优化的步骤及方法论。
2 多线程优化六步法
2.1 定位优化点
2.2 执行链路分析
对问题点的执行链路进行分析,主要分几方面:
- 链路里涉及的操作节点;
- 节点自身的耗时;是io密集型还是cpu密集型;是否依赖和修改外部变量;此节点是否是核心路径;
- 节点间彼此依赖关系;
2.3 异步链路设计
- 将链路根据依赖关系进行重排,把被依赖的放在前面;
- 彼此不依赖有相同起点的节点并行化;设计并行任务结果获取及后续依赖节点的通知机制
- 如果有指定响应时间目标的链路,为核心路径节点设计降级方案;根据响应时间要求及已耗时数据对非核心路径节点调用进行舍弃;
- 将对变量修改的逻辑收拢,且尽量在主线程中处理,避免需要做的多线程变量可见性和时序性同步
2.4 并发框架选择
1.线程池
优点:复用线程,减少线程创建销毁成本及减少请求时延
2.AKKA
优点:不需要关注多线程之间并发同步和数据一致性;轻量级高并发
3.REACTOR
优点:rule采用pull处理消息,避免消息积压;异步非阻塞io,避免阻塞当前线程
2.5 并发工具选择
多线程执行涉及到一系列细节问题,如共享变量可见性,执行顺序,结果的获取、后续操作的通知等,所以要结合需求使用一系列相关的并发工具类做多线程执行正确性的保障
2.6 效果验证
1.压测
注意点:
- 完全相同的环境以及测试负载
- 注意混部情况其他服务可能对验证服务造成的影响
- 通过加压减压调整请求量观察服务器处理能力的变化及稳定性
2.性能指标验证
- 验证并发用户数、响应时间及吞吐量这种调优目标量;
- 观察服务器的负载指标,防止因优化带来服务器超出负载能力;
- 观察上下游服务的业务指标和服务器负载,防止因优化带来上下游超出负载能力
3.业务结果验证
3 举例
以我们前段时间进行的商品主数据下发消费能力调优进行举例说明整个优化过程:
3.1 优化点定位
3.2 执行链路分析
梳理各步骤对入参和保存时需要的变量的处理,分析各步骤相互依赖关系,是否可并行,进行执行过程优化调整。
3.3 异步链路设计
- 1、 3、4、5异步并行处理,且因对其他变量修改逻辑无依赖,放在最前面提交。
- 2、7、8、9、10、11根据依赖关系,把相关性的逻辑收拢,把被依赖的逻辑提前。
- 13也异步提交。最后通过completionService.take().get()遍历获取各任务执行结果进行合并返回最终结果
3.4 并发框架选择
/**
* io任务线程池
*/
public static ThreadPoolExecutor threadPoolExecutorForIO= new ThreadPoolExecutor(Runtime.getRuntime().availableProcessors(),Runtime.getRuntime().availableProcessors()*2,1, TimeUnit.MINUTES,new ArrayBlockingQueue(2014),new ThreadPoolExecutor.CallerRunsPolicy());
3.5 并发工具选择
这里使用CompletionService来获取多线程的执行结果,并进行结果归集。
CompletionService通过在线程结果完成时提交到阻塞队列,避免通过遍历future结果的方式导致先提交的任务耗时长造成的阻塞等待。
CountingExecutorCompletionService<Boolean> completionService= new CountingExecutorCompletionService(ExecutorCollector.threadPoolExecutorForIO);
//任务提交
completionService.submit(callableA);
//结果归集
boolean result=true;
for(int i = 0; i<completionService.getSubmittedTaskCount(); i++)
{
result&=completionService.take().get();
}
3.6 效果验证
1.压测
采用jmeter对两台相同配置的服务器(分别部署优化版本和原始版本)加压,观察服务负载情况
2.性能指标验证
异步版本耗时在80-100ms,同步版本耗时在120-160ms
异步版本吞吐量在17000/5分钟,同步版本吞吐量在15000/5分钟
线程数高的原因:用到了线程池,预置的核心线程数为逻辑核数64,因为涉及到io操作较多,最大线程数配成了128。
3.业务结果验证
4 总结
程序性能优化方法关系到方方面面,而多线程异步优化无疑是其中很重要的一种途径。它不光关系到并发框架的选择、多种线程工具类的使用,还关系到对整个处理链路的业务理解和编排分析。希望通过这一课可以帮大家理清相关的思路,作为日常优化工作的一个参考。
内容来源:京东云开发者社区