8

极致性能优化指南(一)

 3 years ago
source link: http://sunliangliang.com/?p=56
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

最近对正在负责的一个系统做了一个压测,在此复盘了这次压测的整体流程,以及需要关注的指标,由于很多数据指标没有被保留,因此没有完全还原场景,后续再来完善吧,这篇就做入门指引吧。

  • 系统优化点
  • JVM模板
  • 优化前后指标对比

1.压测指标

压测的直播如下

指标 期望值 描述 cpu.idle(cpu剩余) min>=30% 即cpu的最高占用率不能高于70% lantency(接口耗时) avg <=50ms 对于高并发的系统,QPS要求比较高的,一般lantency要控制在10ms以内 mem.used(内存使用率) avg<=80% 内存使用率一般不要超过能机器80%

2.系统优化点

系统优化一般是从如下几个点去考虑,这里先简单介绍下对应的工具,后续会通过场景再现讲解。工具的地址会在文章末尾给出。

  • Arthas:阿里开源的一个性能分析工具,全方位的定位性能问题 Arthas:https://arthas.aliyun.com/doc/
  • useful-scripts:一些非常牛逼的脚本,可以帮助你解决很多线上问题
  • show-busy-java-threads:这里重点提一下这个脚本,可以协助我们解决线程的问题

2.1.lantency

接口响应耗时较长,这里首先需要定位到耗时的代码位置,使用Arthas

trace com.xiaoju.manhattan.messenger.controller.MsgPushController pushMsg 这里使用Arthas的trace命令定位到耗时较长的接口,然后可以逐步往下跟踪找到对应的方法,进行优化。
降低接口耗时,提高QPS的方案,一般来说就是采用多级缓存以及异步化的方式。

  • 常用的本地缓存框架:GuavaCache和caffeine
  • 分布式缓存一般使用Redis:记得设置超时时间,rediskey的值不宜过大

对于QPS要求比较高,以及lantency要求比较高的,日志一定要使用异步日志。

  • 引入包: "com.lmax:disruptor:3.3.6"
  • log4j2.xml配置

下面附上我这边的配置吧,includeLocation是用来打印行号的,这个对性能还是比较大的,会占用比较高的cpu资源,后续再来完善具体的原因

<?xml version="1.0" encoding="UTF-8"?>
<Configuration status="INFO">
 <Properties>
  <Property name="SERVICE_NAME">messenger-api</Property>
  <Property name="LOG_HOME">../logs/messenger/${SERVICE_NAME}</Property>
  <Property name="ENCODING">utf-8</Property>
 </Properties>
 <Appenders>
  <!-- INFO日志 -->
  <RollingRandomAccessFile name="INFO_FILE"
   fileName="${LOG_HOME}/business.log"
   filePattern="${LOG_HOME}/business.log.%d{yyyyMMddHH}"  immediateFlush="false" append="false">
   <FinBamaiLayout/>
   <Policies>
    <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
   </Policies>
   <DefaultRolloverStrategy>
    <Delete basePath="${LOG_HOME}">
     <IfFileName glob="business.log.*"/>
     <IfLastModified age="30d"/>
    </Delete>
   </DefaultRolloverStrategy>
  </RollingRandomAccessFile>

<!-- ERROR日志 -->
  <RollingRandomAccessFile name="ERROR_FILE"
   fileName="${LOG_HOME}/error.log"
   filePattern="${LOG_HOME}/error.log.%d{yyyyMMddHH}"  immediateFlush="false" append="false">
   <FinBamaiLayout/>
   <Policies>
    <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
   </Policies>
   <DefaultRolloverStrategy>
    <Delete basePath="${LOG_HOME}">
     <IfFileName glob="error.log.*"/>
     <IfLastModified age="60d"/>
    </Delete>
   </DefaultRolloverStrategy>
   <Filters>
    <ThresholdFilter level="error" onMatch="ACCEPT" onMismatch="DENY"/>
   </Filters>
  </RollingRandomAccessFile>


    <!-- public消息回执的日志 -->
    <RollingRandomAccessFile
      name="public_log" fileName="${LOG_HOME}/public.log"
      filePattern="${LOG_HOME}/public.log.%d{yyyyMMddHH}"  immediateFlush="false" append="false">
      <PatternLayout pattern="%msg%n"/>
      <Policies>
        <TimeBasedTriggeringPolicy interval="1" modulate="true"/>
      </Policies>
    </RollingRandomAccessFile>

</Appenders>

<Loggers>

<AsyncLogger name="PublicLog" level="info" additivity="false" includeLocation="false">
      <AppenderRef ref="public_log"/>
    </AsyncLogger>

<AsyncLogger name="org.springframework" level="INFO"/>
  <AsyncLogger name="org.apache.httpclient.wire" level="INFO"/>
  <AsyncLogger name="org.apache.http" level="INFO"/>
  <AsyncLogger name="org.apache.commons.httpclient" level="INFO"/>
  <AsyncLogger name="org.mybatis.spring" level="INFO"/>

<AsyncLogger name="com.xiaoju" level="INFO" additivity="false" includeLocation="true">
   <AppenderRef ref="INFO_FILE"/>
   <AppenderRef ref="ERROR_FILE"/>
  </AsyncLogger>

<AsyncLogger name="com.xiaojukeji.carrera.common.disf.EndpointManagerSD" level="WARN" additivity="false" includeLocation="false">
   <AppenderRef ref="INFO_FILE"/>
   <AppenderRef ref="ERROR_FILE"/>
  </AsyncLogger>

<AsyncLogger name="com.xiaoju.manhattan.financing.base.metric.odin" level="WARN" additivity="false" includeLocation="true">
   <AppenderRef ref="INFO_FILE"/>
   <AppenderRef ref="ERROR_FILE"/>
  </AsyncLogger>


  <Root level="INFO" includeLocation="false">
   <AppenderRef ref="INFO_FILE"/>
   <AppenderRef ref="ERROR_FILE"/>
  </Root>
 </Loggers>
</Configuration>

2.2.cpu

cpu占用过高一般是线程阻塞等原因导致,这部分需要使用show-busy-java-threads去定位代码

wget --no-check-certificate https://github.com/oldratlee/useful-scripts/archive/release.zip

然后可以基于命令去查找定位到线程阻塞的代码。我这里线程的Busy都是0并且没有线程Blocked的数据,是因为现在没有进行压测。

3.JVM

JVM优化,主要是降低gc的时间,避免出现fullGC 如何查看GC的时间,具体问题百度吧

下面给出一些JVM参数的模板

  • 8C16G:G1
java_opts="-XX:+UseG1GC -Xms8g -Xmx8g -Xss512k -XX:+PrintCommandLineFlags -Xloggc:/home/data1/logs/gc.log -XX:GCLogFileSize=300m \
-XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps \
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m -XX:ParallelGCThreads=8 \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/data1/logs/_heapdump"
  • 4C8G:G1
-server -Xms4g -Xmx4g -XX:MaxNewSize=2g                                      \
-XX:+UseG1GC -XX:InitiatingHeapOccupancyPercent=40 -XX:G1HeapRegionSize=8m   \
-XX:+ExplicitGCInvokesConcurrent -XX:ParallelGCThreads=4                     \
-Xloggc:/home/admin/logs/gc.log -XX:+PrintGCDetails -XX:+PrintGCDateStamps   \
-Dsun.rmi.dgc.server.gcInterval=2592000000                                   \
-Dsun.rmi.dgc.client.gcInterval=2592000000                                   \
-XX:MetaspaceSize=256m -XX:MaxMetaspaceSize=256m                             \
-XX:ReservedCodeCacheSize=256m                                               \
-XX:MaxDirectMemorySize=512m                                                 \
-XX:+HeapDumpOnOutOfMemoryError -XX:HeapDumpPath=/home/admin/logs            \
-XX:ErrorFile=/home/admin/logs/hs_err_pid%p.log

4.tomcat调优

在并发比较高的场景下还需要优化tomcat

核心优化的参数如下几个

  • tomcat.max-threads
  • tomcat.accept-count
  • max-connections

4.优化前后对比

4.1.优化前

客户端监控

服务端监控

4.2.优化后

客户端监控

服务端监控

性能分析工具

更多内容可关注公众号


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK