当前位置: 首页 > 网络应用技术

经过2周的优化,QP终于翻了一番!

时间:2023-03-08 16:31:16 网络应用技术

  不久前,我们的服务遇到了性能瓶颈。因为早期需求太焦虑了,所以我没有注意这一方面的优化。当我们不得不偿还技术债务时,这是非常痛苦的。

  在非常低的QPS压力下,服务器负载可以达到10-20,CPU的使用率超过60%,并且在每个流量峰值时将大量报告接口。它更加大胆,担心它将成为粉碎骆驼的最后一根稻草,从而导致雪崩。

  需求最终放慢速度后,领导者设定了我们的目标,限制了我们在两个星期内完全解决服务性能问题。在过去两周的调查和梳理中,它被发现并解决了多个性能瓶颈,修改了系统融合计划,并最终意识到可以通过服务来处理的QP。正常的熔化,并且在压力降低后可以迅速恢复正常。以下是调查和解决某些问题的过程。

  要解决的第一个问题是该服务的总体负载较高和CPU高。

  我们的服务可以总体上总结以从某个存储或远程调用中获取一批数据,然后是这些数据的各种幻想转换,最后返回。由于数据转换过程很长,而且许多操作,因此系统CPU更高,但是在正常情况下,当CPU US超过50%时,它会更加夸张。

  我们都知道,您可以使用顶部命令来查询系统中系统中各个过程的CPU和内存职业。但是JVM是Java应用程序的领域。我应该使用什么工具来使用JVM中各种线程的资源占用?

  JMC还可以,但是使用它更麻烦,并且需要一系列设置。我们有另一种选择,即使用。JTOP只是一个罐子包。它的项目地址是Yujikiriki/JTOP。我们可以轻松地将其复制到server.jvm内部统计信息。

  JTOP使用默认参数打印5个消耗CPU的线程堆栈。

  形状像:

  通过观察线程堆栈,我们可以找到要优化的代码。

  在我们的代码中,发现了许多JSON序列化和依赖性以及Bean复制的CPU。之后,通过代码优化,通过提高BEAN的重用速率,PB取代了JSON和其他方法,从而大大降低了CPU压力。

  在服务熔融框架上,我们选择了Hystrix。尽管它不再宣布维护,但也建议使用Ali开源的前哨,但是由于该部门的技术堆栈是Hystrix,并且没有明显的缺点,因此可以使用它。

  让我们首先介绍基本情况。我们在控制器接口的外层和内部RPC调用处添加Hystrix注释。隔离方法是线程池模式。超时时间设置为200ms,最大线程数为500。

  在响应时间中要解决的第一个问题是界面的响应时间异常。当观察接口中的访问日志时,您可以发现接口需要1200ms,有些甚至超过2000ms。由于线程池模式,HyStrix将使用异步线程执行真实的业务逻辑,并且主线程一直在等待。一旦等待超时,可以立即返回主线程。因此,接口会消耗超过超时,并且问题可能会在Hystrix Framework层,弹簧框架层或系统层中发生。

  推荐春季靴基本教程和实际战斗示例:https://github.com/javastacks/spring-boot-practice

  目前,您可以分析运行时线程堆栈。我使用JSTACK打印电路堆栈,并将多个打印的结果纳入火焰图表(请参阅应用程序调试工具范围映射)以观察。

  如上图所示,许多线程被停止。这些线程已锁定。发现来源被发现,然后下降是我们的业务代码。

  在HyStrix注释中,HyStrixCommand使用这些时间列表来处理异步线程。他们将在超时时执行并返回超时结果。当呼叫数量很大时,设置这些timerListener将由于锁而阻止它,这将导致接口设置的超时未能生效。

  然后检查呼叫为什么有这么多的计时列表。

  由于该服务依赖于多个位置的相同RPC返回值,因此平均接口响应将获得相同的值3-5次,因此localcache在接口中的返回值中添加到该RPC的返回值。检查代码发现HyStrixCommand是添加到LocalCache Get方法中,因此,当独立的QPS 1000时,HyStrix调用方法3000-5000次会生成大量的Hystrix TimerListener。

  该代码类似于:

  修改代码并将HyStrixCommand修改为LocalCache的负载方法以解决此问题。此外,为了进一步减少Hystrix框架对性能的影响,HyStrix隔离策略将其更改为信号号码,并最大的时间模式接口的稳定性将是稳定的,并且由于这些方法是在主线程上执行的,因此HyStrix线程池维护的维护以及主线程和HyStrix线程之间的上下文开关,并且系统CPU利用率进一步下降。

  但是,使用信号量隔离模式还应注意一个问题:信号量只能限于该方法是否可以输入执行。方法返回之后,确定是否处理了接口超时和超时。结果,请求超时始终被信号量占据,但是无法处理该框架。

  服务隔离和降级的另一个问题是,无法按预期降低和融合服务。我们认为,当交通非常大时,交通应该继续融化,但是Hystrix偶尔会融合。

  当Hystrix保险丝参数最初调试时,我们采用了日志观察方法。由于日志被设置为异步,因此我们看不到真实的时间日志,并且有很多错误信息干扰,这是低效且不准确的。引入HyStrix的可视化接口后,提高了调试效率。

  Hystrix可视化模式分为服务器和客户端。服务器是我们要观察的服务。我们需要在服务中介绍一个软件包,并为输出指标信息添加接口。然后启动客户端并填写服务器地址。

  通过类似于上图的视觉界面,Hystrix的总体状态非常明显。

  由于上述优化,接口的最大响应时间已完全可控,并且可以通过严格限制接口方法的并发量来修改接口的熔融策略。修复我们可以公差50ms的平均响应时间,服务可以接受的最大QP是2000,然后您可以获得适当的信号量限制。如果被拒绝的数量太多,则可以添加一些冗余。

  这样,当流量突变突变时,可以通过拒绝部分请求来控制接口所接受的请求总数。在这些总请求中,最大时间严格受到限制。各种策略可以确保接口的平均响应时间。

  当高负载在保险丝期间无法恢复时,在接口破裂时,服务负载将继续上升,但是在QPS压力降低后服务延迟的问题。

  当服务器负载特别高时,请使用各种工具观察服务的内部状态。结果是不可靠的,因为观察值通常使用收集点的方法,该方法在观察服务时改变了服务。与Ti相关的堆栈。

  但是,可以发现此时将有大量的错误日志输出。目前,该服务已经稳定了很长时间。还有先前的打印登录,并且延迟单元甚至在几分钟内计数。大量错误日志不仅会导致I/O压力,而且还会导致线程堆栈的采集和日志存储器的分布将增加服务器压力并且由于较大的日志量,该服务已更改为异步日志,该日志量通过I/O阻塞线的屏障消失。

  然后修改服务中的日志记录点,在打印日志时不再打印异常堆栈,然后重写弹簧框架的异常手,以完全减少日志卷的输出。结果,也会控制日志输出当误差量极大时,因此在熔化后,它将不再增加由于日志中的对数而增加的压力。一旦QPS压力降低,保险丝开关就会关闭,并且该服务很快就可以快速服务。返回正常状态。

  此外,在查看JSTACK输出的线程堆栈时,偶然发现了一个奇怪的堆栈。

  在JSTACK的输出中,您可以看到多个线程的堆栈的顶部停留在春季的异常处理中,但是此时没有日志输出,业务也不异常。不要做任何事情。

  再次组合代码上下文,原始弹簧正在处理我们的控制器数据绑定。要处理的数据是参数apicontext。

  控制器代码类似于:

  根据普通例程,我们应该在此apicontext类中添加一个参数Parsolver,以便Spring会在解析此参数时称呼该参数解析器以生成相应的参数类型。但是Spring会处理这种参数解析器吗?

  答案是使用上面的“奇怪”代码,创建一个空的apicontext类,然后尝试将所有传递参数转入此类。在apicontext类中属性的参数绑定完成后。

  不幸的是,我们的界面的上层将通过三十或四十个参数传递给我们,因此每次都会执行大量的“尝试”。参数解析器解决此问题后,接口性能提高了近一个-第十。

  性能优化不是一夜之间的问题。将所有技术债务堆积到最后一个是不错的选择。向某些代码写作提供更多关注。使用黑色技术时,请注意是否有隐藏的坑是积极的解决方案。您还可以执行定期的绩效测试,以发现和解决该代码中最近引入的不安因素。

  资料来源:https://zhenbianshu.github.io/