您好!
欢迎来到京东云开发者社区
登录
首页
博文
课程
大赛
工具
用户中心
开源
首页
博文
课程
大赛
工具
开源
更多
用户中心
开发者社区
>
博文
>
rt下降40%?程序并行优化六步法
分享
打开微信扫码分享
点击前往QQ分享
点击前往微博分享
点击复制链接
rt下降40%?程序并行优化六步法
自猿其说Tech
2022-04-28
IP归属:未知
478440浏览
计算机编程
### 1 背景 性能优化是我们日常工作中很重要的一部分,主要有以下原因: - 降低服务器和带宽等硬件成本:用更少的资源处理更多的请求 - 提高现实世界的运行效率:人机处理效率存在数量级的偏差,同样机器世界的效率提升能带来现实世界效率提升的方法效果 - 提高用户的体验:解决响应缓慢、宕机等问题 而并行优化在改善程序接口响应时间和吞吐量指标方面是个利器,所以本次结合前段时间做的一段长链路执行逻辑代码的优化,给大家讲讲程序并行优化的步骤及方法论。 ### 2 多线程优化六步法 #### 2.1 定位优化点 一般是通过全链路监控、火焰图、自定义打点、生产报警等先找到耗时长的性能问题点,之后通过多线程并行化的方式达到优化程序响应时长和吞吐量的目的。 #### 2.2 执行链路分析 对问题点的执行链路进行分析,主要分几方面: - 链路里涉及的操作节点; - 节点自身的耗时;是io密集型还是cpu密集型;是否依赖和修改外部变量;此节点是否是核心路径; - 节点间彼此依赖关系; #### 2.3 异步链路设计 - 将链路根据依赖关系进行重排,把被依赖的放在前面; - 彼此不依赖有相同起点的节点并行化;设计并行任务结果获取及后续依赖节点的通知机制 - 如果有指定响应时间目标的链路,为核心路径节点设计降级方案;根据响应时间要求及已耗时数据对非核心路径节点调用进行舍弃; - 将对变量修改的逻辑收拢,且尽量在主线程中处理,避免需要做的多线程变量可见性和时序性同步 #### 2.4 并发框架选择 ##### 1.线程池 ![](//img1.jcloudcs.com/developer.jdcloud.com/e1b2323a-33fa-43b8-b0be-e00f6fe8496b20220428141623.png) 描述:具体业务任务继承接口 Runnable、Callable ,在调用 ExecutorService.submit 接口时,会提交任务到 ExecutorService 内部的一个任务队列中。同时,在 ExecutorService 内部还存在一个预先申请的线程池(Thread Pool),线程池中的线程会从任务队列中领取一个任务来执行。 优点:复用线程,减少线程创建销毁成本及减少请求时延 注意点:cpu密集型和io密集型任务应进行不同的线程池配置;为避免不同任务相互干扰重要业务最好独立使用线程池;不同线程之间要注意操作的有序和数据的可见性 ##### 2.AKKA ![](//img1.jcloudcs.com/developer.jdcloud.com/68aab15f-cbf6-4c1a-ae0d-38b148be12e920220428141720.png) 描述:每个 Actor 代表的是可以被调度执行的轻量单元。如图中所示,Actor A 和 Actor C 在向 Actor B 发送消息时,所有消息会被底层框架发送到 Actor B 的 Mailbox 中,然后底层的 Akka 框架调度代码会触发 Actor B,来接收并执行消息的后续处理。这样,基于 Actor 模型的这套并发框架,首先就保证了消息可以被安全地在各个 Actor 之间传递,同时也保证了每个 Actor 实例可以串行处理接收到的所有消息。 优点:不需要关注多线程之间并发同步和数据一致性;轻量级高并发 注意点:actor任务粒度要小,避免承接太多业务逻辑;计算密集型任务更能发挥出AKKA的优势 ##### 3.REACTOR ![](//img1.jcloudcs.com/developer.jdcloud.com/e44e4bcb-5813-4211-8e38-2b36e2331e2920220428141747.png) 描述:输入流 Flux 就是 Reactor 中典型的异步消息流,它代表了一个包含 0 个到 N 个的消息序列。另外,图中的 Rule 代表的是一个基于消息的处理逻辑或规则,输入流中的消息可以被中间多个处理逻辑组合连续加工之后,再生成一个包含 0 个到 N 个的输出消息流 Flux。 优点:rule采用pull处理消息,避免消息积压;异步非阻塞io,避免阻塞当前线程 注意点:函数式编程,会有一定的语法学习成本和理解成本;针对消息流处理的、基于 IO 密集型的异步交互场景比较有优势 #### 2.5 并发工具选择 多线程执行涉及到一系列细节问题,如共享变量可见性,执行顺序,结果的获取、后续操作的通知等,所以要结合需求使用一系列相关的并发工具类做多线程执行正确性的保障 #### 2.6 效果验证 ##### 1.压测 一般通过jmeter、loadrunner等后端性能测试软件,不断对系统施加压力,并验证系统化处于或长期处于临界饱和阶段的稳定性以及性能指标,并试图找到系统处于临界状态时的主要瓶颈点。 注意点: - 完全相同的环境以及测试负载 - 注意混部情况其他服务可能对验证服务造成的影响 - 通过加压减压调整请求量观察服务器处理能力的变化及稳定性 ##### 2.性能指标验证 - 验证并发用户数、响应时间及吞吐量这种调优目标量; - 观察服务器的负载指标,防止因优化带来服务器超出负载能力; - 观察上下游服务的业务指标和服务器负载,防止因优化带来上下游超出负载能力 ##### 3.业务结果验证 一般通过diff工具通过采集相同请求的响应对比判别是否影响业务;也可通过qa辅助构建针对改动的测试集去做验证 ### 3 举例 以我们前段时间进行的商品主数据下发消费能力调优进行举例说明整个优化过程: #### 3.1 优化点定位 主数据程序接收商品批量下发处理缓慢,触发下发积压报警 #### 3.2 执行链路分析 梳理各步骤对入参和保存时需要的变量的处理,分析各步骤相互依赖关系,是否可并行,进行执行过程优化调整。 商品主数据处理步骤分析: ![](//img1.jcloudcs.com/developer.jdcloud.com/13f484b6-1d44-4920-b5bf-006fb321c9bf20220428142211.png) #### 3.3 异步链路设计 1. 1、 3、4、5异步并行处理,且因对其他变量修改逻辑无依赖,放在最前面提交。 2. 2、7、8、9、10、11根据依赖关系,把相关性的逻辑收拢,把被依赖的逻辑提前。 3. 13也异步提交。最后通过completionService.take().get()遍历获取各任务执行结果进行合并返回最终结果 #### 3.4 并发框架选择 出于团队知识栈及框架应用场景综合考虑,这里选择了线程池作为并发框架,并结合多io场景做了线程池参数配置。 ```java /** * 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结果的方式导致先提交的任务耗时长造成的阻塞等待。 ```java 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对两台相同配置的服务器(分别部署优化版本和原始版本)加压,观察服务负载情况 ![](//img1.jcloudcs.com/developer.jdcloud.com/18acea7f-21df-4869-bdd8-6a6ca5017e5920220428143233.png) ###### 2.性能指标验证 ![](//img1.jcloudcs.com/developer.jdcloud.com/aa2ecb8f-cc12-4ad3-9576-e09bc103ad4820220428143258.png) 1)耗时和吞吐量异步版本要优于同步版本 异步版本耗时在80-100ms,同步版本耗时在120-160ms 异步版本吞吐量在17000/5分钟,同步版本吞吐量在15000/5分钟 ![](//img1.jcloudcs.com/developer.jdcloud.com/646c4a98-06b1-485a-b9b5-b201f63866a020220428143329.png) 2)cpu使用率异步版本略高一点,线程数异步版本比较高 线程数高的原因:用到了线程池,预置的核心线程数为逻辑核数64,因为涉及到io操作较多,最大线程数配成了128。 ##### 3.业务结果验证 因为公司框架不支持http的diff,此处采用了自己抽检请求结果及qa协助走查和code review的方式保证业务结果的准确性 ### 4 总结 程序性能优化方法关系到方方面面,而多线程异步优化无疑是其中很重要的一种途径。它不光关系到并发框架的选择、多种线程工具类的使用,还关系到对整个处理链路的业务理解和编排分析。希望通过这一课可以帮大家理清相关的思路,作为日常优化工作的一个参考。 ------------ ###### 自猿其说Tech-JDL京东物流技术与数据智能部 ###### 作者:冯鸿儒
原创文章,需联系作者,授权转载
上一篇:一条sql了解MYSQL的架构设计
下一篇:时序时空数据库调研
相关文章
Taro小程序跨端开发入门实战
Flutter For Web实践
配运基础数据缓存瘦身实践
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
本文将从Optional所解决的问题开始,逐层解剖,由浅入深,文中会出现Optioanl方法之间的对比,实践,误用情况分析,优缺点等。与大家一起,对这项Java8中的新特性,进行理解和深入。
01
Taro小程序跨端开发入门实战
为了让小程序开发更简单,更高效,我们采用 Taro 作为首选框架,我们将使用 Taro 的实践经验整理了出来,主要内容围绕着什么是 Taro,为什么用 Taro,以及 Taro 如何使用(正确使用的姿势),还有 Taro 背后的一些设计思想来进行展开,让大家能够对 Taro 有个完整的认识。
01
Flutter For Web实践
Flutter For Web 已经发布一年多时间,它的发布意味着我们可以真正地使用一套代码、一套资源部署整个大前端系统(包括:iOS、Android、Web)。渠道研发组经过一段时间的探索,使用Flutter For Web技术开发了移动端可视化编程平台—Flutter乐高,在这里希望和大家分享下使用Flutter For Web实践过程和踩坑实践
01
配运基础数据缓存瘦身实践
在基础数据的常规能力当中,数据的存取是最基础也是最重要的能力,为了整体提高数据的读取能力,缓存技术在基础数据的场景中得到了广泛的使用,下面会重点展示一下配运组近期针对数据缓存做的瘦身实践。
自猿其说Tech
文章数
426
阅读量
2149964
作者其他文章
01
深入JDK中的Optional
01
Taro小程序跨端开发入门实战
01
Flutter For Web实践
01
配运基础数据缓存瘦身实践
添加企业微信
获取1V1专业服务
扫码关注
京东云开发者公众号