干货 | 携程10个有效降低客户端超时的方法

news2024/11/23 13:33:16

作者简介

Wen,携程资深后端开发工程师,专注系统性能、稳定性、交易系统等领域。

一、背景

在现今的信息时代,微服务技术已成为一种重要的解决方案,微服务技术可以使系统的规模和功能变的更加灵活,从而获得更高的可扩展性和可用性。然而,微服务调用中出现的超时问题,却也成为系统可用性的一大隐患。超时会导致客户端的性能下降,甚至可能无法正常工作。本文针对超时问题,提出相关的优化手段,降低微服务调用超时的风险。

1.1 误区

当我们遇到超时或执行慢的问题时,我们往往会认为是依赖方出现了问题。

例如:访问 Redis、DB、 RPC 接口变慢、超时,第一时间找依赖方排查问题,对方反馈的结论是,我这边(服务端)没有问题,请检查一下你那边(客户端)是否有问题。

实际上,性能下降是一个非常复杂的问题,它可能涉及多个方面,包括服务端和客户端。例如:代码质量、硬件资源、网络状况等问题都会导致性能下降,从而引发响应慢、超时等问题。因此,我们需要全面地分析问题,找出影响性能的各种因素。

1.2 分享的目的

本文将详细介绍我们在生产环境中遇到的慢执行和超时等问题,并提出相关的优化手段,通过优化长尾性能,降低变慢或超时的风险,提升系统的稳定性。

二、超时的分类

常见的超时一般有两类:

a. 连接超时(ConnectTimeout):指建立网络连接所需要的时间超出了设定的等待时间。

b. Socket 超时(SocketTimeout):指在数据传输过程中,客户端等待服务端响应的时间超出了设定的等待时间。

如下图,①就是连接超时关注的时间,②就是 Socket 超时关注的时间,本文讲解的超时为 Socket 超时。

42618c68d1eca20fa73d52e52c88114d.png

图1 客户端请求过程

三、超时问题分析与优化


3.1 设置合理的超时时间

根据实际情况设置合理的超时时间,避免因为超时时间设置不合理导致的接口超时。

1)分析

看下客户端设置的超时时间是否合理。比如调用服务端 P99.9 是100ms ,客户端设置的超时时间是 100ms ,就会有 0.1% 的请求会超时。

2)优化方案

我们在设置超时时间需要综合考虑网络延迟、服务响应时间、GC 等情况。

以门票活动查询引擎为例:

  • 核心接口:最小值( P99.9*3 ,用户可接受的等待时间),核心会影响到订单,在用户可接受范围内尽可能出结果。

  • 非核心接口:最小值( P99.9*1.5,用户可接受的等待时间),非核心不影响订单,不展示也没关系。

3.2 限流

当系统遇到突发流量时,通过限流的方式,控制流量的访问速度,避免系统崩溃或超时。

1)分析

看下超时时间点的请求量是否有突增,比如有某些突然的活动,这个时候应用没有提前扩容,面对突增流量会导致应用负载比较高,从而导致超时问题。

2)优化方案

评估当前应用最大可承载的流量,配置限流,维度可以是单机+集群。

单机限流:在面对突增流量时避免单机崩溃。

集群限流:在有限的资源下提供最大化的服务能力,保证系统稳定性,不会出现崩溃或故障。

3.3 提升缓存命中率

提升缓存命中率,可以提高接口的响应速度,降低接口的响应时间,从而减少超时的发生。

1)分析

分析调用链路,找到慢的地方对其进行优化,提升服务端的响应速度。

如下图所示,很明显可以看到服务端执行时间超过了客户端配置的超时时间 200ms 导致超时。

d801015ecd274b5d9cdbdf77545e92e1.png

图2 客户端调用服务端超时链路

继续分析服务端执行链路,发现是因为缓存没有命中导致的。

e5cf48c8686d91c99a978bd2b3216874.png

图3 缓存未命中链路

2)优化方案

对于高并发系统来说,常见的是使用缓存来提升性能。

如下图是之前的缓存架构,这种缓存架构有两个风险。

a. 缓存是固定过期的,会导致某个时间大量 key 失效直接击穿到数据库。

b. 主动刷新机制是删除缓存,监听数据库 binlog 消息删除缓存,如果大批量刷数据会导致大量 key 失效。

d39619b028cc72e48aab90659dca208b.png

图4 固定过期+懒加载模式

针对上面的风险我们优化了缓存架构,固定过期改为主动续期缓存,主动监听消息刷新缓存的方案,如下图所示。

91ea74de060104b521bdc5fc1a33b33a.png

图5 缓存前后架构对比

3)效果

缓存命中率提升到 98% 以上,接口性能(RT)提升 50% 以上。

3efcdb516eff4d914f0f4553559db996.png

图6 处理性能提升50%

这个缓存优化方案在我们团队之前写的一篇文章《1分钟售票8万张!门票抢票背后的技术思考》中有详细的介绍,具体细节这个地方不再展开,有兴趣的同学可以自行阅读。

3.4 优化线程池

减少不合理的线程,降低线程切换带来的超时。

1)分析

a. HTTP 线程数

先看下服务端 HTTP 线程数是否有明显增加,且流量没有增长,要确认 HTTP 线程数增加不是因为流量增长导致的。如下面两张图,流量正常的情况下 HTTP 线程数增加,说明是服务端响应变慢导致,可以确认超时是服务端原因。

c2a3a823bcb11dc9f33a8a54ebedfe48.png

图7 服务流量平稳

626cb971ce33bf2b5a6690c320ffc4a2.png

图8 HTTP线程数突增

b. 总线程数

再看下总线程数是否有增加(排除 HTTP 线程数),如果有,说明有使用多线程导致线程数量增加。这个时候需要 Dump 下线程,看下哪些线程使用的比较多。

2)解决方案

a. 统一管理线程池:动态配置参数+监控能力

通过工具类封装统一的线程池,提供动态配置参数和线程池监控能力。

  • 效果

线程池具备监控能力,如下图是最小值(核心线程数)、最大值(最大线程数)和当前线程池中线程数量的监控,可以参考这个来调整线程池参数。

b1d5d7cc9a150727eaaaa6db600b307b.png

图9 线程池水位线监控

b. 异步改同步:小于10ms 的不使用多线程

高并发的场景下线程太多,线程调度时间得不到保障,一次任务需要多个 CPU 时间片,下一次调度的时间无法得到保障。

如下图是一个线程池执行耗时埋点,通过埋点发现 A 在线程池中执行的比较快,平均线和 P95 都在 10ms 以下,没有必要使用线程池,改成同步执行。

3b7949b4d074e3f7afa484c940bf742a.png

图10 优化前执行耗时

  • 效果

接口性能提升明显,平均线从 2.7ms 降低到 1.6ms,P99.9 从 23.7ms 降低到 1.7ms。

之前使用多线程,请求量有波动的时候线程增加比较多,导致线程调度时间得不到保障,体现到 P99.9 就很高。

6dccaaef6fbef6f4acb3e1be58d3fb82.png

图11 优化前后耗时对比

另外可以明显看到总线程数也相应减少了很多。

4a8718c2876f4cf149442a01aa82efc5.png

图12 异步改同步后总线程数减少

3.5 优化 GC

优化 GC,减少 GC 的停顿时间,提高接口的性能。

1 )分析

首先看超时时间点是否有 Full GC,没有再看下 Yong GC 是否有明显的毛刺,如下图可以看到 3 个明显的毛刺。

d4816811fb735c7c6c56d014ad58d8d2.png

图13 Yong GC时间

如果超时的时间点(如下图)可以对应上 GC 毛刺时间点,那可以确认问题是由于Yong GC 导致。

403c4f438d0a8b2af67744be418d0b74.png

图14 客户端调用服务端超时次数

2)解决方案

a. 通用性 JVM 参数调优

检查 -Xmx -Xms 这两个值设置的是否一样,如果不一样 JVM 在运行时会根据实际情况来动态调整堆大小,这个调整频繁会有性能开销,并且初始化堆较小的话,GC 次数会比较频繁。

  • 效果

-Xmx3296m -Xms1977m 改成 -Xmx3296m -Xms3296m 后效果如下图所示,频率和时间都有明显的下降。

241643555c1b3cfbd020b38a6ee43908.png

图15 通用性JVM参数调优后效果

b. G1 垃圾回收器参数调优

背景:如果没有设置新生代最大值和最小值或者只设置了最大值和最小值中的一个,那么 G1 将根据参数 G1MaxNewSizePercent(默认值为60)和 G1NewSizePercent(默认值为5)占整个堆空间的比例来计算最大值和最小值,会动态平衡来分配新生代空间。

JVM刚启动默认分配新生代空间是总堆的 5%,随着流量的增加,新生代很容易就满了,从而发生 Yong GC,下一次重新分配更多新生代空间,直到从默认的 5% 动态扩容和合适的初始值。这种配置在发布接入流量或者大流量涌入时容易发生频繁的 Yong GC。

针对这类问题,优化方案是调大 G1NewSizePercent,调大初始值,让 GC 更加平稳。这个值需要根据业务场景参考GC日志中Eden初始大小分布来设置,太大可能会导致 Full GC 问题。

以查询引擎为例,根据 GC 日志分析,新生代大小占堆比例在 35% 后相对平稳,设置的参数为:


-XX:+UnlockExperimentalVMOptions -XX:G1NewSizePercent=35

  • 效果

优化后效果如下图,可以看到优化之后 GC 次数从 27次/min 降低到 11次/min,GC 时间从 560ms 降低到 250ms。

1024973309c23493b2dd570f62432c28.png

图16 G1参数调优后效果

3.6 线程异步改成 NIO 异步编程

NIO(非阻塞 IO)可以减少线程数量,提高线程的利用率,从而降低线程切换带来的超时。

1)分析:CPU 指标

分析 CPU 相关指标,如果出现 CPU 利用率正常,CPU Load 高需要重点关注(如果是CPU 利用率高的情况,说明 CPU 本身就很繁忙,那 CPU Load 高也比较正常)。

在分析之前,先介绍几个概念:

a. CPU时间片

CPU 将时间分成若干个时间片,每个时间片分配给一个线程使用。当一个时间片用完后,CPU 会停止当前线程的执行,进行上下文切换到下一个任务,以此类推。

这样可以让多个任务在同一时间内并发执行,提高系统的效率和响应速度。

下图模拟了单核 CPU 执行的过程,需要注意的进行上下文切换是需要开销的,但实际一次上下文切换需要的时间很短(一般是微秒级别)。

c35199699b6ed51298a2bfa490f92455.png

图17 CPU执行线程流程

b. CPU利用率

按时间片维度来理解,假设每次时间片都正好被使用完。

247706866ff56f2eda550fed0effc623.png

c. CPU Load

从上面概念分析,如果出现 CPU 利用率正常,但是 CPU Load 高,那说明 CPU 空闲时间片、等待线程数很多,正在使用的时间片很少,这种情况要减少 CPU Load 需要减少等待线程数。

5bf183581266a31348e5ecda8aaf8f51.png

2分析:实际案例

我们之前生产遇到过多次 CPU Load 高 CPU 利用率正常的情况。问题出现前后代码没有变动,比较明显的变化是流量有上涨。排查代码发现有使用线程池并发调用接口的地方,调用方式如下图。

89cd9b04c6e331192d6f38cc5d6fef97.png

图18 线程池执行模型

这种方式在流量较低的情况下看不出什么问题,流量变高会导致需要的线程数量成倍增加。

例如:一次请求 A 需要调用 BCD 3个接口,那100个并发需要的线程数就是100 + 3*100=400(第一个100是A对应的主线程,后面的3*100是 BCD 需要的100个线程)。

3)解决方案

线程池并发调用改成 NIO 异步调用,如下图所示。

和之前对比,100 个并发,需要的线程数也是 100(这个地方不考虑 NIO 本身的线程,这个是全局的,并且是相对固定很少的线程数)。

17bf1a40bd3557f75de42fd9e51a72c9.png

图19 NIO异步调用执行模型

4)效果

超时问题没再出现,CPU Load 平均下降50%,之前 Load 经常超过2(CPU 核数为2),改造之后 Load 降到 0.5 左右。

916fec5b0bfb469b7b4bbfece223823c.png

图20 CPU Load优化后效果

3.7 启动阶段预热

启动阶段预热可以提前建立链接,减少流量接入时的链接建立,从而降低超时的发生。

1)分析

应用拉入后出现大量超时,并且 CPU Load 高 CPU 利用率正常,说明有很多等待线程,这种是拉入后有大量请求在等待被处理。

之前我们生产遇到过是在等待 Redis 建立链接,建链的过程是同步的,应用刚拉入请求量瞬间涌入就会导致大量请求在等待 Redis 建链完成。

2)解决方案

启动阶段预热提前建立链接,或者是配置 Redis 的最小空闲连接数。其他资源准备也可以通过启动阶段预热完成,比如 DB 链接、本地缓存加载等。

3.8 优化 JIT

JIT(Just-In-Time)编译可以提高程序的运行效率,灰度接入流量将字节码编译成本地机器码,避免对接口性能的影响。

1)JIT 介绍

JIT 是 Just-In-Time 的缩写,意为即时编译。JIT 是一种在程序运行时将字节码编译为本地机器码的技术,可以提高程序的执行效率。

在 Java 中,程序首先被编译为字节码,然后由 JVM 解释执行。但是,解释执行的效率较低,因为每次执行都需要解释一遍字节码。为了提高程序的执行效率,JIT 技术被引入到 Java 中。JIT 会在程序运行时,将频繁执行的代码块编译为本地机器码,然后再执行机器码,这样可以大大提高程序的执行效率。

2)分析

JIT 技术可以根据程序的实际运行情况,动态地优化代码,使得程序的性能更好。但是JIT 编译过程需要一定的时间,因此在程序刚开始运行时,可能会出现一些性能瓶颈。

如下图应用拉入后 JIT 时间很久,那可以确认是 JIT 导致超时。

591b2cbfb8535f6c67b7cdaa8024a9ea.png

图21 JIT执行时间

3)解决方案

优化 JIT 一个比较好的方案是开启服务预热(预热功能携程 RPC 框架是支持的)。

原理是让应用拉入后不是立马接入100% 流量,而是随着时间移动来逐渐增加流量,最终接入100% 流量,这种会让小部分流量将热点代码提前编译好。

4)效果

开启服务预热后,如下图所示,应用流量是逐渐增加的,可以看到响应时间随着时间越来越低,这就达到了预热的效果。

363af38a9be71575468ba15975150c56.png

图22 服务拉入后请求量和响应时间

3.9 换宿主机

当宿主机负载过高时,可以考虑更换宿主机,避免宿主机负载过高影响容器负载。

1)分析

a. CPU Throttled 指标

看下应用 CPU 节流指标,CPU 节流会导致 CPU 休眠引起服务停顿。如果 CPU 利用率正常还是出现了 CPU 节流,这种大多数都是宿主机问题导致。

923f32a05c2e16e11be7db8309762598.png

图23 CPU节流情况

b. 宿主机指标

排查宿主机 CPU 利用率、CPU Load、磁盘、IO 等指标是否正常,如下图 CPU Load在某个时刻后大于1说明宿主机负载较大。

5d6019b0ae636fac50ad63e154618562.png

图24 宿主机CPU Load监控

2)解决方案

重启机器换宿主机。

3.10 优化网络

排查不稳定的网络线路,保证网络的稳定性。

1)分析

网络的重点看下 TCPLostRetransmit(丢失的重传包)指标。比如下图,某个点指标异常,而这个点其他指标都正常,那可以初步怀疑是网络问题导致,最终确认需要找网络相关团队确认。

d21e8b72e132f6e10a5e56b97313a052.png

图25 TCPLostRetransmit指标

2)解决方案

找网络相关团队排查优化。

四、总结

回顾全文,主要讲解遇到超时问题怎么分析、怎么定位、怎么优化,从简单到复杂总结了 10 种常见的优化方法。这些方法不一定能解决其他不同业务场景的超时问题,具体需要结合自己的实际业务场景来验证。

本文总结的方法都是我们在生产中遇到的真实情况,通过不断实践总结出来的,希望这些内容能够给阅读本文的同学带来一定的收获。

4.1 优化注意事项

  • 超时时间设置和 GC 调优需要结合自己业务场景来优化。

  • NIO 异步编程改造成本、复杂度较高,我们也在探索更简单的方式,例如 JDK19 引入的虚拟线程(类似 Go 协程),可以用同步编程方式来实现异步的效果。

【推荐阅读】

  • 携程中转交通方案拼接性能优化

  • 携程 SOA 的 Service Mesh 架构落地

  • 日均流量200亿,携程高性能全异步网关实践

  • 掌控 Android 编译利器,携程火车票AAR 编译速度优化实践

9067a93688af998133c049db5e3ce9ec.jpeg

 “携程技术”公众号

  分享,交流,成长

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/600255.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

docker基本命令学习 | Docker网络、Docker镜像发布

💗wei_shuo的个人主页 💫wei_shuo的学习社区 🌐Hello World ! docker安装、卸载 docker安装使用 卸载旧版本docker或者环境 [rootiZf8zdcobr3fw7vn0p3538Z /]# yum remove docker \ > docker-client \ >…

打破逢节降价桎梏!海尔智家:满足用户,全网第一

又是一年618,每到这个上半年最重要的消费节点,许多品牌卖家纷纷掀起价格战。 他们使出满减、满赠、满返等五花八门的策略,为了压制对手进行冲量,这也一度让“逢节降价”成为主流。 在市场天平偏向卖家的时代,这些策略…

SVN服务端visualsvn5.1.4下载安装(windows环境)(实操)

Apache Subversion 通常被缩写成 SVN,是一个开放源代码的版本控制系统,Subversion 在 2000 年由 CollabNet Inc 开发,现在发展成为 Apache 软件基金会的一个项目,同样是一个丰富的开发者和用户社区的一部分。 SVN相对于的RCS、CVS…

做自己喜欢的事

这两天沸沸扬扬的消息说稚辉君公司拿到了百度投资 稚晖君刚拿了百度投资,估值被曝已超独角兽 然后昨晚上小孩发烧,我陪床不敢死睡,跟大佬聊了下拿到投资的感受。 然后说到搞技术好玩这个事情,我就跟他分享了我前天到经历 我前天到…

Jenkins概念及安装配置教程(二)

如何安装Jenkins? Jenkins 安装程序也可以作为通用 Java 包 (.war) 使用。如果您将 Jenkins 与 Selenium 一起用于执行跨浏览器测试,我们建议使用 .war 文件,因为您可以通过在非无头模式下在浏览器上执行的自动化测试来见证测试场景的执行。…

【C# 10 和 .NET 6】使用MVC模式构建网站(笔记1)

Building Websites Using the Model-View-Controller Pattern 使用模型-视图-控制器模式构建网站 本章介绍使用 Microsoft ASP.NET Core MVC 在服务器端构建具有现代 HTTP 架构的网站,包括构成 ASP.NET Core MVC 项目的启动配置、身份验证、授权、路由、请求和响应管…

如何设计和使用文档模板 | 技术写作什么鬼

今天看到叶伟民老师的一篇文章,瞬间泪目:叶老师,您是懂人性的啊。在我整天鞭策自己“不能再拖了”的关键时刻,及时分享经验: 是的,这篇文章实在是拖了太久,了太久,太久,久…

web前端 --- javascript(03) -- 函数、内置对象

函数(function) 具有名称的,为了实现特定功能的代码集合体 (1)javascript如何定义函数:function关键字定义 function 函数名称 ([ 参数列表 ]){ // 函数体 // [return 返回值]…

【Springboot】发送QQ邮件

系列文章目录 文章目录 系列文章目录前言添加Maven依赖QQ邮箱开启POP服务配置application.properties文件Controller层编写 vue前端(也可以直接省略) 前言 这篇博客用于简单实现SpringBoot中使用Controller发送邮件请求,用户可以收到邮件。 …

Python读写access数据库的实战代码

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

chatgpt赋能python:使用Python关闭端口的方法

使用Python关闭端口的方法 在网络安全中,关闭端口是非常重要的一项任务。一旦一个端口被打开并暴露给互联网,恶意用户就可能通过它们的攻击进入您的服务器或计算机系统。Python是一种流行的编程语言,也可以用来关闭端口。下面介绍一些常用的…

JSONSQL:使用SQL过滤JSON类型数据(支持多种数据库常用查询、统计、平均值、最大值、最小值、求和语法)...

1. 简介 在开发中,经常需要根据条件过滤大批量的JSON类型数据。如果仅需要过滤这一种类型,将JSON转为List后过滤即可;如果相同的条件既想过滤数据库表中的数据、也想过滤内存中JSON数据,甚至想过滤Elasticsearch中的数据&#xff…

chatgpt赋能python:Python内置变量:掌握这些变量,让你的编程更高效

Python内置变量:掌握这些变量,让你的编程更高效 Python作为一门优秀的编程语言,自然不会缺少重要的内置变量。这些内置变量可以帮助程序员轻松地实现各种编程功能,提高编程效率。在本文中,我们将介绍Python内置变量的…

chatgpt赋能python:Python内置函数使用指南

Python内置函数使用指南 Python是一种高级编程语言,得益于其简单易学的语法、强大的标准库和丰富的第三方模块,现已成为全球最受欢迎的编程语言之一。其中,Python内置函数是Python编程的重要组成部分,本文将为您介绍这些内置函数…

四种主要的IO模型

基本概念 基本概念阻塞IO指的是需要内核IO操作彻底完成后,才返回到用户空间执行用户的操作。阻塞指的是用户空间程序的执行状态。传统的IO模型都是同步阻塞IO。在Java中,默认创建的socket都是阻塞的。简单来说:阻塞是指用户空间&#xff08…

尚硅谷-云尚办公-项目复盘

尚硅谷-云尚办公-项目复盘 资料地址本文介绍问题汇总问题1.knife4j无法下载 视频4问题2.dev等含义 视频5问题3.wrapper继承/实现图 视频8问题4.修改统一返回结果 视频11问题5.修改后新增也变修改 视频29问题6.redis中key值乱码 视频55-60问题7.RangeError: Maximum call stack …

高完整性系统工程(六):INTRODUCING ADA

目录 1. ADA的历史 2. ADA的特点 2.1 Strong, Static Typing 强语言、强静态类型语言 2.1.1 ADA is Strong, Static Typing 2.1.2 C is Weak, Static Typing 2.2 Module System 2.3 Portable 2.3.1 ADA 2.3.2 C 2.3.3 Cost of Runtime Checking 2.4 Readability …

IPython使用学习笔记

学习《利用python进行数据分析》第三章 IPython:一种交互式计算和开发环境的笔记,共享给大家,同时为自己作为备忘用。 安装ipython用pip即可。ps.博主用的是win7系统,所以接下来的都是在windows系统下操作的。 一.Ipython基础 启动&#xff…

chatgpt赋能python:Python关闭程序代码的实现方法

Python 关闭程序代码的实现方法 如果你是一个经验丰富的 Python 工程师,你应该知道如何在程序中实现正常关闭。不过,如果你是一个新手,这可能会变得有点棘手,特别是当你需要在程序中添加一些特定的关闭功能的时候。本文将为您介绍…

【javaEE】计算机网络原理初始

目录 1、网络发展史 1.1、独立模式 1.2、网络互连 1.2.1、局域网(LAN) 1.2.2、广域网(WAN) 1.2.3、广域网和局域网的区别 1.2.4、局域网组建网络的方式 (了解) 2、网络通信基础 2.1、I…