通向架构师的道路第三天之apache性能调优Word文件下载.docx
《通向架构师的道路第三天之apache性能调优Word文件下载.docx》由会员分享,可在线阅读,更多相关《通向架构师的道路第三天之apache性能调优Word文件下载.docx(22页珍藏版)》请在冰豆网上搜索。
你系统就仅仅只有一条交易?
你怎么能够判断这条交易涉及到的数据量最大?
更不用说交易是彼此间有依赖的,可能a+b+c+d的交易的一个混合组织就能够超出你单笔交易所涉及到的数据量了呢!
2.2
合理的制定系统最大用户、并发用户
提供下面这个公式,以供大家在平时或者日常需要进行的性能测试中作为一个参考。
(1)
计算平均的并发用户数:
C=nL/T
公式
(1)中,C是平均的并发用户数;
n是loginsession的数量;
L是loginsession的平均长度;
T指考察的时间段长度。
(2)
并发用户数峰值:
C’≈C+3根号C
公式
(2)则给出了并发用户数峰值的计算方式中,其中,C’指并发用户数的峰值,C就是公式
(1)中得到的平均的并发用户数。
该公式的得出是假设用户的loginsession产生符合泊松分布而估算得到的。
实例:
假设有一个OA系统,该系统有3000个用户,平均每天大约有400个用户要访问该系统,对一个典型用户来说,一天之内用户从登录到退出该系统的平均时间为4小时,在一天的时间内,用户只在8小时内使用该系统。
则根据公式
(1)和公式
(2),可以得到:
C=400*4/8=200
C’≈200+3*根号200=242
F=VU*R/T
其中F为吞吐量,VU表示虚拟用户个数,R表示每个虚拟用户发出的请求数,T表示性能测试所用的时间
R=T/TS。
2.3
影响和评估性能的几个关键指标
从上面的公式一节中我们还得到了一个名词“吐吞量”。
和吞吐量相关的有下面这些概念,记录下来以供参考。
²
吞吐量
指在一次性能测试过程中网络上传输的数据量的总和。
对于交互式应用来说,吞吐量指标反映的是服务器承受的压力,在容量规划的测试中,吞吐量是一个重点关注的指标,因为它能够说明系统级别的负载能力,另外,在性能调优过程中,吞吐量指标也有重要的价值。
吞吐率
单位时间内网络上传输的数据量,也可以指单位时间内处理客户请求数量。
它是衡量网络性能的重要指标,通常情况下,吞吐率用“字节数/秒”来衡量,当然,你可以用“请求数/秒”和“页面数/秒”来衡量。
其实,不管是一个请求还是一个页面,它的本质都是在网络上传输的数据,那么来表示数据的单位就是字节数。
事务
就是用户某一步或几步操作的集合。
不过,我们要保证它有一个完整意义。
比如用户对某一个页面的一次请求,用户对某系统的一次登录,淘宝用户对商品的一次确认支付过程。
这些我们都可以看作一个事务。
那么如何衡量服务器对事务的处理能力。
又引出一个概念----TPS
TPS(TransactionPersecond)
每秒钟系统能够处理事务或交易的数量,它是衡量系统处理能力的重要指标。
点击率(HitPerSecond)
点击率可以看做是TPS的一种特定情况。
点击率更能体现用户端对服务器的压力。
TPS更能体现服务器对客户请求的处理能力。
每秒钟用户向web服务器提交的HTTP请求数。
这个指标是web应用特有的一个指标;
web应用是“请求-响应”模式,用户发一个申请,服务器就要处理一次,所以点击是web应用能够处理的交易的最小单位。
如果把每次点击定义为一个交易,点击率和TPS就是一个概念。
容易看出,点击率越大。
对服务器的压力也越大,点击率只是一个性能参考指标,重要的是分析点击时产生的影响。
需要注意的是,这里的点击不是指鼠标的一次“单击”操作,因为一次“单击”操作中,客户端可能向服务器发现多个HTTP请求。
吞吐量指标的作用:
ü
用户协助设计性能测试场景,以及衡量性能测试场景是否达到了预期的设计目标:
在设计性能测试场景时,吞吐量可被用户协助设计性能测试场景,根据估算的吞吐量数据,可以对应到测试场景的事务发生频率,事务发生次数等;
另外,在测试完成后,根据实际的吞吐量可以衡量测试是否达到了预期的目标。
用于协助分析性能瓶颈:
吞吐量的限制是性能瓶颈的一种重要表现形式,因此,有针对性地对吞吐量设计测试,可以协助尽快定位到性能冰晶所在位置。
平均相应时间
也称为系统响应时间,它一般指在指定数量的VU情况下,每笔交易从mouse的click到IE的数据刷新与展示之间的间隔,比如说:
250个VU下每笔交易的响应时间不超过2秒。
当然,响应时间也不能一概而论,对于实时交易如果银行柜台操作、超市收银员(邪恶的笑。
)的操作、证交所交易员的操作来说这些操作的响应时间当然是越快越好,而对于一些企业级的如:
与银行T+1交易间的数据跑批、延时交易、T+1报表等,你要求它在2秒内响应,它也做不到啊。
就好比你有个1MB的带宽,你传的东西是超过4MB,你要它在2秒内跑完理论速度也做不到啊,对吧,所以有些报表或者数据,光前面传输时间就不止两秒了。
一口咬死说我所有的交易平均相应时间要2秒,真的是不科学的!
2.4
合理的性能测试
VU数量的增加
一个合理的性能测试除了需要合理的计算VU的数量、合理的设置系统平均响应时间外还需要合理的在测试时去规划发起VU的时间,比如说,我看到有人喜欢这样做压力测试。
第一秒时间,500个并发用户全部发起了。
结果导致系统没多久就崩了,然后说系统没有满足设计要求。
为什么说上述这样的做法是不对的?
我们说不是完全不对,只能说这样的测试已经超过了500个VU的并发的设计指标了。
合理的并发应该是如下这样的:
有25-50个VU开始起交易了,然后过一段时间又有25-50个用户,过一段时间又增加一些VU,当所有的设计VU都发起交易了,此时,再让压力测试跑一段时间比如说:
24*7是比较合理的。
所以VU数量不是一上手就500个在一秒内发起的,VU数量的增加应该如下面这张趋势图:
这是一个阶梯状的梯型图,可以看到VU的发起是逐渐逐渐增多的,以下两种情况如果发生需要检查你的系统是否在原有设置上存在问题:
VU数量上升阶段时崩溃
有时仅仅在VU数量上升阶段,系统就会了现各种各样的错误,甚至有崩溃者,这时就有重新考虑你的系统是否有设置不合理的地方了。
VU全部发起后没多久系统崩溃
VU在达到最高值时即所有的VU都已经发起了,此时它是以一条直的水平线随着系统运行而向前延伸着的,但过不了多久,比如说:
运行24*7小时,运行了没一、两天,系统崩溃了,也需要做检查。
所以,理想的性能测试应该是VU数量上升到最终VU从发起开始到最后所有VU把交易做完后,VU数量落回零为止。
吐吞量的变化
从2.3节我们可以知道,吞吐量是随着压力/性能测试的时间而逐渐增大的,因此你的吞吐量指示应该如下图所示:
肯定是这样,你的吞吐量因该是积累的,如果你的吞吐量在上升了一段时间后突然下落,而此时你的性能测试还在跑着,如下图所示:
那么,此时代表什么事情发生了?
你可以查一下你的loaderrunner或者jmeter里对于这段吞吐量回落期间的交易的response的状态进行查看,你将会发现大量的error已经产生,因为产生了error,所以你的交易其实已经出错了,因此每次运行的数据量越来越小,这也就意味着你的压力测试没有过关,系统被你压崩了!
平均响应时间
平均响应时间如VU的数量增加趋势图一样,一定是一开始响应时间最短,然后一点点增高,当增高到一定的程度后,即所有的VU都发起交易时,你的响应时间应该维持在一个水平值,然后随着VU将交易都一笔笔做完后,这个响应时间就会落下来,这段时间内的平均值就是你的系统平均响应时间。
看看它,有没有符合设计标准?
内存监控
我们就来说AppServer,我们这边用的是Tomcat即SUN的JVM的内存变化,我们就用两张图例来讲解吧:
理想状态情况下的JVM内存使用趋势:
这是一个波浪型的(或者也可以说是锯齿型的)趋势图,随着VU数量的一点点增加,我们的内存使用数会不断的增加,但是JVM的垃圾回收是自动回收机制的,因此如果你的JVM如上述样的趋势,内存上涨一段时间,随即会一点点下落,然后再上涨一点,涨到快到头了又开始下落,直到最后你的VU数量全部下降下来时,你的JVM的内存使用也会一点点的下降。
非理想状态情况下的JVM内存使用趋势:
嘿嘿嘿,看到了吗?
你的JVM随着VU数量的上升,而直线上升,然后到了一定的点后,即到了java–Xmx后的那个值后,突然直线回落,而此时你的交易还在进行,压力测试也还在进行,可是内存突然回落了。
因为你的JVM已经crash了,即OUTOFMEMORY鸟。
CPULoad
我们来看一份测试人员提交上来CPU得用率的报告:
WebServer
AppServer
DBServer
60%
98%
=_=!
(ohmygod)
6%
囧
同时平均响应时间好慢啊。
拿过来看了一下代码与设计。
Struts+Spring+JDBC的一个框架,没啥花头的,再仔细一看Service层。
大量的复杂业务逻辑甚至报表的产生全部用的是javaobject如:
List,Hashmap等操作,甚至还有在Service层进行排序、复杂查询等操作。
一看DB层的CPU利用率才6%,将一部分最复杂的业务拿出去做成StoreProcedure(存储过程后),再重新运行压力测试。
57%
26%
同时平均响应时间比原来快了15-16倍。
为什么?
?
看看第一份报告,我们当时还查看了数据库服务器的配置,和APPServer的配置是一个级别的,而利用率才6%。
数据库,至所以是大型的商用的关系型数据库,你只拿它做一个存储介质,你这不是浪费吗?
人家里面设置的这个StoreProcedure的处理能力,索引效率,数据分块等功能都没有去利用,而用你的代码去实现那么多复杂业务比如说多表关联、嵌套等操作,用必要吗?
那要数据库干什么用呢?
是啊,我承认,原有这样的代码,跨平台能力强一点,可付出的代价是什么呢?
用户在乎你所谓的跨平台的理论还是在乎的是你系统的效率?
一个系统定好了用DB2或者是SQLSERVER,你觉得过一年它会换成ORACLE或者MYSQL吗?
如果1年一换,那你做的系统也只能让用户勉强使用一年,我劝你还是不要去做了。
在中国,有人统计过5年左右会有一次系统的更换,而一些银行、保险、金融行业的系统一旦采用了哪个数据库,除非这个系统彻底出了问题,负责是不会轻意换数据库的,因此不要拿所谓的纯JAVA代码或者说我用的是Hibernate,ejb实现可以跨数据库这套来说事,效率低下的系统可以否定你所做的一切,一切!
三、Apache服务器的优化
上面两节,讲了大量的理论与实际工作中碰到的相关案例,现在就来讲一下在我们第一天和第二天中的ApacheHttpServer+Tomcat这样的架构,怎么来做优化吧。
3.1Linux/UnixLinux系统下Apache并发数的优化
ApacheHttpServer在刚安装完后是没有并发数的控制的,它采用一个默认的值,那么我们的WebServer硬件很好,允许我们撑到1000个并发即VU,而因为我们没有去配置导致我们的WebServer连300个并发都撑不到,你们认为,这是谁的责任?
ApacheHttp服务器采用prefork或者是worker两种并发控制模式。
preforkMPM
使用多个子进程,每个子进程只有一个线程。
每个进程在某个确定的时间只能维持一个连接。
在大多数平台上,PreforkMPM在效率上要比WorkerMPM要高,但是内存使用大得多。
prefork的无线程设计在某些情况下将比worker更有优势:
它可以使用那些没有处理好线程安全的第三方模块,并且对于那些线程调试困难的平台而言,它也更容易调试一些。
workerMPM使用多个子进程,每个子进程有多个线程。
每个线程在某个确定的时间只能维持一个连接。
通常来说,在一个高流量的HTTP服务器上,WorkerMPM是个比较好的选择,因为WorkerMPM的内存使用比PreforkMPM要低得多。
但workerMPM也由不完善的地方,如果一个线程崩溃,整个进程就会连同其所有线程一起"
死掉"
.由于线程共享内存空间,所以一个程序在运行时必须被系统识别为"
每个线程都是安全的"
一般来说我们的ApacheHttpServer都是装在Unix/Linux下的,而且是采用源码编译的方式来安装的,我们能够指定在编译时Apache就采用哪种模式,为了明确我们目前的Apache采用的是哪种模式在工作,我们还可以使用httpd–l命令即在Apache的bin目录下执行httpd–l,来确认我们使用的是哪种模式。
这边,我们使用Apache配置语言中的”IfModule”来自动选择模式的配置。
我们的ApacheHttpServer在配完后一般是没有这样的配置的,是需要你手动的添加如下这样的一块内容的,我们来看,在httpd.conf文件中定位到最后一行LoadModule,敲入回车,加入如下内容:
<
IfModuleprefork.c>
ServerLimit
20000
StartServers
5
MinSpareServers
MaxSpareServers
10
MaxClients
1000
MaxRequestsPerChild0
/IfModule>
上述参数解释:
默认的MaxClient最大是256个线程,如果想设置更大的值,就的加上ServerLimit这个参数。
20000是ServerLimit这个参数的最大值。
如果需要更大,则必须编译apache,此前都是不需要重新编译Apache。
生效前提:
必须放在其他指令的前面
StartServers
指定服务器启动时建立的子进程数量,prefork默认为5。
MinSpareServers
指定空闲子进程的最小数量,默认为5。
如果当前空闲子进程数少于MinSpareServers,那么Apache将以最大每秒一个的速度产生新的子进程。
此参数不要设的太大。
MaxSpareServers
设置空闲子进程的最大数量,默认为10。
如果当前有超过MaxSpareServers数量的空闲子进程,那么父进程将杀死多余的子进程。
如果你将该指令的值设置为比MinSpareServers小,Apache将会自动将其修改成"
MinSpareServers+1"
MaxClients
256
限定同一时间客户端最大接入请求的数量(单个进程并发线程数),默认为256。
任何超过MaxClients限制的请求都将进入等候队列,一旦一个链接被释放,队列中的请求将得到服务。
要增大这个值,你必须同时增大ServerLimit。
MaxRequestsPerChild10000
每个子进程在其生存期内允许伺服的最大请求数量,默认为10000.到达MaxRequestsPerChild的限制后,子进程将会结束。
如果MaxRequestsPerChild为"
0"
,子进程将永远不会结束。
将MaxRequestsPerChild设置成非零值有两个好处:
1.可以防止(偶然的)内存泄漏无限进行,从而耗尽内存。
2.给进程一个有限寿命,从而有助于当服务器负载减轻的时候减少活动进程的数量。
Prefork.c的工作方式:
一个单独的控制进程(父进程)负责产生子进程,这些子进程用于监听请求并作出应答。
Apache总是试图保持一些备用的(spare)或者是空闲的子进程用于迎接即将到来的请求。
这样客户端就不需要在得到服务前等候子进程的产生。
在Unix系统中,父进程通常以root身份运行以便邦定80端口,而Apache产生的子进程通常以一个低特权的用户运行。
User和Group指令用于设置子进程的低特权用户。
运行子进程的用户必须要对它所服务的内容有读取的权限,但是对服务内容之外的其他资源必须拥有尽可能少的权限。
在上述的<
后再加入一个”<
IfModule>
”如下红色加粗(大又粗)内容:
IfModuleworker.c>
50
ThreadLimit
200
5
5000
MinSpareThreads
25
MaxSpareThreads
500
ThreadsPerChild
100
ServerLimit16
服务器允许配置的进程数上限。
这个指令和ThreadLimit结合使用设置了MaxClients最大允许配置的数值。
任何在重启期间对这个指令的改变都将被忽略,但对MaxClients的修改却会生效。
ThreadLimit64
每个子进程可配置的线程数上限。
这个指令设置了每个子进程可配置的线程数ThreadsPerChild上限。
任何在重启期间对这个指令的改变都将被忽略,但对ThreadsPerChild的修改却会生效。
默认值是"
64"
.
StartServers3
服务器启动时建立的子进程数,默认值是"
3"
MinSpareThreads75
最小空闲线程数,默认值是"
75"
这个MPM将基于整个服务器监视空闲线程数。
如果服务器中总的空闲线程数太少,子进程将产生新的空闲线程。
MaxSpareThreads250
设置最大空闲线程数。
250"
如果服务器中总的空闲线程数太多,子进程将杀死多余的空闲线程。
MaxSpareThreads的取值范围是有限制的。
Apache将按照如下限制自动修正你设置的值:
worker要求其大于等于MinSpareThreads加上ThreadsPerChild的和
MaxClients400
允许同时伺服的最大接入请求数量(最大线程数量)。
任何超过MaxClients限制的请求都将进入等候队列。
400"
16(ServerLimit)乘以25(ThreadsPerChild)的结果。
因此要增加MaxClients的时候,你必须同时增加ServerLimit的值。
ThreadsPerChild25
每个子进程建立的常驻的执行线程数。
默认值是25。
子进程在启动时建立这些线程后就不再建立新的线程了。
MaxRequestsPerChild
0
设置每个子进程在其生存期内允许伺服的最大请求数量。
到达MaxRequestsPerChild的限制后,子进程将会结束。
注意
对于KeepAlive链接,只有第一个请求会被计数。
事实上,它改变了每个子进程限制最大链接数量的行为。
Worker.c的工作方式:
每个进程可以拥有的线程数量是固定的。
服务器会根据负载情况增加或减少进程数量。
一个单独的控制进程(父进程)负责子进程的建立。
每个子进程可以建立ThreadsPerChild数量的服务线程和一个监听线程,该监听线程监听接入请求并将其传递给服务线程处理和应答。
Apache总是试图维持一个备用(spare)或是空闲的服务线程池。
这样,客户端无须等待新线程或