门户网站采用的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
|
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
|
#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
|
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
|
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
|
#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
|