门户网站采用的Apache服务器,搭建在阿里云上,最近总是出现访问慢,甚至无法访问。
重启Apache服务后,才可以正常访问,初步原因是米扑博客的访客增量过多,百度等爬虫抓取压力过大。
统计访客IP和PV数量,发现跟平时差不多,甚至还略低,服务器应该不至于响应这么慢,从而需要针对这个问题进行分析,来解决网站访问过慢。
原因分析
1、在页面访问变慢情况发生时,使用 top 命令查看了服务器的负载情况,发现负载并不高,初步估计不是程序的问题。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 | top - 13:20:06 up 2:22, 3 users , load average: 1.08, 1.43, 1.27 Tasks: 137 total, 1 running, 134 sleeping, 0 stopped, 2 zombie Cpu(s): 47.7%us, 2.0%sy, 0.0%ni, 50.3% id , 0.0%wa, 0.0%hi, 0.0%si, 0.0%st Mem: 1920944k total, 1738316k used, 182628k free , 38040k buffers Swap: 4095984k total, 11892k used, 4084092k free , 264372k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3393 mysql 20 0 1455m 833m 6972 S 4.0 44.4 3:21.95 mysqld 23728 apache 20 0 1139m 59m 26m S 0.0 3.2 0:31.30 httpd 24891 apache 20 0 1140m 57m 23m S 0.0 3.0 0:01.02 httpd 23922 apache 20 0 1139m 57m 24m S 0.0 3.0 0:20.55 httpd 23975 apache 20 0 1141m 56m 23m S 0.0 3.0 0:26.89 httpd 24058 apache 20 0 1138m 55m 23m S 0.0 3.0 0:19.04 httpd 24004 apache 20 0 1140m 55m 21m S 0.0 2.9 0:14.15 httpd 24390 apache 20 0 1139m 54m 21m S 24.6 2.9 0:18.93 httpd 23947 apache 20 0 1138m 53m 21m S 0.0 2.9 0:26.20 httpd 22835 apache 20 0 1138m 53m 21m S 19.6 2.9 0:49.06 httpd 24396 apache 20 0 1133m 47m 21m S 0.0 2.5 0:05.40 httpd 24155 apache 20 0 1130m 47m 23m S 0.0 2.5 0:10.82 httpd 24608 apache 20 0 1129m 44m 21m S 0.0 2.4 0:09.03 httpd
|
2、查看 httpd 进程数量
ps -ef | grep httpd | wc -l
查看结果
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 | # ps -ef | grep httpd | grep -v grep | wc -l 13 # ps -ef | grep httpd | grep -v grep root 25024 1 0 13:20 ? 00:00:00 /usr/sbin/httpd apache 25026 25024 1 13:20 ? 00:00:00 /usr/sbin/httpd apache 25027 25024 14 13:20 ? 00:00:07 /usr/sbin/httpd apache 25028 25024 2 13:20 ? 00:00:01 /usr/sbin/httpd apache 25029 25024 5 13:20 ? 00:00:02 /usr/sbin/httpd apache 25032 25024 7 13:20 ? 00:00:03 /usr/sbin/httpd apache 25034 25024 0 13:20 ? 00:00:00 /usr/sbin/httpd apache 25035 25024 9 13:20 ? 00:00:04 /usr/sbin/httpd apache 25037 25024 4 13:20 ? 00:00:02 /usr/sbin/httpd apache 25038 25024 9 13:20 ? 00:00:04 /usr/sbin/httpd apache 25039 25024 5 13:20 ? 00:00:02 /usr/sbin/httpd apache 25040 25024 0 13:20 ? 00:00:00 /usr/sbin/httpd apache 25051 25024 1 13:20 ? 00:00:00 /usr/sbin/httpd |
上面数据发现,线程数已经达到了 apache 设置的最大值。
由此断定是网站访问人数过多造成了访问过慢。
3、查看了服务器连接数和当前的建立连接数
1)查看连接数
netstat -ant | grep -E ":80|:443"
结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | # netstat -ant | grep -E ":80" | wc -l 95 # netstat -ant | grep -E ":443" | wc -l 19 # netstat -ant | grep -E ":80|:443" | wc -l 93 # netstat -ant | grep -E ":80|:443" | tail tcp 0 0 115.29.237.28:36458 111.13.101.139:80 TIME_WAIT tcp 340 0 115.29.237.28:443 111.206.221.28:57598 ESTABLISHED tcp 0 0 115.29.237.28:36360 111.13.101.139:80 TIME_WAIT tcp 0 0 115.29.237.28:36317 111.13.101.139:80 TIME_WAIT tcp 0 0 115.29.237.28:55044 124.65.195.162:8080 TIME_WAIT tcp 0 0 115.29.237.28:50159 180.149.132.151:80 TIME_WAIT tcp 0 0 115.29.237.28:443 123.125.71.52:21474 ESTABLISHED tcp 0 0 115.29.237.28:36374 111.13.101.139:80 TIME_WAIT tcp 0 0 115.29.237.28:443 183.27.179.242:32514 FIN_WAIT2 tcp 0 0 115.29.237.28:60051 180.149.131.98:80 TIME_WAIT |
2)查看建立连接数
netstat -ant | grep ESTABLISHED | grep -E ":80|:443"
结果如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # netstat -ant | grep ESTABLISHED | grep -E ":80" | wc -l 6 # netstat -ant | grep ESTABLISHED | grep -E ":443" | wc -l 6 # netstat -ant | grep ESTABLISHED | grep -E ":80|:443" | wc -l 13 # netstat -ant | grep ESTABLISHED | grep -E ":80|:443" tcp 0 0 115.29.237.28:443 199.30.25.63:6419 ESTABLISHED tcp 0 0 115.29.237.28:38044 140.205.140.205:80 ESTABLISHED tcp 0 0 115.29.237.28:443 220.181.108.113:11102 ESTABLISHED tcp 0 0 115.29.237.28:443 111.206.221.33:49204 ESTABLISHED tcp 0 0 115.29.237.28:50808 180.149.132.151:80 ESTABLISHED tcp 0 0 115.29.237.28:58839 119.36.92.42:80 ESTABLISHED tcp 0 0 115.29.237.28:53981 36.42.32.216:8080 ESTABLISHED tcp 0 0 115.29.237.28:443 95.108.181.81:43824 ESTABLISHED tcp 0 0 115.29.237.28:80 173.212.254.158:55468 ESTABLISHED tcp 0 0 115.29.237.28:443 199.30.24.54:40307 ESTABLISHED tcp 0 0 115.29.237.28:443 173.212.254.158:46540 ESTABLISHED tcp 0 0 115.29.237.28:52123 179.176.118.31:8080 ESTABLISHED |
发现连接数特别多,远远超过了米扑博客设置的Apache服务器允许的估计值。
4、熟悉服务器的 MPM 配置
刚开始的时候,对于Apache服务器的 MPM 配置方式不是特别的熟悉,认为修改服务器配置可以解决问题。
MPM是Apache的核心,它的作用是管理网络连接、调度请求。
Apache2.0中MPM分为3种:perfork、worker、event
perfork 从Apache1.3中继承下来的,它采用的是进程管理方式,所以它可以提供更可靠的性能和更好的兼容性;
worker 是Apache2.0中新增加的方式,它采用了线程控制方法,可以比perfork更节 约系统开销、处理更多的数据量,但同时兼容性并不是很好,很多旧的程序无法工作在worker下;
event仍处于试验阶段,它为每个任务分配不同的进程池,目前不应该采用。
通过命令 httpd -l 可以获取目前Apache采用的是哪种 MPM
本文主要介绍的Apache配置,包括 prefork 或者 work 模式的配置
1) prefork 模式
以 prefork 模式工作的 apache 的默认配置:
1 2 3 4 5 6 7 8 | <IfModule prefork.c> ServerLimit 256 StartServers 5 #指定服务器启动时建立的子进程数量 MinSpareServers 10 #指定空闲子进程的最小数量 MaxSpareServers 20 #指定空闲子进程的最大数量 MaxClients 150 #客户端最大接入请求的数量(单个进程并发线程数),任何超过该限制的请求都将进入等候队列 MaxRequestsPerChild 0 #指定每个子进程在其生存周期内允许伺服的最大请求数量,默认为10000,0表示子进程永远不结束 < /IfModule > |
当Apache被启动时,自动会采用 prefork 控制进程在最初建立 StartServers 个子进程后,为了满足 MinSpareServers 设置的需要创建一个进程,等待一秒钟,继续创建两个,再等待一秒钟,继续创建四个……如此按指数级增加创建的进程数,最多达到每秒32个,直到满足 MinSpareServers 设置的值为止。这种模式可以不必在请求到来时再产生新的进程,从而减小了系统开销以增加性能。
MaxSpareServers 设置了最大的空闲进程数,如果空闲进程数大于这个值,Apache会自动kill掉一些多余进程。这个值不要设得过大,但如果设的值比 MinSpareServers小,Apache会自动把其调整为 MinSpareServers+1。
如果站点负载较大,可考虑同时加大MinSpareServers和MaxSpareServers
MaxClients 是这些指令中最为重要的一个,设定的是 Apache可以同时处理的请求,是对Apache性能影响最大的参数。
MaxClients 缺省值150,一般是远远不够的,如果请求总数已达到这个值(可通过 ps -ef | grep httpd | wc -l 来查看),那么后面的请求就要排队,直到某个已处理请求完毕。这就是系统资源还剩下很多而HTTP访问却很慢的主要原因。
虽然理论上 MaxClients 这个值越大,可以处理的请求就越多,但Apache默认的限制不能大于256
在 apache2 中通过ServerLimit指令无须重编译Apache就可以加大MaxClients,此时必须 MaxClients ≤ ServerLimit ≤ 20000
MaxRequestsPerChild用来控制每个进程在处理了多少次请求之后自动销毁,这个参数可以设置为0表示无限,即不销毁进程
而且 ServerLimit 必须大于等于 MaxClients 数量,否则报错
虽然通过设置ServerLimit,可以把MaxClients加得很大,但是往往会适得其反,系统耗光所有内存。以一台服务器为例:内存2G,每个apache进程消耗大约3%(可通过ps aux来确认,如上面的top命令结果)的内存,也就是60M,这样,理论上这台服务器最多跑30个apache进程就会耗光系统所有内存,所以,设置MaxClients要慎重。
使用场景:
周末晚上,在访问量高峰期,经常会出现突然之间发生非常多的并发连接(ps -ef | grep httpd | wc -l),然后突然之间减少了很多访问。如果Apache没有准备足够数量的预备进 程,那访问只能等待Apache每秒1个的新增进程,随后又要将多余的进程删除,那Apache只能一直忙于新建和销毁进程,大大地降低了访问速度。可以 适当的提前增加 StartServers、MinSpareServers、MaxSpareServers 来使得Apache不需要一直忙于作无用功。
最后,推荐MaxRequestsPerChild不要设置为0,强烈推荐设置为非0,可以保护Apache进程免遭内存泄漏的影响,因为你不知道运行在Apache上的应用程式在什么时候会出错导致内存泄漏。
优化设置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | <IfModule prefork.c> #StartServers 4 #MinSpareServers 4 #MaxSpareServers 20 #ServerLimit 128 #MaxClients 100 #MaxRequestsPerChild 2000 StartServers 10 MinSpareServers 10 MaxSpareServers 30 ServerLimit 1280 MaxClients 1000 MaxRequestsPerChild 5000 < /IfModule > |
2)worker 模式
以 worker 模式工作的 apache 的默认配置为:
1 2 3 4 5 6 7 8 | <IfModule worker.c> StartServers 2 MaxClients 150 MinSpareThreads 25 MaxSpareThreads 75 ThreadsPerChild 25 MaxRequestsPerChild 0 < /IfModule > |
Worker 由主控制进程生成“StartServers”个子进程,每个子进程中包含固定的ThreadsPerChild线程数,各个线程独立地处理请求。同样,为了不在请求到来时再生成线程,
MinSpareThreads 和 MaxSpareThreads 设置了最少和最多的空闲线程数;
MaxClients 设置了同时连入的clients最大总数。如果现有子进程中的线程总数不能满足负载,控制进程将派生新的子进程。
MinSpareThreads 和 MaxSpareThreads的最大缺省值分别是75和250,这两个参数对Apache的性能影响并不大,可以按照实际情况相应调节。
ThreadsPerChild 是worker MPM中与性能相关最密切的指令。
ThreadsPerChild的最大缺省值是64,如果负载较大,64也是不够的。这时要显式使用 ThreadLimit指令,它的最大缺省值是20000
Worker 模式下所能同时处理的请求总数是由子进程总数乘以ThreadsPerChild 值决定的,应该大于等于MaxClients
处理请求总数 = 子进程总数 * ThreadsPerChild >= MaxClients
如果负载很大,现有的子进程数不能满足时,控制进程会派生新的子进程。默认最大的子进程总数是16,加大时也需要显式声明ServerLimit(最大值是20000)。
需要注意的是,如果显式声明了ServerLimit,那么它乘以 ThreadsPerChild的值必须大于等于MaxClients,而且MaxClients必须是ThreadsPerChild的整数倍,否则 Apache将会自动调节到一个相应值。
3)itk.c 模式
仔细查看分析httpd.conf配置文件后,发现还有一种 itk 模式
直接给出其默认配置,供参考
1 2 3 4 5 6 7 8 | <IfModule itk.c> StartServers 4 MinSpareServers 4 MaxSpareServers 4 ServerLimit 128 MaxClients 64 MaxRequestsPerChild 400 < /IfModule > |
服务器的apache采用的是 prefork 的工作模式
对 MaxClients 进行了相应的调整,发现服务启动后很短时间,连接数就能够达到最大。
5、查看用户访问日志
查看用户访问日志和详情页面,将配置中的 access_log 打开,发现85%以上的访问都是直接访问的资源文件,
由此判定,用户可能使用了多线程的下载工具,或者这些资源遭受了盗链(可能性更大)。
优化方案
1. 限制单个IP进行连接的线程,不允许多线程连接资源
对于IP限制,采用了 mod_limitipconn 这个模块。
这个模块的优点是配置简单,缺点是不能够针对单独的文件夹或者文件进行设置,而且不支持虚拟主机。
在 apache 中安装了这个模块后,在配置文件中添加如下几段就可以生效了:
1 2 3 4 5 6 7 8 9 10 11 | ExtendedStatus On < IfModule mod_limitipconn.c > < Location / > # 所有虚拟主机的/目录 MaxConnPerIP 3 # 每IP只允许3个并发连接 NoIPLimit image/* # 对图片不做IP限制 < /Location > < Location /mp3 > # 所有主机的/mp3目录 MaxConnPerIP 1 # 每IP只允许一个连接请求 OnlyIPLimit audio /mpeg video # 该限制只对视频和音频格式的文件 < /Location > < /IfModule > |
2. 添加URL重写,防止盗链
防止盗链,一个重要的方法就是判断请求的 refer
但是如果一些浏览器发出请求的时候将 refer 去掉,或者伪装,这个办法就无能为力了。
但是貌似还有更高级的方法,还是可以实现这个功能。
安装apache的 mod_rewrite 模块后,在apache配置文件中添加
1 2 3 4 5 6 | RewriteEngine On RewriteCond %{HTTP_REFERER} !^https: //blog .mimvp.com/.*$ [NC] RewriteCond %{HTTP_REFERER} !^https: //blog .mimvp.com$ [NC] RewriteCond %{HTTP_REFERER} !^https: //blog .mimvp.com/.*$ [NC] RewriteCond %{HTTP_REFERER} !^https: //blog .mimvp.com$ [NC] RewriteRule .*\.(gif|jpg|swf)$ https: //blog .mimvp.com /404 .png [R,NC] |
这样盗链的请求会被重定向到一个错误页面,从而减少下载带给服务器的压力。
3. 网站打不开,httpd无法启动,提示错误“No space left on device: Cannot create SSLMutex”
错误信息如下:
1 2 3 4 5 6 7 8 9 10 11 | [Sat Dec 16 06:12:32 2017] [error] server reached MaxClients setting, consider raising the MaxClients setting [Sat Dec 16 06:20:38 2017] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec ) [Sat Dec 16 06:20:38 2017] [warn] Init: Name-based SSL virtual hosts only work for clients with TLS server name indication support (RFC 4366) [Sat Dec 16 06:20:38 2017] [notice] Digest: generating secret for digest authentication ... [Sat Dec 16 06:20:38 2017] [notice] Digest: done [Sat Dec 16 06:20:39 2017] [warn] Init: Name-based SSL virtual hosts only work for clients with TLS server name indication support (RFC 4366) [Sat Dec 16 06:20:39 2017] [notice] Apache /2 .2.27 (Unix) DAV /2 mod_ssl /2 .2.27 OpenSSL /1 .0.1e-fips SVN /1 .6.11 mod_perl /2 .0.4 Perl /v5 .10.1 configured -- resuming normal operations [Sat Dec 16 06:21:07 2017] [error] server reached MaxClients setting, consider raising the MaxClients setting [Sat Dec 16 06:30:06 2017] [notice] suEXEC mechanism enabled (wrapper: /usr/sbin/suexec ) [Sat Dec 16 06:30:06 2017] [error] (28)No space left on device: Cannot create SSLMutex Configuration Failed |
根据错误提示第一行“[error] server reached MaxClients setting, consider raising the MaxClients setting”,认为是MaxClients数量太少
查看 apache 工作模式:
# httpd -l
Compiled in modules:
core.c
prefork.c
http_core.c
mod_so.c
工作模式为 prefork.c ,于是进一步查看 httpd.conf 的 prefork.c 配置
vim httpd.conf
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <IfModule prefork.c> #StartServers 4 #MinSpareServers 4 #MaxSpareServers 20 #ServerLimit 128 #MaxClients 100 #MaxRequestsPerChild 2000 StartServers 10 MinSpareServers 10 MaxSpareServers 20 ServerLimit 64 MaxClients 64 MaxRequestsPerChild 3000 < / IfModule> |
因为服务器配置不高、日访客并发不大,于是 MaxClients 配置数较小,是合理的。
进一步分析,关注到错误提示“No space left on device: Cannot create SSLMutex”,这一句的含义是没有剩余资源创建 SSLMutex 共享变量
联想到了Linux无法创建句柄,并会无法提供服务,于是查看共享变量资源的占用情况
ipcs : ipcs provides information on the ipc facilities for which the calling process has read access.
1 2 3 4 5 6 7 8 9 10 11 12 | # ipcs -s ------ Semaphore Arrays -------- key semid owner perms nsems 0x00000000 0 root 600 1 0x00000000 32769 root 600 1 0x00000000 163842 apache 600 1 0x00000000 196611 apache 600 1 0x00000000 229380 apache 600 1 0x00000000 262149 apache 600 1 0x00000000 294918 apache 600 1 ....... |
发现共享变量有数百个被apache进程占用,无法释放,资源耗尽了。
于是,需要删除占尽的共享变量信号资源,删除命令如下:
1 | ipcs -s | perl -ane '/^0x00000000/ && `ipcrm -s $F[1]`' |
重新启动Apache httpd 服务器:
/etc/init.d/httpd restart
httpd无法重启的问题解决!
查看正常情况下的共享变量信号:ipcs -s
1 2 3 4 5 6 7 | # ipcs -s ------ Semaphore Arrays -------- key semid owner perms nsems 0x00000000 4587520 apache 600 1 0x00000000 4620289 apache 600 1 0x00000000 4653058 apache 600 1 |
查看 ipcs 的限制参数:ipcs -l
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 | # ipcs -l ------ Shared Memory Limits -------- max number of segments = 4096 max seg size (kbytes) = 67108864 max total shared memory (kbytes) = 17179869184 min seg size (bytes) = 1 ------ Semaphore Limits -------- max number of arrays = 128 max semaphores per array = 250 max semaphores system wide = 32000 max ops per semop call = 32 semaphore max value = 32767 ------ Messages: Limits -------- max queues system wide = 3751 max size of message (bytes) = 65536 default max size of queue (bytes) = 65536 |
显示发现,Semaphore Limits 最大为128,超过了这个数量,httpd 服务将会无法再启动,也就导致了上面的网站打不开。
总结
通过优化 prefork 模式,启用限制IP和防盗链,米扑博客的访问速度提升非常明显
而且,CPU、内存、负载、MySQl等指标不仅没有上升,反而大幅下降了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | top - 15:05:06 up 4:07, 3 users , load average: 0.73, 0.67, 1.44 Tasks: 150 total, 1 running, 147 sleeping, 0 stopped, 2 zombie Cpu(s): 34.2%us, 1.7%sy, 0.0%ni, 63.8% id , 0.0%wa, 0.0%hi, 0.3%si, 0.0%st Mem: 1920944k total, 1422288k used, 498656k free , 37156k buffers Swap: 4095984k total, 786528k used, 3309456k free , 257536k cached PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 3393 mysql 20 0 1506m 222m 4620 S 2.0 11.9 7:58.70 mysqld 1733 apache 20 0 1146m 66m 28m S 0.0 3.5 0:09.77 httpd 1753 apache 20 0 1147m 62m 23m S 0.0 3.3 0:05.92 httpd 1735 apache 20 0 1145m 62m 23m S 0.0 3.3 0:07.30 httpd 1937 apache 20 0 1145m 60m 21m S 0.0 3.2 0:06.85 httpd 2196 apache 20 0 1144m 58m 21m S 0.0 3.1 0:02.53 httpd 1737 apache 20 0 1140m 56m 24m S 0.0 3.0 0:09.53 httpd 1942 apache 20 0 1137m 54m 23m S 0.0 2.9 0:03.96 httpd 1734 apache 20 0 1138m 54m 22m S 0.0 2.9 0:05.24 httpd 1932 apache 20 0 1138m 53m 21m S 0.0 2.9 0:04.42 httpd 2200 apache 20 0 1139m 53m 21m S 0.0 2.9 0:02.10 httpd |
客户请求连接数
1 2 3 4 5 6 7 8 9 10 11 12 13 | # ps -ef | grep httpd | wc -l 32 # netstat -ant | grep -E ":80|:443" | wc -l 37 # netstat -ant | grep ESTABLISHED | grep -E ":80|:443" | wc -l 6 # netstat -ant | grep ESTABLISHED | grep -E ":80|:443" tcp 0 0 115.29.237.28:38044 140.205.140.205:80 ESTABLISHED tcp 0 0 115.29.237.28:80 203.208.60.200:41095 ESTABLISHED tcp 0 0 115.29.237.28:443 1.203.144.157:53865 ESTABLISHED tcp 0 0 115.29.237.28:80 52.39.115.165:56716 ESTABLISHED tcp 0 0 115.29.237.28:80 113.13.100.150:9134 ESTABLISHED tcp 0 0 115.29.237.28:443 113.13.100.150:8138 ESTABLISHED |