一 序言
-------
我想linux爱好者没有不知道Apache的. 作为一个linux的管理员更应该精通Apache的配制. 在这里我就我的理解描述一下Apache. 作为一个被广泛使用的Web服务器, Apache将reliable摆在了第一位, performance只是第二位的东东. 这一理念我觉得很好. 当然, 它最好的地方是开放源码, 这才使我们有机会深入了解世界上最流行的Web Server.
二 基本结构介绍
---------------
Apache是由模块组成, 其中http_core.c是最根本的, 一个最小的Apache编译只包含这一个模块. 你可以用"httpd -l" 可以列出Apache被编译进了哪些模块. 动态模块是需要在配制文件中定义的. 在Redhat中, 只有"http_core.c"被编译进去了, 其余的都被编译成了so. 可以被Apache动态加载. Redhat中的Apache是比较特殊的, 标准的Apache只有一个配制文件httpd.conf, 其他的文件都是空的.
下面是在配制文件中装载模块的命令:
LoadModule digest_module modules/mod_digest.so
LoadModule proxy_module modules/libproxy.so
LoadModule php3_module modules/libphp3.so
... ...
ClearModuleList
AddModule mod_actions.c
AddModule mod_userdir.c
AddModule mod_alias.c
... ...
LoadModule是用于动态加载模块的. ClearModuleList是删除Apache中的模块列表. AddModule是将模块加入到列表中去. 上面命令是用于重新构造模块列表, 模块在列表中的顺序代表了其处理时的优先级, 后面的优先级高.
Apache将一个Cl.net的请求按以下步骤处理:
1. URL -> Filename translation
2. Auth ID checking
3. Auth access checking
4. Access checking other than auth
5. Determining MIME type of the object requested
6. Fixups
7. Actually sending a response back to the client
8. Logging the request
Apache本身只负责内存分配, IO, 进程管理和模块管理等, 它通过调用模块提供的接口函数完成配制命令的处理和用户请求的实际处理. 其中的核心模块http_core.c提供了最基本的命令, 每一个模块通常都会有自己的配制命令. 模块可以参与上述的八个步骤. 每一个模块提供一个struct module, 其中定义了大量的函数指针和结构指针, 通过struct module告诉Apache该模块所支持的命令和参与的步骤.
struct module中定义了改模块的名字, 这是通过宏来完成的, 其名字就是__FILE__. 所以在文件mod_foo.c中定义的模块名字就叫mod_foo.c.
Apache内部维护了一个链表(头指针为top_module). 最初时只有一个
http_core.c. 每一条命令LoadModule, AddModule. 都会在表头加一项. 而在每一步, Apache都会按照链表中的顺序来依次调用每个模块提供的函数. 而在许多步, Apache碰到第一个返回OK的即结束. 因此, 表中的顺序( 这也就是所谓的优先级了 )可以对系统有很大影响.
下面看一下上面说过的八个步骤:
1. URL -> Filename translation
将URL转换为本地的文件名, 例如: mod_alias.c会在这一步处理alais
2. Auth ID checking
3. Auth access checking
4. Access checking other than auth
进行认证处理
5. Determining MIME type of the object requested
决定被请求对象的MIME类型
6. Fixups
不知道(谁知道请告诉我)
7. Actually sending a response back to the client
将应答发送给客户
8. Logging the request
写log
认证的部分会在后面讲. 我们看一看最后几步. Apache继承了通过扩展名来判断MIME类型的方法. AddType用来加入新的一些扩展名:
AddType application/x-httpd-php3 .php3
AddType application/x-tar .tgz
这两条指令定义了后缀为php3和tgz的文件的MIME类型. 这里用的都是标准的MIME类型, 所谓的决定MIME类型其实还包括了另一种情况: 模块会用一些字符串来描述一个请求, Apache根据这一字符串来选择一个相应的模块来处理该请求. 这些字符串都是内部自定义的. 细节会在下面描述.
AddHandler cgi-script .cgi
AddHandler server-parsed .shtml
Sethandler cgi-script
AddHandler定义了何种扩展名用那一个字符串进行描述.
SetHandler将一个目录下的文件都指定用这一个字符串描述.
我在这里提到的命令都是与其结构密切相关的. Handler和Type的关系在下面会描述的. 许多的东东从外面是看不清楚的, 下面, 我们从里面看.
三 程序的基本结构
-----------------
Apache有非常好的跨平台性. 为了实现这一目标和简化模块编写者的负担,Apache完成了许多基本的功能如IO, 内存分配等, 这些接口都是与具体平台无关的. 还有一些很有用的例程如: hash table, array 等. 在整个体系中, Apache有一个基本点, 它尽可能的使用简单的结构和算法, 这不仅易于理解和维护, 还提高了它的稳定性.
在UNIX系统上, Apache采用了多进程模型, 在Window上采用了多线程模型.多进程模型中, 其子进程处理客户请求, 父进程用于管理子进程. 当系统过载时父进程会再启动几个子进程, 当系统空闲时, 父进程会杀掉几个子进程. 子进程的数目在"MinSpareServers"和"MaxSpareServers"之间. 而且, 每个子进程处理的请求个数也是有限制的, 这可以解决诸如内存泄漏等问题. 所有的进程状态都被记录在share memory中. 由于每个进程的状态记录在其中的一小块内存上, 它通常也只读写这一块内存, 因此, Apache没有使用什么同步机制.
在Richard Steve的书上说到的几种多进程服务器模型, Apache都使用了,在不同的系统上根据其特点选择使用不同的方法:
1. accept :
在accept处阻塞, 只有在accept是在内核级实现的才行.
2. select :
在select处阻塞.
3. mutex/lock_file :
使用mutex或lock_file来进行对accpet进行互斥.
三种方法都要求进行阻塞, 区别在于阻塞与不同的地方. 前两种方法都会由所谓的巨群问题: 多个阻塞在同一个资源上的进程被同时唤醒引发再次竞争. 不过, 按Richard Steve 的评测, 第一种方法最快, 第二种其次, 第三种最慢. 其实, 在linux上第三种方法也会有巨群问题.
Apache虽然并不强调性能, 这并不意味着他们不重视性能. 而是Apache认为在Server端realiable才是第一位的. 但Apache的性能还是不错的.
来源:Bricks with GNU&LINU