一.成功的应用都是相似的,失败的应用各有各的失败
对于Web应用来说,影响性能的集中体现在网络/CPU/内存/IO/数据库/缓存这6个环节的处理上。一个稳定应用的特征应该包括:
1. 网络流量平稳,连接数保持稳定
2. 低CPU负载
3. 内存曲线平稳
4. IO高响应
5. 数据库的低负载,高响应
简单的来说,对于每个不稳定的应用来说,都会有一个瓶颈,通过以上的工具分析,加上自己的判断,找到那个瓶颈,解决它!
我们可以通过以下监控工具可监控到应用的各项指标
1. 网络监控工具 Cacti ,可以监控到整个服务器的流量/硬盘/CPU负载等情况
2. JVM 监控 JConsole (JDK 1.5 以上版本自带), %JAVA_HOME%/bin/jconsole
Java 启动加上一下参数:
-Dcom.sun.management.jmxremote.port=5004 (监控端口)
-Dcom.sun.management.jmxremote.ssl=false
-Dcom.sun.management.jmxremote.authenticate=false
3. Visual VM (JDK 1.6_07 以上版本自带) %JAVA_HOME%/bin/jvisualvm
配置方法同JConsole , Visual VM 比 JConsole 多了一个实用功能Thead Dump.
4. Kill -3 来获取 Thead Dump
TheadDump可以很清晰的告诉你JVM当前正在做什么,内存里有哪些进程,是否有死锁。重点观察RUNNABLE 和 LOCK 是否有死锁。
5. 数据库查看
MySql 的 show processlist命令
二.调优手段和策略
1. 网络层优化
不同类型的应用所能承受的网络负载各不相同,对于长连接应用来说,比如下载应用,所能承受的连接数就相对低,对于短链接应用,比如计数应用,所能承受的连接数就相对高,对于一般有数据库的应用来说,每秒的连接数保持在700以下都是没问题的。
这里调优的方面包括:
1) Linux可以优化网络的TCP参数,提高网络的响应,
2) 增加服务器实现负载均衡,目的降低单台服务器流量
3) 优化程序,尽可能的减少处理的时间,如果一定有长时间处理的场景,可以采用异步方式处理。先返回一个请求ID,然后客户端再通过这个ID来获得结果。
2. CPU优化
就目前的硬件环境来看,CPU都不会是一个瓶颈。如果CPU出现负载高,基本上都是程序处理不当引起,比如有深度递归或者循环或者频繁写磁盘,一旦出现高负载,会引起一系列的连锁反应,响应降低,连接数增加。
这个环节主要是优化程序:
1) 减少循环和递归
2) 减少synchronized 的用法。对于多服务器场景,要实现锁机制,用synchronized 不一定适合。Synchronized 使用不当,极容易造成死锁。
3) 减少死锁发生的可能性。JConsole提供了一个检测死锁的方法,TheadDump 也可以分析死锁。
3. 内存优化
对于Java应用来说,内存调优是关键。先来认识一下Java内存的构成和垃圾回收的机制:
堆是应用程序使用的主要部分,一旦堆满,应用程序就会抛出Out Of Memory错误。具体关于堆的构成以及垃圾回收算法,可以参考文档:
http://aleung.blogbus.com/logs/4712392.html
这个环节的优化:
1) 增加JVM内存,使得可使用的堆内存尽可能多,延长垃圾回收的时间。需要注意的是,一般来说,回收1G内存所需要的时间是7秒左右,如果这个时间访问量比较高,极容易导致应用停止响应,所以并非是越大内存越好。
通过增加 XX:+PrintGCDetails 参数可以观察到垃圾回收的频率和时间
2) 调整垃圾回收策略,加快JVM的回收,因为Web应用响应高,很多都是无用内存,加快回收可以保证有效堆会更多,这种方式会消耗更多的CPU。
4. IO 优化
为了提高IO的响应,尽可能的不要把所有文件写到同一个目录下,所有的请求集中在一个磁盘上。当一个目录下超过2000个文件,就会导致IO响应下降。
这个环节调优包括
1) 采用更快的IO设备,比如NAS 存储设备
2) 如果可能,将文件缓存到内存,或者先写内存,定时回写磁盘。
3) 将读和写操作分开来实现。比如一个线程只写,一个线程只读。
5. 数据库优化
这个环节可以优化的地方比较多,通常由以下几个方面:
1) 建立索引
2) 对于复杂的业务逻辑,采用存储过程实现
3) 对于大数据量,采用归档或者分表方式进行处理。比如 100万的数据要0.1秒就读取出来,如果采用分表,降低的数据规模,实现这个是不难的。
6. 缓存
这个的最终目的是为了保证以上各个环节顺畅。
缓存从架构上来分包括
1) 集中式缓存,如Memcached
2) 分布式缓存,如 OpenJPA 的二级缓存,采用JGroup 方式来同步
从类型上来分包括
1) 页面缓存,页面缓存可以定时更新
2) 数据缓存,可以先把数据缓存到内存,然后定时写入数据库。
三.构架一个稳定应用
1. 应用分类
首先要认识到,不同的应用需要的资源和处理的方式是不一样的,所以在开发一个应用之前,要认识到应用的特征及其可能的瓶颈。为每类应用定制一个解决套餐,是成功的基础。
2. 可重用的处理模型
要建立一个可重用的程序库,比如计数框架,缓存框架,模板框架。当你需要的时候,不是重新开发,而是拿过来直接使用就可以了。
3. 持续监控和改进
随着时间的推移,应用所需要的资源也会不同,有些应用可能比预想的差,需要减少资源,有些发展迅猛,需要增加资源。这些都需要我们建立一个可持续的监控机制。
四.常见不稳定问题处理办法
1. 内存只涨不降
查看一下网络流量,看是否是持续增加,如果网络流量正常,很可能你的程序有内存泄漏问题。
建议的办法:改程序/增加内存或者提高垃圾回收的频率。
更多有待完善中。。
本指南不是一个教科读书,更多的是经验总结,目的是为了开发高响应/高并发的Web应用,会不断的补充完善,如果需要具体的帮助,可以Google一下。