上期我们讲到LoaRunner性能测试MPM调优,这期我们讲LoaRunner性能测试MPM相关参数。
MPM相关参数
MPM模块常见的相关参数包括:MaxSpareServers、MinSpareServersServerLimit、StartServersThreadsPerChild、MaxConnectionsPerChildMaxRequestWorkers、ListenBackLogListenCoresBucketsRatio、MaxMemFreeReceiveBufferSize。
1)MaxSpareThreads 表示最大空闲线程数 语法:MaxSpareThreadsnumber 适合的MPM模块:event,workermpm_netware,mpmt_os2 该指令设置空闲子进程的最大数量。所谓空闲子进程是指没有正在处理请求的子进程。如果当前有超过MaxSpareServers数量的空闲子进程那么父进程将杀死多余的子进程。
只有在非常繁忙机器上才需要调整这个参数,此参数设的通常不能设置过大,直到空闲线程的数量小于该数量为止。如果你将该指令的值设置为比MinSpareServers小,Apache将会自动将其修改成“MinSpareServers+1”。 对于worker和event来说,该默认值为MaxSpareThreads250。 对于mpm_netware默认为MaxSpareThreads100。 mpmt_os2的工作与mpm_netware。对于mpmt_os2默认值10。 注意: MaxSpareThreads值的范围受到限制。Apachehttpd将根据以下规则自动更正给定值: mpm_netware该值需要大于MinSpareThreads。 对于worker和event该值必须大于或等于MinSpareThreads和ThreadsPerChild的和。
2)MinSpareServers 表示可用于处理请求峰值的最小空闲线程数 语法:MinSpareThreadsnumber 适合的MPM模块:event,workermpm_netware,mpmt_os2 表示处理请求峰值的最小空闲线程数。不同的MPM处理此指令的方式有所不同。 worker模块和event模块该项默认值为MinSpareThreads75。如果服务器的空闲线程数小于所设置的值,则会创建子线程,直到空闲线程数大于我们所设置的最小空闲线程数。
如果服务器中没有足够的空闲线程,则将创建子进程直到空闲线程的数量大于number为止。如果ListenCoresBucketsRatio启用可能会创建其他进程/线程。mpm_netware模块的默认值为MinSpareThreads10,对于mpmt_os2模块默认值为5。
3)ServerLimit 表示可配置进程数的上限 语法:ServerLimitnumber 适合的模块:event,worker,prefork 对于preforkMPM来说是通过MaxRequestWorkers来配置的因为preforkMPM是一个子进程只生成一个线程。对于worker和eventMPM来说会通过ThreadLimit和MaxRequestWorkers两个参数来配置其最大值。MaxRequestWorkers指令可以在服务器重启过程中修改。
使用这个指令时必须特别注意的是ServerLimit的值不能设置的比实际的使用的值高出太多,如果设置的值过大,则会分配很多我们并不需要使用的内存。如果将ServerLimit和MaxRequestWorkers都设置为高于系统可以处理的值则Apachehttpd可能无法启动,或者系统可能变得不稳定。
注意:一般来说ServerLimit最大的值可以设置到20000这是由服务器限制的,如果需要设置为更大的值那么需要修改mpm源文件中的MAX_SERVER_LIMIT值。 4)StartServers 表示服务器启动时创建的子进程数 语法:StartServersnumber 合适的MPM模块:event,workerprefork,mpmt_os2 StartServers指令用于设置启动时创建的子服务器进程的数量。通常会根据MinSpareThreads、MaxSpareThreadsMinSpareServers、MaxSpareServers来动态调整StartServers的值。 缺省值因MPM而异。worker和event默认为StartServers3prefork默认为5,mpmt_os2默认为2。
5)ThreadsPerChild 表示每个子进程创建的线程数 语法:ThreadsPerChildnumber 适合的模块:event,worker,mpm_winnt 该指令表示设置每个进程创建的线程数,服务器启动时先是创建子进程数,再创建线程,如使用mpm_winnet模块,则只会生成一个子进程那么该指令设置应该需要大于服务器处理的最大负载,如果使用worker模块,会生成多个子进程,则线程总数应大于服务器的负载。
mpm_winnt该指令的缺省值为25。其它的MPM缺省值ThreadsPerChild是64。 ThreadsPerChild设置的值不能超过ThreadLimit的值。如果配置了更高的值,它将在启动时自动减小并记录警告日志信息。 6)MaxConnectionsPerChild 表示每个线程最多可以处理的连接数 语法:MaxConnectionsPerChildnumber 默认:MaxConnectionsPerChild0 模块:event,worker,preforkmpm_winnt,mpm_netware,mpmt_os2 兼容性:可用的ApacheHTTPServer2.3.9和更高版本。老的版本该参数为MaxRequestsPerChild。 MaxConnectionsPerChild这个指令主要是设置单个子进程最多可以处理的连接数。如果子进程伺服的连接数达到这个最大值,那么该子进程就会被杀掉。如果将MaxConnectionsPerChild的值设置为0,那么表示该子进程可以处理无限多和连接数。将MaxConnectionsPerChild为非零的值,可以限制由于内存泄漏导致进程消耗太多内存量的问题。
7)MaxRequestWorkers 描述:同时处理的最大连接数 句法:MaxRequestWorkersnumber 模块:event,worker,prefork MaxRequestWorkers指令主要是用于设置服务器同时处理的最大连接数,如果超过所设置的值,那么就会出现排队的现象。最大排队值是由ListenBacklog指令来设置,在排队过程中,只有当一个请求结束后才会释放出子进程给其它的连接服务使用。 对于这非线程服务的MPM模块(如prefork)MaxRequestWorkers指令将转换为服务器最大的子进程数。即ServerLimit的值。MaxRequestWorkers指令默认值为256。
对于会产生多线程类的MPM模块(如event或worker),MaxRequestWorkers指令将用来限制服务器客户端的连接连接数。混合的MPM,默认的ServerLimit值为16,默认的ThreadsPerChild值为25在这种情况下设置MaxRequestWorkers指令的值必须大于16乘以25的积。 在2.3.13版之前MaxRequestWorkers指令以前的版称之为MaxClients。 8)ListenBacklog 表示挂起连接队列的最大长度,即排队的队列度 语法:ListenBackLogbacklog 默认:ListenBackLog511 适合的模块:event,worker,prefork,mpm_winntmpm_netware,mpmt_os2 ListenBackLog指令用于设置连接数队列长度,默认值为511,一般情况下我们不需要对这个指令进行设置或调整,但如果某些系统受到TCPSYN攻击时,可以适当的增加这个值。
9)ListenCoresBucketsRatio 表示在线CPU核数与监听桶的比率 语法:ListenCoresBucketsRatioratio 默认值:ListenCoresBucketsRatio0(disabled) 适合的模块:event,worker,prefork 这个选项有两个核心内容要搞清楚,一是在线CPU核数;二是监听桶。 首先我们介绍什么是在线CPU核数。kernel使用4个bitmap来保存分别处于4种状态的CPUcore:possible、present、active和online。其中online就是表示在线的CPU核数。在/sys/devices/system/cpu目录下有一个文件online记录着当前所有在线的CPU核数。
linux操作系统在初始化的时候会调用开启smp多核。cpuhotplug可以根据cpu负载的情况,自动开核,做到性能与功耗的平衡。最后空闲的cpu会进入cpuidle状态。cpuhotplug原理如图所示。
要研究监听桶那么就必须先理解TCP连接的过程,以及TCP连接与套接字的关系。TCP连接过程如图所示。
listen函数是用来监听已经通过bind()函数绑定了addr+port的套接字。监听之后,套接字就从CLOSE状态转变为LISTEN状态。这个套接字就可以对外提供TCP连接的窗口。connect()函数则用于向某个已监听的套接字发起连接请求,也就是发起TCP的三次握手过程。
那么TCP连接与套接字有什么关系呢?每个TCP连接不管是客户端还是服务器端都会关联一个套接和该套接字所指向的文件描述符。当服务器接受到ACK消息后,则表示三次握手已经完成,客户端和服务器端的TCP连接已经建立好了。
TCP连接建立好后,这个TCP连接会放在listen()打开的established queue队列中等待accept的消息此时TCP连接关联的套接字是listen套接字和指向文件的描述符。当established queue队列中的TCP被accept()接受后,就会关联accept()所指定的套接字,并分配一个新的文件描述符,也就是说经过accept()后,这个连接和listen套接字已经没有任何关系了。
一般情况下一个addr+port只能被一个套接字绑定,也就说是addr+port不能重用,不同套接字只能绑定在不同的addr+port上。
监听套接字的线程都是抢占式监听,在同一时刻监听套接字上只能有一个监听线程在监听或者说在使用,当这个监听线程接收到请求后,会让出监听的资格,此时其它的监听线程会去抢这个监听权,但同时只能有一个线程抢到监听权。其它工作过程如图所示。
正常情况下addr+port只能被一个套接字绑定,如果将地址和端口重用,那么组合起来就是套接字重用,在现在的linux内核中支持地址重用。socket选项SO_REUSEADDR,支持端口重用的socket选项SO_REUSEPORT。
设置了端口重用选项后,再去绑定套接字相当于一个实例绑定了两个或多个addr+port。对于监听进程/线程来说,每次重用的套接字被称为监听桶(listener bucket),即每个监听套接字都是一个监听桶。
以httpd的worker或event模型为例,假设目前有N个子进程,每个子进程又包含一个监听线程和N个工作线程。其工作过程如图所示。
使用了地址重用和端口重用技术,就相当于同一个addr+port绑定多个套接字。如图一个监听桶下面绑定了三个套接字同时会有三个线程来监听三个套接字,但每个套接字还是与地址未重用和端口未重用一样的逻辑都是抢占式的方式来获取监听权。
地址重用和端口重用带好的好处就是可以减轻监听时互斥锁的争抢,避免“饥饿问题”,提高监控效率,并且可以更好的实现负载均衡,但这个也受限于CPU的核心,如果只是单核的CPU,那么地址重用和端口重用并没有什么优势,因为线程数不够。 现在可以明白ListenCoresBucketsRatio这个选项的含义了,即设置在线CPU核数与监听桶的比例。
10)MaxMemFree 表示在不调用free分析内存的情况下允许分配器保留的最大空闲内存数。 语法:MaxMemFreeKBytes 默认值:MaxMemFree2048 适合的模块:event,worker,preforkmpm_winnt,mpm_netware 在MPM线程中,每个线程都有自己的分配器,该参数表示不调用free()函数进行释放内存时,允许每个分配器保持的最大空闲内存数。如果设置为零时表示该阀值不受限制。 11)ReceiveBufferSize 表示TCP接收的数据时的缓存大小 语法:ReceiveBufferSizebytes 默认值:ReceiveBufferSize0 适合的模块:event,worker,preforkmpm_winnt,mpm_netwarempmt_os2 用于设置TCP接收数据时缓存区的大小如果设置为0则表示以操作系统的这个值为准以上是MPM模块中涉及到的常见的指令设置。