项目性能优化
服务容器优化
Tomcat优化
- ????通过什么可以判断出需要对Tomcat是瓶颈并需要进行调优???
优化原因
- 基于应用性能瓶颈分析得出结论,当响应时间较长时,系统瓶颈主要村在鱼Tomcat中
- 系统异常率较高,与Tomcat内部IO模型有关
- 服务端线程数计算公式TPS/(1000ms/RT均值)
嵌入式Tomcat配置
可以通过暴露/actuator/configprops接口获得当前tomcat的配置信息
- 做了任何修改一定要确认配置是否生效,否则等于白干!!!
# Tomcat的maxConnections、maxThreads、acceptCount三大配置,分别表示最大连接数,最大
线程数、最大的等待数,可以通过application.yml配置文件来改变这个三个值,一个标准的示例如
下:
server.tomcat.uri-encoding: UTF-8
# 思考问题:一台服务器配置多少线程合适?
server.tomcat.accept-count: 1000 # 等待队列最多允许1000个请求在队列中等待
server.tomcat.max-connections: 20000 # 最大允许20000个链接被建立
## 最大工作线程数,默认200, 4核8g内存,线程数经验值800
server.tomcat.threads.max: 800 # 并发处理创建的最大的线程数量
server.tomcat.threads.min-spare: 100 # 最大空闲连接数,防止突发流量
# 暴露所有的监控点
management.endpoints.web.exposure.include: '*'
# 定义Actuator访问路径
management.endpoints.web.base-path: /actuator
# 开启endpoint 关闭服务功能
management.endpoint.shutdown.enabled: true
- maxThreads:最大线程数
- 每一次Http请求到达web服务,Tomcat都会创建一个线程来处理该请求
- 最大线程数决定了web服务容器最多可以同时处理多少个请求,默认值为200
- 最大线程数只是影响Tps的因素之一
- 增加线程是有成本的,线程过多不仅会带来线程上线文切换的成本,而且线程也需要消耗内存资源
- JVM默认Xss堆栈大小为1M
- RT均值很低不用设置,均值很高可以考虑增加一定线程数
- 如果接口响应时间低于100ms,足以产生足够的TPS
- 1c2g:200以此类推
- accept-count:最大等待连接数
- 当调用Http请求数到达Tomcat的最大线程数时,还有新的请求进来,这是Tomcat会将剩余请求放入等待队列中
- accept-count就是队列能够接受的最大等待连接数
- 默认值是100,如果等待队列超了,新的请求会被拒绝(connection refused)
- Max Connections:最大连接数
- 同一时间内,Tomcat能够接受的最多达连接数。如果设置为-1,则表示不限制
- 默认值:
- 对于BIO模式,默认值是MaxThreads;如果使用定制的Executor执行器,那默认将是执行器的MaxThreads值
- 对于NIO模式,默认值是10000
- 当连接数达到最大值Max Connections后系统会继续接收连接,但不会超过accept-count限制
调优配置生效确认
调优前后性能对比
小结
提升Tomcat最大线程数,在高负载场景下,TPS提升接近1倍,同时RT大幅降低
网络IO模型调优
IO模型介绍
优化内容:使用NIO2的Http协议实现,对请求连接器进行改写
结论:待补充
@Configuration
public class TomcatConfig {
//自定义SpringBoot嵌入式Tomcat
@Bean
public TomcatServletWebServerFactory servletContainer() {
TomcatServletWebServerFactory tomcat = new
TomcatServletWebServerFactory() {
};
tomcat.addAdditionalTomcatConnectors(http11Nio2Connector());
return tomcat;
}
//配置连接器nio2
public Connector http11Nio2Connector() {
Connector connector = new
Connector("org.apache.coyote.http11.Http11Nio2Protocol");
Http11Nio2Protocol nio2Protocol = (Http11Nio2Protocol)
connector.getProtocolHandler();
//等待队列最多允许1000个线程在队列中等待
nio2Protocol.setAcceptCount(1000);
// 设置最大线程数
nio2Protocol.setMaxThreads(1000);
// 设置最大连接数
nio2Protocol.setMaxConnections(20000);
//定制化keepalivetimeout,设置30秒内没有请求则服务端自动断开keepalive链接
nio2Protocol.setKeepAliveTimeout(30000);
//当客户端发送超过10000个请求则自动断开keepalive链接
nio2Protocol.setMaxKeepAliveRequests(10000);
// 请求方式
connector.setScheme("http");
connector.setPort(9003); //自定义的端口,与源端口9001
可以共用,知识改了连接器而已
connector.setRedirectPort(8443);
return connector;
}
}
调优后性能对比
小结
将NIO升级为AIO之后,RT的毛刺大幅降低,异常数(超时3s)几乎为0。
容器优化Tomcat升级Undertow
介绍
Undertow是一个用Java编写的灵活的高性能Web服务器,提供基于NIO的阻塞和非阻塞API。
- 支持Http协议
- 支持Http2协议
- 支持Web Socket
- 最高支持到Servlet4.0
- 支持嵌入式
SpringBoot的web环境中默认使用Tomcat作为内置服务器,其实SpringBoot提供了另外2种内置的服务 器供我们选择,我们可以很方便的进行切换。
- Undertow红帽公司开发的一款基于 NIO 的高性能 Web 嵌入式服务器 。轻量级Servlet服务器, 比Tomcat更轻量级没有可视化操作界面,没有其他的类似jsp的功能,只专注于服务器部署,因此 undertow服务器性能略好于Tomcat服务器;
- Jetty开源的Servlet容器,它是Java的web容器。为JSP和servlet提供运行环境。Jetty也是使用Java 语言编写的。
配置操作过程
- 在spring-boot-starter-web排除tomcat
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-tomcat</artifactId>
</exclusion>
</exclusions>
</dependency>
- 导入其他容器的starter
<!--导入undertow容器依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-undertow</artifactId>
</dependency>
- 配置
# 设置IO线程数, 它主要执行非阻塞的任务,它们会负责多个连接
server.undertow.threads.io: 800
# 阻塞任务线程池, 当执行类似servlet请求阻塞IO操作, undertow会从这个线程池中取得线
程
# 默认值是IO线程数*8
server.undertow.threads.worker: 8000
# 以下的配置会影响buffer,这些buffer会用于服务器连接的IO操作,有点类似netty的池化内
存管理
# 每块buffer的空间大小越小,空间就被利用的越充分,不要设置太大,以免影响其他应用,合
适即可
server.undertow.buffer-size: 1024
# 每个区分配的buffer数量 , 所以pool的大小是buffer-size * buffers-per-region
# 是否分配的直接内存(NIO直接分配的堆外内存)
server.undertow.direct-buffers: true
- 压测
调优后性能对比
小结
- 更换了服务容器之后,RT更加平稳,TPS的增长趋势更平稳,异常数(超时3s)几乎为0。
- 在低延时情况下,接口通吐量不及Tomcat。
- 稳定压倒一切,如果只是写json接口,且对接口响应稳定性要求高,可以选用Undertow
其他可以优化的地方
- ARP-IO模型
- Jetty 容器
- KeepAliveTimeout【默认】
- MaxKeepAliveRequests
- …………
数据库调优
- 提升网站整体通吐量,数据库是关键之一
- 流畅页面的访问速度,良好的网站功能体验
- 避免网站页面出现访问错误 由于数据库连接timeout产生页面5xx错误
- 由于慢查询造成页面无法加载 由于阻塞造成数据无法提交
- 增加数据库的稳定性:很多数据库问题都是由于低效的查询引起的
影响数据库性能因素
- 服务器:OS、cpu、memory、网络
- Mysql
- 数据库表结构【对性能影响最大】
- 低效率的sql语句
- 超大的标
- 大事务
- 数据库配置
- 数据库整体架构
- …………
调优内容
- 优化sql语句:根据需求创建良好的sql语句
- 数据库表结构:索引、主键、多表关系
- Mysql配置调优:最大连接数、连接超时、线程缓存、查询缓存、排序缓存、连接查询缓存等
- 服务器硬件优化:多核CPU、更大的内存
使用索引加快查询速度(待定)
-
场景一:面试官问你,有没有做过数据库优化呀,我来问你一个特别简单的问题:查询的时候有没有给 查询字段加过索引?为什么要加,加上之后怎么就会变快呢?请您简单描述一下原因
- 索引就是事先排好顺序,从而在查询的时候可以使用二分法等高效率的算法查找
- 除了索引查找,就是一般顺序查找,这两者的差异是数量级的差异。
- 二分法查找的时间复杂度是O(log n),而一般顺序查找的时间复杂度是O(n)
- 索引的数据结构是B+树,是一种比二分法查找时间复杂度更好的数据结构。
-
全值匹配我最爱,最左前缀要遵守;
-
带头大哥不能死,中间兄弟不能断;
-
索引列上不计算,范围之后全失效;
-
Like百分写最右,覆盖索引不写星;
-
不等空值还有OR,索引失效要少用。
OpenResty调优
介绍
OpenResty是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方 模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服 务和动态网关。OpenResty的目标是让你的Web服务直接跑在 Nginx 服务内部,充分利用 Nginx 的非阻 塞 I/O 模型,不仅仅对 HTTP 客户端请求,甚至于对远程后端诸如 MySQL、PostgreSQL、Memcached 以及 Redis 等都进行一致的高性能响应。
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSDlike 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中 表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
缓存调优
多级缓存
缓存预热
进程内缓存
配置进程内缓存
JVM调优
什么时候进行JVM调优
- 系统吞吐量与响应性能不高或下降
- 堆内存(老年区)持续上涨达到设置的最大内存值
- Full Gc频繁
- Gc停顿时间过长,运行P99百分位响应时间
- 应用出现OOM等内存异常
- 应用中使用本地缓存,且占用大量的内存空间
调优内容
内存分配+垃圾回收
- 合理使用堆内存
- Gc搞高效回收垃圾对象,释放内存空间
是否可以把内存空间设置足够大,那么就不需要回收垃圾呢?
问题背景:JVM回收垃圾时机,当JVM内存占满触发垃圾回收!
不可以原因如下:
- 不回收垃圾,内存增长巨快,再大的空间都不够用;10w请求,3GB垃圾对象
- 物理层面:64位操作系统可以支持非常大的内存,但不是无限
- 32位操作系统: 2~32 = 4GB
- 64位操作系统: 2~64 =16384PB
- 内存设置既不能太大,也不能太小需要基于业务场景平衡考量:内存空间设置过大,一旦内存空间 触发垃圾回收,就会非常危险,寻找这个垃圾非常耗时,由于内存空间足够大,寻找这个垃圾的时 候,极其的消耗时间,因此导致程序停顿;
调优原则及目标
- 优先原则:优先架构调优和代码调优,JVM是不得已的手段
- 大多数的java应用不需要进行JVM调优
- 观测性原则:大仙问题解决问题,没有问题不找问题
目标
下面展示了一些JVM调优的量化目标的参考。注意:不同应用的JVM调优量化目标是不一样的。
- 堆内存使用率 <= 70%;
- 老年代内存使用率<= 70%;
- avg pause <= 1秒;
- Full GC 次数0 或 avg pause interval >= 24小时 ;
- 创建更多的线程
回复