系统性能定义
1.Throughtput,吞吐量。每秒钟可以处理的请求数,任务数。
2.Latency,系统延迟。系统在处理一个请求或一个任务时的延迟。
两者的关系:
- throughput越大,Latency越差。因为请求过多,系统繁忙,自然响应速度会降低。
- latency越好,能支持的Throughput越高。latency短说明系统处理速度快,自然便可以处理更多的请求。
要说明一个系统的性能,便需要收集系统的Throughput和Latency两个值。
- 首先,需要定义Latency这个值。这个值的意义不是普适性的,需要根据不同的业务来定义,比如一些实时系统,需要的latency便会很短,如5ms。
- 其次,开发性能测试工具。一个用来制造高强度的Throughput,另一个用来监测latency。对一个工具可以用很多现有开源的或者商业的。
latency可以通过在代码中打日志测量,但是会影响到程序的执行,而且也只能看到代码内部的latency,无法计算到线上系统的。对于网络延迟可以使用一些抓包工具。 - 最后,开始性能测试。不断的调整throughput,观察系统负载,观察latency。找到系统的最大负载并且知道系统的响应延迟是多少。
定位性能瓶颈
当在测试过程中,发现性能有问题时,不用急于去怀疑代码。首先要查看操作系统的报告,查看OS的cpu使用率,内存使用率,查看系统IO和网络的IO,网络连接数。通过观察以上数据,基本可以判断出性能问题出在哪。
- CPU使用率。如果使用率不高,但是Throughput和latency上不去,说明我们的程序没有忙于计算,而是干了别的事情,比如IO。
- 然后可以看下IO大不大,IO和CPU一般反着来,CPU利用率高则IO低,IO大则CPU小。IO看三个点,磁盘文件IO,驱动程序的IO,内存换页率。
- 查看网络带宽状态。
- 如果CPU不高,IO不高,内存使用不高,网络带宽使用不高,但是系统性能上不去,说明程序可能有问题。比如程序被阻塞,或者是一些数据库的索引未建立。
性能测试结果的分析:
结合jmeter的测试报告
上图是一个jmeter的测试报告,在报告中,有平均响应时间和90%,95%,99%的响应时间。
首先给结论:平均值不靠谱
再来说说为什么,在进行性能测试时,得到的结果数据不会都一样,而是有高有低。例如测试了10次,有9次都是1ms,但是第十次的时间是2s,那么平均值就是200.9,很明显这个并不能正确反映我们系统的性能。这个2s,应该作为一个异常值去除掉。就好像体育比赛一样,很多项目评委打分时,是需要去掉一个最高分和最低分的。
设备指纹4.x的版本中,js集成在server中,当页面集成了js时,需要先下载js,然后才是使用js拿到设备指纹。第一次下载后,js便会缓存在本地,方便以后调用就不需要每次都去下载js了。
而下载js便会受到网络的影响。在测试时,经常下载js会达到秒级。所以在性能测试时,需要将下载js的这个动作不纳入统计中。
正确的统计是使用百分比分布统计
然后继续看上面的图,99%Line;
这个就是表示99%的请求均小于某值,在测试过程中,应该重点关注这样的数据结果。
常见系统瓶颈
代码调优
- 类型转换。
之前在3.x的设备指纹中,出现过严重的性能下降问题,后来定位发现,是在类型转换时用了fastjson的一个方法,导致系统时间大量的耗在上面了。 - 异步操作。有些同步操作,会非常影响性能,尤其在网络较差的情况下,很可能阻塞我们的业务。使用异步,可以提升性能但是会带来编码上的复杂性。异步下的状态通知通常是个问题,比如消息事件通知方式,有回调方式等,这些方式同样可能会影响性能。但是通常来说,异步操作会让性能的吞吐率有很大提升(Throughput),但是会牺牲系统的响应时间(latency)。
现在4.5的设备指纹中,便将以前的同步日志改成了异步写日志。 - IO模型调优
之前在学习netty的过程中,了解了几种不同的网络IO模型,这个需要根据自己的业务情况选择合适的IO模型。
系统调优
- 多核CPU调优。之前在使用mongo的时候,每次进入mongo shell ,总是会打印几串warning,由于平时是只看error忽略warning的,就没太在意。后来线上性能除了问题,才注意看了那几个warning。其中一个就是NUMA。
NUMA技术(Non-Uniform Memory Access)。
NUMA的相关策略:
- 每个进程(或线程)都会从父进程继承NUMA策略,并分配有一个优先node。如果NUMA策略允许的话,进程可以调用其他node上的资源。
简单点说,在有多个物理CPU的架构下,NUMA把内存分为本地和远程,每个物理CPU都有属于自己的本地内存,访问本地内存速度快于访问远程内存,缺省情况下,每个物理CPU只能访问属于自己的本地内存。对于MongoDB这种需要大内存的服务来说就可能造成内存不足
网络调优
- TCP调优
TCP链接一是会占用文件描述符,另一个是会开缓存,一般来说一个系统可以支持的TCP链接数是有限的,TCP链接对系统的开销是很大的。正是因为TCP是耗资源的,所以对于TCP的链接数量需要关注。当过多时,很可能大量消耗资源导致系统无响应。之前线上反馈,服务端的链接数过多,后来排查发现,是因为安卓sdk使用了HTTP1.1,默认开启的是keepalive长连接导致的链接不释放。
所以,我们要注意配置KeepAlive参数,这个参数的意思是定义一个时间,如果链接上没有数据传输,系统会在这个时间发一个包,如果没有收到回应,那么TCP就认为链接断了,然后就会把链接关闭,这样可以回收系统资源开销。(注:HTTP层上也有KeepAlive参数)对于像HTTP这样的短链接,设置一个1-2分钟的keepalive,这可以在一定程度上防止DoS攻击。
数据库调优
传统的关系型数据库不太了解,对于NOSQL数据库,例如mongodb,需要关注索引的情况。之前在测试设备指纹的时候,便出现了系统性能越跑越慢的情况,后来发现是索引建立不全导致的。