Apache HTTP Server应用的几个场景
前言
尽管Apache具有重量级、耗资源、低性能(相比其它的WebServer)的特点,但是同时它也具有兼容性强、稳定性高、模块丰富等特点,且处理动态请求比大多Web Server要优越。另外,它对Windows的支持要比Nginx与Lighttpd要好。
在选择Web服务器时,我们最经常关注的一点是性能(注1),以下附上一个简单的性能测试结果:
测试服务器的配置:
Virtualization: OpenVZ RAM: 512MB CPU: 4 cores @ 2.8Ghz OS: CentOS 5
分别安装了Nginx、Cherokee、Lighttpd和Apache,然后使用并发工具测试,得到如下的结果:
(图片顺序为Nginx、Cherokee、Lighttpd、Apache1、Apache2)
结果显示Apache的性能并是最好的,但即使如此,由于它对windows的支持以及丰富的模块,还有它于Web领域具有霸主地位。所以我们会经常接触它,本文就总结自己实际应用到的一些场景。
Apache安装
下载地址http://httpd.apache.org/download.cgi
值得注意的是2.0.64以后的版本,官网中只提供源码和netware压缩包,而之前的版本提供msi文件。由于目标环境为windows,为了简单起见,本文选择2.0.64。另,因为安装都是“next”,所以不再截图。
Web服务器
一个典型的配置如下
Alias /url "you/local/path"
<Directory "you/local/path">
Options Indexes FollowSymLinks –ExecCGI
DirectoryIndex index.html home/index.php home/index.html
Order allow,deny
Allow from all
Deny from google.com
</Directory>
说明如下
Alias /url "you/local/path":资源访问的路径。
Directory "you/local/path":暴露的本地资源之路径。
Options Indexes FollowSymLinks –ExecCGI:允许列出目录的内容;允许目录的符号链;不允许执行CGI脚本。
DirectoryIndex index.php index.html home/index.php home/index.html:默认首页
Order allow,deny:后者优先级大于前者,这一点在后面解释。
Allow from all:允许来自任何地方的HTTP访问请求。
Deny from google.com :禁止域名为google.com的HTTP访问请求。 还记得前面的Order吗?Deny优先级优于Allow,故Allow指令虽然声明允许所有的访问请求,但是apache仍然禁止来自google.com的请求。
反向代理负载均衡服务器
反向代理是指,以代理服务器来接受来自Internet上某个客户端的连接请求,然后将该请求转发至内部网络上的目标服务器,并将从目标服务器上得到的结果返回给客户端的过程。
反向代理示意图
反向代理的使用场景主要以下几个:
1. 加密和SSL加速
2. 负载均衡
3. 缓存静态内容
4. 压缩页面
5. 减速上传
6. 安全
7. 外网发布
基于反向代理的负载均衡常用的场景为实现动静态分离。
使用Apache+Tomcat+缓存服务器来实现动静态分离的方式有三种:JK、http_proxy、ajp_proxy。其中,JK相对后两者来说配置相对复杂,但是性能较好。JK方式是我们常用的方式。
下面的就是JK一个典型的例子。
JK方式需要额外下载mod_jk模块,地址为
http://www.apache.org/dist/tomcat/tomcat-connectors/jk/binaries/windows/。下载完成后将其中的mod_jk.so文件拷贝至%APACHE_HOME%/modules目录下。
JK的配置最关键的有三个文件:
文件名 |
说明 |
http.conf | 前面提到的,Apache服务器的配置文件,这里用来加载JK模块以及JK配置文件信息。 |
workers.properties | 到Tomcat服务器的链接定义文件。 |
uriworkermap.properties | URI映射文件,用来指定哪些URI由Tomcat处理,也可以直接在httpd.conf中配置,但是将这些配置文件单独放置有一个好处就是JK模块会定期更新该文件的内容,使得我们修改配置的时候无需重启Apache服务器。 |
下面是一个典型的httpd.conf对JK的配置
# (httpd.conf) # 加载 mod_jk 模块 LoadModule jk_module modules/mod_jk.so JkWorkersFile conf/workers.properties JkMountFile conf/uriworkermap.properties JkLogFile logs/mod_jk.log JkLogLevel warn |
接下来需要在conf目录创建两个文件分别是workers.properties、uriworkermap.properties。
内容大概如下
# # workers.properties # # list the workers by name worker.list=DLOG4J, status # localhost server 1 # ------------------------ worker.s1.port=8009 worker.s1.host=localhost worker.s1.type=ajp13 # localhost server 2 # ------------------------ worker.s2.port=8109 worker.s2.host=localhost worker.s2.type=ajp13 worker.s2.stopped=1 worker.DLOG4J.type=lb worker.retries=3 worker.DLOG4J.balanced_workers=s1 worker.DLOG4J.sticky_session=1 worker.status.type=status |
这里我们配置了两个类型为 ajp13 的 worker 分别是 s1 和 s2,它们指向同一台服务器上运行在两个不同端口 8009 和 8109 的 Tomcat。接着我们配置了一个类型为 lb(也就是负载均衡的意思)的 worker,它的名字是 DLOG4J。需要说明的是lb是逻辑的 worker,职责是管理前面配置的两个物理连接 s1 和 s2。最后我们还配置了一个类型为 status 的 worker,用来监控 JK 本身。但是有了这三个 worker 还不够,我们还需要告诉 JK,哪些 worker 是可用的,所以就有 worker.list = DLOG4J, status 这行配置。
/*=DLOG4J /jkstatus=status !/*.gif=DLOG4J !/*.jpg=DLOG4J !/*.png=DLOG4J !/*.css=DLOG4J !/*.js=DLOG4J !/*.htm=DLOG4J !/*.html=DLOG4J |
配置说明所有的请求都由 DLOG4J 这个 worker 进行处理,除了下面的几个例外。/jkstatus 请求由 status 这个 worker 处理。另外感叹号开头且符合规则的URI 不要由 JK 进行,包含图片、css 文件、js 文件以及静态 html 文本文件。
缓存服务器
这里只考虑Apache自带的cache模块,关于与Squid或者varnish等整合另外介绍。
httpd.conf配置如下:
LoadModule cache_module modules/mod_cache.so LoadModule disk_cache_module modules/mod_disk_cache.so UseCanonicalName On <IfModule mod_cache.c> CacheDefaultExpire 3600 CacheMaxExpire 86400 <IfModule mod_disk_cache.c> CacheEnable disk / CacheRoot d:/cache CacheDirLevels 2 CacheDirLength 1 CacheMaxFileSize 1048576 CacheMinFileSize 10 </IfModule> </IfModule> |
配置说明
UseCanonicalName:指令设置为 On 可以使用disk缓存时显著提高缓存的命中率。
CacheDefaultExpire:设置那些既没有包含"Expires" 或"Cache-Control"头,也没有包含"Last-Modified"头的缓存对象的默认有效期(按秒计),默认是为86400秒。
CacheMaxExpire: 指定失效周期的最大值;默认值是一天 (86400)。
CacheEnable disk / 使用disk 缓存类型 ,这里为缓存目录
CacheRoot d:/cache 存放缓存文件的目录,需保证运行的用户拥有该目录的写入权限。
CacheDirLevels指定了子目录的层数,
CacheDirLength指定了每级子目录名的字符数
CacheMaxFileSize 缓存文件的最大值(byte)
CacheMaxFileSize:缓存文件的最小值(byte)。
日志服务
(注2)一个典型的例子:
[Wed Oct 11 14:32:52 2000] [error] [client 127.0.0.1] client denied by server configuration: /export/home/live/ap/htdocs/test
第一项是事件发生的日期和时间;第二项是事件的严重性, LogLevel指令使只有高于指定严重性级别的事件才会被记录;第三项是产生事件的IP地址;此后是事件本身,在此例中,服务器拒绝了这个客户的访问。服务器在记录被访问文件时,用的是文件系统路径,而不是Web路径。
本文仅介绍日志服务使用的模块和常用的日志格式。
1. 访问日志
相关模块 |
相关指令 |
· SetEnvIf |
访问日志中会记录服务器所处理的所有请求,其文件名和位置取决于 CustomLog 指令,LogFormat指令可以简化日志的内容。这里阐述访问日志的服务器配置。
不同版本的Apache httpd用了不同的模块和指令来控制对访问的记录,包括mod_log_referer, mod_log_agent, 模块和 TransferLog 指令。现在,CustomLog 指令包含了旧版本中相关指令的所有功能。
2. 日志格式
这是一个典型的记录格式
LogFormat "%h %l %u %t \"%r\" %>s %b" common
CustomLog logs/access_log common
上述定义了一种特定的日志格式字符串,并给它起了个 别名 叫 common,其中的"%"指示服务器用某种信息替换,其他字符信息则不作替换。引号(")必须加转义符反斜杠,以避免被解释为字符串的结束。格式字符串还可以包含特殊控制符,如换行"\n"、制表符"\t"。
CustomLog指令建立新的使用指定日志格式的日志文件,除非其文件名以斜杠开头,否则其路径是一个相对于ServerRoot的相对路径。
上述配置是一个普通的日志格式,被称为Common Log Format (CLF),它被许多不同的Web服务器所采用,并可以为许多日志分析程序所辩识,它产生的事件记录有如:
127.0.0.1 - frank [10/Oct/2000:13:55:36 -0700] "GET /apache_pb.gif HTTP/1.0" 200 2326
记录的各部分说明如下。
127.0.0.1
(%h
)
这是发送请求到服务器的客户的IP地址。如果 HostnameLookups 设为 On
,则服务器会尝试解析这个IP地址的主机名,但是,并不推荐这样配置,因为会显著拖慢服务器,最好是用一个日志后续处理器还判断主机名,比如 logresolve。如果客户和服务器之间存在代理,那么记录中的这个IP地址就是那个代理,而不是客户面前的那个机器的IP地址。
-
(%l
)
这是由客户端 identd
判断的RFC 1413身份,输出中的符号 "-" 表示此处信息无效。除非在严格控制的内部网络中,此信息通常并不可靠,不应该被使用。只有在IdentityCheck 设为 On
时,Apache才会试图得到这项信息。
frank
(%u
)
这是由HTTP认证系统得到的访问该网页的客户名称,环境变量 REMOTE_USER
会被设为该值并提供给CGI脚本。如果状态码是401,表示客户没有通过认证,则此值没有意义。如果网页没有设置密码保护,则此项应该是"-
"。
[10/Oct/2000:13:55:36 -0700]
(%t
)
这是服务器完成对请求的处理时的时间,其格式是:
[day/month/year:hour:minute:second zone]
day = 2*digit
month = 3*letter
year = 4*digit
hour = 2*digit
minute = 2*digit
second = 2*digit
zone = (`+' | `-') 4*digit
可以在格式字符串中使用 %{format}t
改变时间的输出形式,format
与C标准库中的 strftime(3)
用法相同。
"GET /apache_pb.gif HTTP/1.0"
(\"%r\"
)
引号中是客户发出的包含了许多有用信息的请求内容。可以看出,该客户的动作是GET
,请求的资源是/apache_pb.gif
,使用的协议是HTTP/1.0
。另外,还可以记录其他信息,如:格式字符串 "%m %U%q %H
" 会记录动作、路径、请求串、协议,结果其输出会和"%r
" 一样。
200
(%>s
)
这个是服务器返回给客户端的状态码。这个信息非常有价值,因为它指示了请求的结果,或者是被成功响应了(以2开头),或者被转向了(以3开头),或者出错了(以4开头),或者产生了服务器端错误(以5开头)。完整的状态码列表参见HTTP specification (RFC2616 section 10).
2326
(%b
)
最后这项是返回给客户端的不包括响应头的字节数。如果没有信息返回,则此项应该是 "-
",如果希望记录为 "0
" 的形式,就应该用%B
。
伪静态技术
伪静态是一种将动态页面请求伪装成静态页面请求的手段,目的是改善动态页面对搜索引擎不好友好的缺点。同时也可以解决动态页面不被浏览器缓存的问题,提高系统性能。
一个典型的配置:
# #httpd.conf 配置rewriter模块 LoadModule rewrite_module modules/mod_rewrite.so # <IfModule mod_rewrite.c> RewriteEngine On RewriteRule ^(.*)/forum-([0-9]+)-([0-9]+)\.html$$1/forumdisplay.jsp?fid=$2&page=$3 </IfModule> |
RewriteEngine 指令打开或关闭运行时的重写引擎。如果设置为off,则此模块在运行时不执行任何重写操作。使用该指令可以使此模块无效,而无须注释所有的RewriteRule指令!
RewriteRule指令是重写引擎的根本。此指令可以多次使用。每个指令定义一个简单的重写规则。这些规则的定义顺序尤为重要——在运行时,规则是按这个顺序逐一生效的。
指令后面的参数均为正则表达式,以下是正则表达式的常见的用法:
文本 . 任意一个单字符 [chars] 字符类: "chars"中的任意一个字符 [^chars] 字符类: 不在"chars"中的字符 text1|text2 选择: text1 或 text2 [num1 – num2] 数字类:从num1~num2 量词 ? 前面的字符出现 0 或 1 次 * 前面的字符出现 0 或 N 次(N > 0) + 前面的字符出现 1 或 N 次(N > 1) 分组 (text) text 组 (常用于设置一个选择的边界,或用于生成后引用: 在RewriteRule中可以用 $N 引用第N个分组) 锚 ^ 以之开头 $ 以之结尾 转义 \c 对给定的字符c进行转义 (比如对".[]()"进行转义,等等) |
当请求URL符合“^(.*)/forum-([0-9]+)-([0-9]+)\.html$”正则表达式时,RewriteRule将使用后者“$1/forumdisplay.jsp?fid=$2&page=$3”(同样是正则表达式)代替前者。代替的顺序为
“.*”=>“$1”、“ [0-9]”=>“ $2”、“ [0-9]”=>“
$3”。比如/hh/forum-1-2.html的URL将被替换为/hh/forumdisplay.jsp?fid=1&page=2。
更加详细的配置请参考注3
AB测试工具
ab全称apache benchmark,为apache server自带的性能测试工具,具有轻量、强大等特点。
结果说明
ServerSoftware: Apache/2.0.64
//待测试服务器使用之软件版本
Server Hostname: 127.0.0.1
//服务器名称
Server Port: 80
//服务器端口
Document Path: /apache_pb.gif
//访问路径
Document Length: 2326 bytes
//页面大小
Concurrency Level: 10
//(ab测试的)使用的并发数
Time taken for tests: 0.374986 seconds
//总花费时间
Complete requests: 100
//完成的总请求数量
Failed requests: 0
//失败的请求数量
Write errors: 0
//写入失败的次数
Total transferred: 257400 bytes
//总的数据传输量
HTML transferred: 232600 bytes
//总的HTML内容传输量
Requests per second: 266.68 [#/sec] (mean)
//最重要的指标之一,平均每次请求花费的时间
Time per request: 37.499 [ms] (mean)
//最重要的指标之二,平均每秒处理多少个请求
Time per request: 3.750 [ms] (mean, across all concurrent requests)
//与以上的结果差别在于:这个结果是先算每次并发的平均时间t1,
最后再对这些时间求平均值t=(t1…tn)/n。
Transfer rate: 669.36 [Kbytes/sec] received
//平均每秒传输的流量,可以帮助排除是否存在网络流量过大导致响应时间延长的问题
Connection Times (ms)
min mean[+/-sd] median max
Connect: 0 0 0.0 0 0
Processing: 15 35 15.9 31 93
Waiting: 15 35 15.9 31 93
Total: 15 35 15.9 31 93
Percentage of the requests served within a certain time (ms)
50% 31
66% 31
75% 46
80% 46
90% 62
95% 78
98% 78
99% 93
100% 93 (longest request)
//所有请求的响应情况,重点关注90%用户的响应时间。
常用参数说明
-n 所有请求数
-c 单次产生的请求数,执行次数等于n/c
-t 限制所有请求在制定时间内完成。默认没有时间限制。
-p 表单提交之数据
-T 表单提交使用之Content-type头信息。
-v 设置显示信息的等级: - 4或以上显示头信息, 3或以上显示响应代码(404, 200等), 2或以上显示警告和其他信息。
-V 显示版本号并退出。
-w 以HTML格式输出结果。
-i 执行HEAD请求,与GET请求不同的是,HEAD请求并不会下载页面至本地。
注释
注1 测试结果引自文章http://www.whisperdale.net/11-nginx-vs-cherokee-vs-apache-vs-lighttpd.html
注2 Apache日志模块官网文档:http://man.chinaunix.net/newsoft/ApacheManual/logs.html
注3 Apache mod_rewriter模块官方文档:
http://www.phpchina.com/resource/manual/apache/mod/mod_rewrite.html