服务优化实践

简介: v服务优化实践

性能分析常用方法

1. top

top指令默认用来监控cpu使用情况,根据cpu使用情况,分析整个系统运作情况(大多数系统cpu密集型)

top指令查询的进程,将会根据cpu使用率大小进行排序,使用的比较多的排在前面,也可以按照内存进行排序,如果需要按照内存排序:按一下m键;

Load verage: 1分钟cpu平均使用率 5分钟cpu平均使用率 15分钟cpu平均使用率

单核cpu(1个核心cpu):

Load average < 1 , 表示cpu毫无压力,比较空闲,运行流畅

Load average= 1, 表示cpu刚刚被占满,没有可提供的cpu资源了

Load average>1,表示cpu满负荷运作,线程处于阻塞状态,等待cpu资源了

Load avergage > 5 , 表示cpu线程严重阻塞,如果时间一长,系统可能被打垮

4核cpu(4个核心cpu):

Load average < 4 , 表示cpu毫无压力,比较空闲,运行流畅

Load average= 4, 表示cpu刚刚被占满,没有可提供的cpu资源了

Load average>4,表示cpu满负荷运作,线程处于阻塞状态,等待cpu资源了

Load avergage > 20 , 表示cpu线程严重阻塞,如果时间一长,系统可能被打垮

2. free

free 排查线程内存问题重要指令,存在问题很多情况下也会使得cpu使用率飙高

3. df

df指令排查磁盘使用情况,有是时候服务出现问题,可能是磁盘不够了。

也可以使用du命令查询磁盘空间情况:

du命令: du -h –max-depth-1 find / -type d -size +50M

定时清理磁盘空间: ****** cat /dev/null > /usr/Catalina.out

4. dstat

dstat: 查看网络通畅情况

dstat: 查看网络通畅情况

netstat: 查询tcp连接情况

lsof : 查看tcp网络连接

查询网络连接: netstat -antp | grep ESTABLISHED lsof -I tcp

Tomcat调优

问题:项目在什么时间点介入调优??(开发之前,测试,线上运行时候)

答案:大多数情况下,项目上线后运行一段时间(持续用户流量,一切问题根源是量的积累,随着用户增多,随着数据量增大,并发的攀升,一段时间,系统出现了问题),此时就必须立马接入调优;

Tomcat配置

1) 空闲线程数(最低保活线程数)

2)最大线程数

3)最大连接数(提升连接数)

4)tomcat等待队列(提升队列)

开启20w个样本测试: 发现最大线程数为232个,有32个属于tomcat本身自带的线程,200个线程属于业务线程。

问题:tomcat最大线程数为200,是否意味着把线程数提升,服务性能是否一定会提升呢??

答案: 理论上,一定会提升,实际上看接口是否是耗时操作

Tomcat调优配置

server:
  port: 9000
  tomcat:
    min-spare-threads: 10
    max-connections: 10000
    max-threads: 800
    accept-count: 1000
    uri-encoding: UTF-8

把优化后配置进行部署,观察得到的线程数量为800,确实对增大了线程数量:

优化后的tps,经过tomcat调优后,发现tps吞吐能力提升了4倍。

JVM调优

Jvm调优本质: 对内存的调优(使用较少内存资源,获取最大的性能),及时释放内存空间垃圾对象;

典型参数

服务器:4cpus , 8GB ,目前来说jvm调优和cpu没有关系,跟内存有关系,因此jvm调优就是调内存:

1、-Xmx4000m 设置堆内存大小(一般情况下,设置物理内存一半即可-经验值设置-如果你想要更加精确的设置,根据程序在压力测试情况下反馈,做出合理的调试; 或是通过线上运行项目的反馈情况,进行合理的调试,不同项目,不同业务场景,调试的目标是不一样的)

2、-Xms4000m 设置堆内存初始化大小,必须和mx设置一致,防止内存抖动,从而损耗性能。

3、-Xmn2g 设置年轻代大小 (eden,s0,s1)

4、-Xss256k 设置线程堆栈大小,jdk1.5线程堆栈大小默认1MB, 相同内存空间,堆栈越小,操作系统就会创建更多的线程,从而提升性能

JVM在进行gc的时候,通过一些参数打印gc日志,记录gc详细过程情况,以便于分析gc参数设置是否合理,然后对gc参数做出及时更改调试;gc日志可以通过可视化的工具分析(gceasy.io)分析即可。

-XX:+PrintGCDetails : 打印gc详细信息

-XX:+PrintGCTimeStamps : 打印gc时间信息

-XX:+PrintGCDateStamp: 打印gc日期信息

-XX:+PrintHeapAtGC : 打印gc堆内存信息

Jvm参数设置,添加到启动脚本即可:

nohup java -Xmx4000m -Xms4000m -Xmn2g -Xss256k -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log  -jar jshop.jar --spring.addition-location=application.yaml > jshop.log 2>&1 &

压力测试,观察gc日志,反馈gc情况:

压测后,把gc.log文件下载下来传到gceasy.io网站进行分析

gc的次数,时间

调试参数

当前服务器配置:8GB, 在大内存服务器下,产生对象很难快速把内存占满,触发gc, 因此对于大内存服务器来说,原则上来说不需要太多的调优,但是对于高并发,高可用的项目来说,jvm调优还是有必要的;因为防止频繁的gc(minor gc , fullgc 都会使得程序stw , 卡顿)

试验1: 把内存调小,产生频繁的gc , stw 卡顿时间将会延长

把内存调小,发现程序卡顿时间变长,变成了560+200=760ms;

小结:

1) 频繁young gc , 必然会导致老年代空间对象变多,甚至会频繁fullgc ,oom

2) 当生命周期比较短暂的对象比较多的话,建议把年轻代空间设置大一些,当老年代空间对象比较多,建议把老年代空间设置大一些。

试验2: 把内存空间调大,不会产生频繁的gc,因此程序运行速度变快,也就是说程序的性能提升了。

思考:为什么会发生fullgc 问题,按照垃圾回收理论(old区域被装满了才会触发fullgc)?

因此根据fullgc触发条件:

1、metaspace 元数据空间发生扩容,就会发生fullgc, 且每扩容一次,发生一次fullgc , 如果需要存储的数据超过maxMetaspace, 发生OOM

2、old memory 内存被占满了,发生fullgc,甚至发生oom;

1) 通过gc日志观察metaspace是否发生扩容

通过日志可以发现:元数据库初始化大小19822k

通过gc日志的观察,发现metaspace元数据空间发生了扩容;从19822k –> 21254k (第一次扩容,发生第一次fullgc)

2) 通过命令查询metaspace元数据空间初始化大小

查询命令: java -XX:+PrintFlagsFinal -version | grep MetaspaceSize

发现fullgc的原因后,就去把metaspace元数据空间的大小设置大一些,让其扩容不要发生了:

年轻代,老年代大小比例:改如何去设置?

JVM堆内存大小 = 年轻代内存空间 + 老年代内存空间

调整年轻代大小,或者是调整老年代大小都会对整个gc都会有影响(会影响gc性能:年轻代内存空间设置小了,频繁触发minor gc , gc时间变快–balance),因此堆内存空间大小比例调试也非常重要的;

Sun(官方推荐设置):

3(young): 8(堆内存空间) 强调: 此设置是推荐设置,不代表最终设置,具体设置还需要根据你的代码业务场景来进行设置;

举个栗子:

当你的代码局部对象非常多,非常适合在年轻代第一次就全部回收,这样业务场景,应该强调把年轻代空间设置大一些,尽量让对象在年轻代被回收

当你老年代对象较多时候,且发送频繁的fullgc , 建议把老年代空间调大一些,方式fullgc的发生。

推荐设置:

官方推荐设置: eden:s0:s1=8:1:1

设置方式: -XX:SurvivorRatio = 8

GC经典组合

吞吐量优先

并行垃圾回收器组合: Parallel Scavenge + Parallel Old , JDK8默认的垃圾回收器,ps+po充分利用多核心cpu资源,在多个cpu上同时执行gc; 因此把这样垃圾回收器叫做吞吐量优先组合;

显式配置(默认不用配)吞吐量优先垃圾回收器组合:

nohup java -Xmx4000m -Xms4000m -Xmn2g -XX:SurvivorRatio=8 -XX:+UseParallelGC -XX:+UseParallelOldGC -Xss256k -XX:MetaspaceSize=200m  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log  -jar jshop.jar --spring.addition-location=application.yaml > jshop.log 2>&1 &
响应时间优先

响应时间优先的垃圾回收器组合:ParNew + CMS (业务线程,gc线程交叉执行,降低业务线程停顿时间,从而降低业务卡顿)

nohup java -Xmx4000m -Xms4000m -Xmn2g -XX:SurvivorRatio=8 -XX:+UseParNewGC -XX:+UseConcMarkSweepGC -Xss256k -XX:MetaspaceSize=200m  -XX:+PrintGCDetails -XX:+PrintGCTimeStamps -XX:+PrintGCDateStamps -XX:+PrintHeapAtGC -Xloggc:gc.log  -jar jshop.jar --spring.addition-location=application.yaml > jshop.log 2>&1 &
G1

G1 垃圾回收器: 一个在逻辑上分代的垃圾回收器,使用方式非常简单,高效: -XX:+UseG1GC

连接池调优

druid:
  #配置初始化大小、最小、最大
  initial-size: 1
  min-idle: 5
  max-active: 10
  max-wait: 1200
  time-between-eviction-runs-millis: 600000
  # 配置一个连接在池中最大空闲时间,单位是毫秒
  min-evictable-idle-time-millis: 300000
  # 设置从连接池获取连接时是否检查连接有效性,true时,每次都检查;false时,不检查
  test-on-borrow: true
  #设置往连接池归还连接时是否检查连接有效性,true时,每次都检查;false时,不检查
  test-on-return: true
  # 设置从连接池获取连接时是否检查连接有效性,true时,如果连接空闲时间超过minEvictableIdleTimeMillis进行检查,否则不检查;false时,不检查
  test-while-idle: true
  # 检验连接是否有效的查询语句。如果数据库Driver支持ping()方法,则优先使用ping()方法进行检查,否则使用validationQuery查询进行检查。(Oracle jdbc Driver目前不支持ping方法)
  validation-query: select 1 from dual
  keep-alive: true
  remove-abandoned: true
  remove-abandoned-timeout: 80
  log-abandoned: true
  #打开PSCache,并且指定每个连接上PSCache的大小,Oracle等支持游标的数据库,打开此开关,会以数量级提升性能,具体查阅PSCache相关资料
  pool-prepared-statements: true
  max-pool-prepared-statement-per-connection-size: 20
  # 配置间隔多久启动一次DestroyThread,对连接池内的连接才进行一次检测,单位是毫秒。
  #检测时:
  #1.如果连接空闲并且超过minIdle以外的连接,如果空闲时间超过minEvictableIdleTimeMillis设置的值则直接物理关闭。
  #2.在minIdle以内的不处理。
问题1:max-active最大连接数设置多少合适?

压力测试:max-active:10 , TPS = 20000

max-active:15 ,TPS = 16000 TPS , 发现连接越多,tps会下降;

max-active:20 , tps = 14000 tps

思考:经过测试,发现随着连接数变多,发现数查询据库性能越来越低;这是什么原因呢??

1、数据库连接连接数不够吗?

压力测试: 观察,是否和连接数有关系?

经过测试,发现数据库查询性能和连接数目前来说没有什么关系!性能其实和客户端设置有关系,当然归根结底是因为数据库的问题;

连接数越多,抢占cpu资源(上下文切换的开销比较耗时,因此降低业务处理性能)

连接池中,io最耗时的,查询的时候,cpu指令执行结束后,然后线程存取数据(io操作:磁盘非常耗时)

最大连接数计算公式 = (cpu核心数 X 2)+ 磁盘数量 = 10

问题2:max-wait设置多少合适呢?

max-wait : 从连接池中获取连接超时等待时间,单位ms,这个参数和连接超时有关系的:

获取连接超时直接原因:

1、连接池未初始化(初始化耗时)

2、连接池使用的时候,连接已经被释放

3、连接正在使用中,需要重建建立连接

4、连接已耗尽,需要等待归还后才能获取连接

设置多少超时时间比较合适。防止连接长时间阻塞,因此需要设置合理的max-wait 的值:

# 推荐设置

1、内网模式下(网络状态非常好): 800ms

2、外网模式下(网络抖动不可避免):max-wait >=1200ms ,TCP建立连接时间耗时1s;

防止死连接

给这个链接设置一个过期时间(等待返回结果超时,如果在规定时间内无法获取结果,释放连接)

connectionTimeout = 3000 # 单位是ms, 连接超时

socketTimeout = 1200 # 单位是ms, 连接建立后,如果在1200ms获取不到数据,连接就自动释放;

分布式部署

为了进一步提高项目吞吐能力,对项目进行水平扩容(服务集群部署),把项目部署2个服务节点,在请求接入层使用openresty(nginx)来实现负载均衡;

nginx.conf配置

根据刚才我们测试,发现性能瓶颈不在数据库,因此数据库测试tps=40000, 项目测试tps=800,因此性能瓶颈不在数据库,而在项目,因此提升系统吞吐能力,必然需要把服务进行水平扩容;

预测:性能瓶颈不在数据库,也不在接入层Openresty,那么项目进行水平扩容后,tps = 800 X 2 = 1600 TPS

相关实践学习
日志服务之使用Nginx模式采集日志
本文介绍如何通过日志服务控制台创建Nginx模式的Logtail配置快速采集Nginx日志并进行多维度分析。
目录
相关文章
|
30天前
|
Ubuntu NoSQL 关系型数据库
Ubuntu系统下安装常用软件
Ubuntu系统下安装常用软件
59 0
Ubuntu系统下安装常用软件
|
1月前
|
安全 网络安全 API
Hotmail邮箱设置Exchange的方法步骤
如何使用Exchange协议设置Hotmail邮箱:在邮件客户端(如Outlook)中添加新账户,选择Exchange服务,输入邮箱全址(@hotmail.com)和服务器地址(outlook.office365.com),启用SSL加密,完成设置后即可在各设备同步邮件、日历和联系人。AokSend提供触发式验证码发信服务,支持SMTP/API接口,高触达,独立IP和服务器。
|
1月前
|
开发者
第十二期乘风伯乐奖--寻找百位乘风者伯乐,邀请新博主入驻即可获奖
乘风伯乐奖,面向阿里云开发者社区已入驻乘风者计划的博主(技术/星级/专家),邀请用户入驻乘风者计划即可获得乘风者定制周边等实物奖励。本期面向阿里云开发者社区寻找100位乘风伯乐,邀请人数月度TOP 1 获奖者(大于108人)可获得AirPods2代!
2433 8
|
30天前
事务提交后回调
事务提交后回调
12 2
|
30天前
使用Hibernate-Validate进行参数校验
使用Hibernate-Validate进行参数校验
18 3
|
30天前
|
NoSQL Java Redis
SpringBoot集成Redis
SpringBoot集成Redis
424 0
|
1月前
|
机器学习/深度学习 存储 分布式计算
机器学习PAI常见问题之DLC的数据写入到另外一个阿里云主账号的OSS中如何解决
PAI(平台为智能,Platform for Artificial Intelligence)是阿里云提供的一个全面的人工智能开发平台,旨在为开发者提供机器学习、深度学习等人工智能技术的模型训练、优化和部署服务。以下是PAI平台使用中的一些常见问题及其答案汇总,帮助用户解决在使用过程中遇到的问题。
|
SQL 存储 算法
【笔记】最佳实践—偏分析场景的实践和优化
PolarDB-X是一款以TP为主的HTAP数据库,也支持一定场景的分析需求。而典型的分析场景一般有以下几类特征:
【笔记】最佳实践—偏分析场景的实践和优化
|
8天前
|
SQL 自然语言处理 数据挖掘
NL2SQL技术方案系列(1):NL2API、NL2SQL技术路径选择;LLM选型与Prompt工程技巧,揭秘项目落地优化之道
NL2SQL技术方案系列(1):NL2API、NL2SQL技术路径选择;LLM选型与Prompt工程技巧,揭秘项目落地优化之道
NL2SQL技术方案系列(1):NL2API、NL2SQL技术路径选择;LLM选型与Prompt工程技巧,揭秘项目落地优化之道
|
11天前
|
关系型数据库 MySQL Linux
centos7安装mysql最新教程
centos7安装mysql最新教程
41 7