首先需要说的是,要想搞明白这些问题,你需要相当多的基础知识.
从C,socket到linux kernel, apache源码都需要.
----------------------
一个 toy http server 大概是这个样子的:
socket()
bind()
listen()
while (conn = accept()) {
fork();
// child process request // parent wait child exit}
apache prefork 是这个样子的:
socket()
bind()
listen()
// create proc mutex, scoreboard// fork children, parent wait child exit, create new child// child-1 | child-2while (connection_count < MaxRequestsPerChild) { | while (connection_count < MaxRequestsPerChild) {
mutex_lock() | mutex_lock()
conn = accept() | conn = accept()
mutex_unlock(); | mutex_unlock();
create_connection(); | create_connection();
set conn vhost lookup data(default_list->names) | set conn vhost lookup data(default_list->names)
if lookup failed, base_server = "a.example.com" | if lookup failed, base_server = "a.example.com"
do { | do {
read_request(); | read_request();
process_request(); | process_request();
} while (keepalive); | } while (keepalive);
connection_count++; | connection_count++;
} | }
child_exit(); | child_exit();
简单解释一下:
apache有一个全局的mutex lock, 哪个子进程占有了这个lock,下个tcp连接就由它来accept.
accept成功后,就开始处理http请求了,如果客户端要求keep alive,那么处理完一个http请求后,等着客户端的下一个http请求.当然对同一个客户端处理的keep alive请求数和时间都是有限制的,要不然一个客户端长时间占有一个子进程,apache很快就不能响应其它客户端的请求了.
这个tcp连接断开后,子进程接着去处理下个tcp连接,当处理的tcp连接数达到MaxRequestsPerChild限制后,这个子进程就退出了.
在apache源码里,子进程叫child server.
一个网站能同时承载的最大用户数和并发用户数不是一个概念.
不过显然最大用户数要远大于并发用户数.
这里说下并发用户数.
sudo ./httpd -k start
mpm_prefork_module:
StartServers 5 # ps aux | grep httpd 可验证, 1个root的process(parent)和5个daemon的process(child)
MinSpareServers 5
MaxSpareServers 10
ServerLimit 256 (最大能调整到20W)
MaxClients 256 (默认等于ServerLimit)
MaxRequestsPerChild 10000
httpd默认启动5个child server,每一个tcp connection会占用一个child server,当前最多启动256个child server.
每个server处理过10000个tcp connection后,会exit. 注意是10000个tcp connection,不是http请求.
访问一个页面,浏览器可能会open多个tcp connection,而不是1个.
所以,为了支持N个并发连接,必须有N个child server.
为了支持N个并发用户,必须有 N * 浏览器open的tcp connection数(@see http://www.browserscope.org/?category=network, 大多数是6).
由于大多数浏览器都会keep alive,所以一旦child server accept了conn,这个conn就会占用这个server几秒时间.
其它需要考虑的就是kernel调度这么多child server执行消耗cpu,内存,io的问题了.
httpd支持的并发用户数 = MaxClients / 6 = 256 / 6 = 43
即使httpd处理http请求非常快,由于keepalive的原因,在几秒内child server并不能处理其它请求
验证: 写个php脚本,fork出300个child,每个child发一个http请求并keep alive. 前256个应该很快就返回了,后边的要等上几秒才能拿到结果.
httpd.conf 调整 MaxSpareServers 256
执行几次 php test-keepalive.php 让child server都启动起来
然后再执行一次 php test-keepalive.php, 观察结果: 有256个php client child 5秒结束,其它的10秒结束.
httpd默认KeepAliveTimeout=5,MaxKeepAliveRequests=100.
具体意思是: tcp connection建立后,可以每隔5秒发一个请求,最多发100个.
run test-keepalive-timeout-max.php