原 荐 下单接口调优实战,性能提高10倍

栏目: Java · 发布时间: 6年前

内容简介:最近公司的下单接口有些慢,老板担心无法支撑双11,想让我优化一把,但是前提是不允许大改,因为下单接口太复杂了,如果改动太大,怕有风险。另外开发成本和测试成本也非常大。对于这种有挑战性的任务,我向来是非常喜欢的,因为在解决问题的过程中,可以学习到很多东西。当时我只是知道下单接口慢,但是没人告诉我慢在哪里,也即是说,哪些瓶颈导致下单接口慢了。其实没人知道也没关系的,因为我们可以通过压测来找到具体的瓶颈。下面会详细介绍一下,在本次压测中遇到的问题以及如何解决,期间用了什么工具。

概述

最近公司的下单接口有些慢,老板担心无法支撑双11,想让我优化一把,但是前提是不允许大改,因为下单接口太复杂了,如果改动太大,怕有风险。另外开发成本和测试成本也非常大。对于这种有挑战性的任务,我向来是非常喜欢的,因为在解决问题的过程中,可以学习到很多东西。

当时我只是知道下单接口慢,但是没人告诉我慢在哪里,也即是说,哪些瓶颈导致下单接口慢了。其实没人知道也没关系的,因为我们可以通过压测来找到具体的瓶颈。

下面会详细介绍一下,在本次压测中遇到的问题以及如何解决,期间用了什么工具。

用到的 工具 和环境

工具

  • Jmeter
  • JAVA自带的jvisualvm
  • JMX
  • nmon

环境

  • 腾讯云Mysql

  • 腾讯云2核4g的服务器1台

找瓶颈

下单属于写接口,大部分情况下,瓶颈都出在 DB 里,程序可能都在等待 DB 锁的释放。为了验证这个想法,我们可以使用 Jmeterjvisualvm 来验证一下。

为了监控服务器和服务器中 JAVA 进程,我们需要开启JMX,可以在JAVA进程启动的时候,添加如下几个参数:

JMX_OPTS="-Dcom.sun.management.jmxremote.port=7969 -Dcom.sun.management.jmxremote -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false -Djava.rmi.server.hostname=xx.xx.xx.xx"

nohup java ${JMX_OPTS} -jar xxxxx.jar

Djava.rmi.server.hostname 填写 JAVA 进程所在服务器的 IP 地址, -Dcom.sun.management.jmxremote.port=7969 是指定 JMX 监控端口的,这里是7969。

重新启动进程后,打开本地的( 我用的是Window10 ) jvisualvm ,添加 JMX 配置。配置成功后,可以点击线程那个 tab ,因为我们要做线程 dump ,观察线程的执行情况。

原 荐 下单接口调优实战,性能提高10倍

原 荐 下单接口调优实战,性能提高10倍

好了,现在我们可以使用 Jmeter 来对下单接口进行压测了。可以先用50线程并发压,执行时间是1分钟。 原 荐 下单接口调优实战,性能提高10倍

在压测的过程中,做一下线程 dump ,同时利用 nmon 观察应用服务器 CPU 的负载情况。

原 荐 下单接口调优实战,性能提高10倍

负载很低,将线程并发调整到100后,CPU还是上不去,这样的话,初步可以判断,代码里有锁。 通过观察dump文件,发现如下信息:

- locked <22f6e7f3> (a com.mysql.cj.core.io.ReadAheadInputStream)
- at com.sun.proxy.$Proxy231.reduceSkuStock(Unknown Source)

触发这个lock的业务代码是 reduceSkuStock 方法。通过阅读代码,发现 reduceSkuStock 被包在一个大事务里面。

@Transactional(rollbackFor = {Exception.class})
 createOrder() {
 //1、扣减库存
 reduceSkuStock();
 //2、创建订单
 insertOrder();
 //3、其他写操作
  。。。。
}

库存记录通常存在一张独立的库存表,由于创建订单的方法,是一个大事务,这样就会导致某条库存记录只有当整个 createorder() 方法执行完后,数据库行锁才会被释放,在这个期间,其他线程是无法对这条库存记录进行写操作的。因此我们可以在 reduceSkuStock() 中,再开一个事务,操作完这条库存记录后,里面释放锁,这样应该可以提高一些性能。为了验证是否是因为事务的原因导致下单接口慢,我们可以直接将 createOrder() 方法的事务去掉,再压测一下。

压测结果发现,下单接口的 TPS 提高了一倍, CPU 也上去了不少,但是仍然不够理想,代码里,应该还有其他的锁。再次做线程 dump ,又发现了一个锁。

- locked <438be230> (a org.apache.http.pool.AbstractConnPool$2)
- at org.apache.http.impl.client.CloseableHttpClient.execute(CloseableHttpClient.java:108)

导致锁的代码是 HttpClientexecute 方法,该方法在执行的时候,一直在等待获取 HTTP 连接,通过查看源代码,发现居然没有使用连接池,醉了。赶紧加上如下代码:

PoolingHttpClientConnectionManager pool = new PoolingHttpClientConnectionManager();
 pool.setDefaultMaxPerRoute(400);
 httpClient = HttpClients.custom().setConnectionManager(pool).build();

再次压测后,发现代码里已经没有锁了。 TPS 提升了5倍。但是接下来还得做几件事情:

1、打印下单接口的所有 SQL ,然后逐一进行 explain 操作,看看有没有全表扫描的语句或者没用到索引的 SQL 语句; 2、观察下单接口执行的过程中, FULL GC 发生的次数; 3、增加应用的 MYSQL 连接数;

好了,到了这地方,我们可以回到前面,来解决库存问题了。由于老板说,不能大改,因此我就在 reduceSkuStock 方法上,再开一个事务。

@Transactional(propagation = Propagation.REQUIRES_NEW)
reduceSkuStock(){}

让执行库存操作的线程执行完后,赶紧释放行锁。这样做也有个风险,就是库存扣减成功后,下单失败了。不过这种情况比较少,因为当时的下单接口中,大部分业务逻辑都在前面做好判断了,到达插入订单的代码时,就只是单独的insert了,除非数据库挂了,不然不会出现下单失败的情况。

在开发环境下,经过调优后,下单接口的TPS提升了3倍左右,当然由于开发环境的数据库和应用服务器都比较差,也会对TPS有影响的。当时优化完后,在生产上进行了压测,发现TPS提升了10倍。

总结

这个是下单接口的逻辑不能大改的情况下的优化方案,一般来说,库存操作应该是单独的服务,可以单独优化的。而单纯的下单逻辑也是可以优化的。

原文链接

下单接口调优实战,性能提高10倍


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

交互设计之路

交互设计之路

库帕 / Chris Ding / 电子工业出版社 / 2006-3 / 38.00元

本书是基于众多商务案例,讲述如何创建更好的、高客户忠诚度的软件产品和基于软件的高科技产品的书。本书列举了很多真实可信的实际例子,说明目前在软件产品和基于软件的高科技产品中,普遍存在着“难用”的问题。作者认为,“难用”问题是由这些产品中存在着的高度“认知摩擦”引起的,而产生这个问题的根源在于现今软件开发过程中欠缺了一个为用户利益着想的前期“交互设计”阶段。“难用”的产品不仅损害了用户的利益,最终也将......一起来看看 《交互设计之路》 这本书的介绍吧!

HTML 压缩/解压工具
HTML 压缩/解压工具

在线压缩/解压 HTML 代码

html转js在线工具
html转js在线工具

html转js在线工具

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试