12月 102011
 

我们知道,Apache可以同时接受很多请求,在Apache的pre-fork模型中,有m个Apache的子进程随时等待着处理请求,这时,有n个请求过来了,那么,这n个请求是如何分配到m个进程来处理的呢?

首先,即使是n个请求同时到达,到操作系统层也会串行化后再提交到应用程序的。

情况一:
假设我们的Apache只listen一个IP:port, 则:
第一步:
父进程在fork子进程之前,会:
socket(…) = s;
listen(s,…) = 0;
这样就产生了一个listen指定ip:port 的文件描述符fd(s); Apache父进程在fork子进程时会将该fd(s)遗传给每一个子进程
第二步:
每个子进程都会处于:  accept(s,…) 状态,等待接受请求,这些子进程在s上排队,先到先得,得到请求后,开始处理请求,处理完请求后继续accept(s,…) 等待请求; 这样,如果每个请求的处理时间相同,则,每个子进程能处理的任务基本是平均分配的。

情况二:
假设我们的Apache同时listen了同一个IP上的80和443端口,则:
第一步:
父进程在fork子进程之间,会:
socket(…) = s80;
listen(s89,…) = 0;

socket(…) = s443;
listen(s443,…) = 0;

semget(…) = semaphore;
产生s80, s443 两个fd (如果是epoll模型,则是创建一个epoll)和一个信号量semaphore

第二步:
因为accept不能同时在两个socket上排队,所以直接进入accept状态就不行了,所以就用到了“同步IO多路技术”,一般有三种实现: select、poll、epoll,其中epoll是最为高效的一种,这里已epoll为例说明。因为epoll只能同时accept多个socket,但是没法让多个进程在epoll的多个socket上排队;所以还得引入一个信号量的概念,信号量是解决同步与互斥问题的,常用的一种实现为semop(…); 这样多个Apache的子进程通过semop在父进程产生的信号量semaphore上排队,这样,保证同时只有一个进程处于可以接受请求的状态,接受到请求之后,退出队列,然后由下一个子进程接受请求。基本流程如下:

 Posted by at 上午 9:42
10月 202011
 

首先,为了方便调试,使用gdb单进程启动apache,并在执行PHP脚本的函数处设置断点:

输入 run 开始运行,并在另外一个终端使用curl访问apache,于是程序会停止在zend_execute_scripts,使用bt查看调用栈如下:

 Posted by at 上午 8:55
7月 092011
 

Apache 通过管道记日志对性能的影响:

  1. 如果访问量比较小,则,使用管道可能比只直接写文件要快那么一点点(不会太明显),因为写管道毕竟是些内存(但是操作系统层面对写文件也是有优化的)
  2. 如果访问量很大,多个进程并发写一个管道,而只有一个进程在读管道,写日志文件,则,管道很容易写慢导致处理进程阻塞,这时,管道产生的瓶颈是很明显的,可能会导致Apache的处理性能减少一个数量级;而且你看到的将是cpu idle很高,iowait很低,但是loadaverage却很高

 Posted by at 上午 10:05
7月 032011
 

最近,在Linux上对Apache-2.1.16做了一下压力测试;只访问一个简单的hello world静态文件,做了多次,每秒处理请求次数才700左右;
一定是哪里出现了问题,因为:
   1. 虽然load很高,但是cpu和io都很低,这种现象就比较奇怪
   2. client端的负载很低
   3. 曾经有过 2000/s的测试,而且当时的机器配置比现在的要差的多

当我使用同样的方法对nginx做测试的时候,nginx可以达到 2.2w/s 。

无奈之下,只好又把strace请出来了,发现写日志的地方很慢。
于是怀疑是磁盘问题; 使用hdparm测试了一下磁盘,没有问题; 又用 time dd … 测试了一下,写的速度也 700+MB/s ; 回头想想,确认了一下nginx也是记日志的,而且都是同一块磁盘,大概不是磁盘问题了。

修改Apache 配置文件,把记日志的配置注释掉, 重新测试,发现可以达到 7k+/s 了;仔细观察Apache的配置,是这样写的:
TransferLog "|/data1/apache2/bin/rotatelogs /path/to/logfile"

于是,我想怀疑rotatelogs 这个程序了,但是似乎也没有道理; 那么就只剩下那个管道了,恩, 大概问题就出现在管道上了。

因为管道的大小为4k, 多进程写管道,单进程读管道,也难免会出现阻塞的情况;另外,load高的时候,io是很低的,所以说磁盘不是瓶颈。 管道是瓶颈

———-
如何利用管道来模拟一个cpu、io都很低,但是load很高的实例呢?

 Posted by at 上午 11:04
2月 252011
 

Apache可以协商的内容基本有四类:

  1. 文档类型: content-type, 通过accept来说明
  2. 语言: language, 通过accept-language来说明
  3. 字符集: charset, 通过accept-charset来说明
  4. 编码: encoding, 通过accept-encoding来说明; (注意是传输过程的编码,不是字符的编码)

相关源码参考:
modules/mappers/mod_negotiation.c

  1. typedef struct {
  2.     apr_pool_t *pool;
  3.     request_rec *r;
  4.     neg_dir_config *conf;
  5.     char *dir_name;
  6.     int accept_q;               /* 1 if an Accept item has a q= param */
  7.     float default_lang_quality; /* fiddle lang q for variants with no lang */
  8.     /* the array pointers below are NULL if the corresponding accept
  9.      * headers are not present
  10.      */
  11.     apr_array_header_t *accepts;            /* accept_recs */
  12.     apr_array_header_t *accept_encodings;   /* accept_recs */
  13.     apr_array_header_t *accept_charsets;    /* accept_recs */
  14.     apr_array_header_t *accept_langs;       /* accept_recs */
  15.     apr_array_header_t *avail_vars;         /* available variants */
  16.     int count_multiviews_variants;    /* number of variants found on disk */
  17.     int is_transparent;       /* 1 if this resource is trans. negotiable */
  18.     int dont_fiddle_headers;  /* 1 if we may not fiddle with accept hdrs */
  19.     int ua_supports_trans;    /* 1 if ua supports trans negotiation */
  20.     int send_alternates;      /* 1 if we want to send an Alternates header */
  21.     int may_choose;           /* 1 if we may choose a variant for the client */
  22.     int use_rvsa;             /* 1 if we must use RVSA/1.0 negotiation algo */
  23. } negotiation_state;

对于协商的表达方式都是一样的,如:

Accept: */*
Accept-Language: zh-cn,zh;q=0.5
Accept-Encoding: gzip,deflate
Accept-Charset: GB2312,utf-8;q=0.7,*;q=0.7

其中“,”和“;”的分隔或许不太好明白其含义,其实,其格式是这样的:
name;q=N;charset=TEXT
如果要表达多组,则用“,”分隔;如:
name;q=N;charset=TEXT,name;q=N;charset=TEXT

其中,q、charset都是可以省略的,如:
Accept: */*
只有一组说明,而且是省略了q和charset

相关源码参考:
modules/mappers/mod_negotiation.c

  1. typedef struct accept_rec {
  2.     char *name;                 /* MUST be lowercase */
  3.     float quality;
  4.     float level;
  5.     char *charset;              /* for content-type only */
  6. } accept_rec;

关于文档类型的协商依赖的是: docs/conf/mime.types
如:
文档类型                     资源扩展名
text/html                   html htm
text/css                    css
text/plain                  txt text conf def list log in  

关于语言和字符集的协商依赖的是: docs/conf/charset.conv
如:
# Lang-abbv Charset     Language
#———————————
en          ISO-8859-1  English
UTF-8       utf8        UTF-8  
Unicode     ucs         Unicode
th          Cp874       Thai    
ja          SJIS        Japanese
ko          Cp949       Korean  
zh          Cp950       Chinese-Traditional
zh-cn       GB2312      Chinese-Simplified
zh-tw       Cp950       Chinese
。。。

其中,第一列是语言的缩写,协商时一般用缩写; 第二列是字符集

—————————
内容可能是根据多个条件来协商的,那么对于一个协商的资源可能涉及到多个扩展名的,如:
content.en.html.gz
该资源如果写成了:
content.html.en.gz
也是可以找到的,只是在做超链接的时候,如果写成了: content.gz.html.en 就找不到了
参考: http://httpd.apache.org/docs/2.2/content-negotiation.html#naming

关于内容协商与cache
对于http1.0来讲,经过协商的内容是不建议cache的;在http1.1中添加了vary的http头,用来告知客户端内容是根据哪些条件来协商的,这样客户端可以尽可能的利用cache,如果协商条件不变的话就可以使用cache的。

====================
参考资料:

http://httpd.apache.org/docs/2.2/content-negotiation.html
http://httpd.apache.org/docs/2.2/mod/mod_negotiation.html

 Posted by at 下午 10:51
11月 022010
 

AddType 是与类型表相关的,描述的是扩展名与文件类型之间的关系,如:
AddType application/x-x509-ca-cert .crt

说明 .crt 扩展名的文件就是application/x-x509-ca-cert类型的; 在内容协商时,如果客户端需要是application/x-x509-ca-cert类型的,就将 .crt结尾的资源返回
注意: 经过内容协商的资源,在http相应头中有相应的Content-Location说明,如:

GET /a HTTP/1.1


Content-Location: a.php

AddHandler 说明什么样的扩展名使用什么样的程序来处理,描述的是扩展名与处理程序之间的关系
AddHandler cgi-script .cgi

 Posted by at 上午 1:36
11月 022010
 

该功能使得服务器可以根据agent指定的http头来选择合适的资源。
涉及的http头包括: Accept-*
涉及的Apache中的知识: 类型表

1。 需要模块 : modules/mod_negotiation.so
2。 需要在目录的Options中添加: MultiViews;  如: Options FollowSymLinks MultiViews Indexes
3。 参考文档: http://apache.jz123.cn/content-negotiation.html
4。 该功能可能会影响到rewrite,参考: http://www.linuxpk.com/4941.html

apache根据你给的资源名称a,查找所有的a.*资源,加入有两种资源: a.txt 和a.php, 在类型表中查出:
.txt 对应文档类型为: text/plain 
.php 对应文档类型为: application/x-httpd-php

如果请求时使用的 accept为: text/plain ,则返回a.txt
如果请求时使用的 accept为: application/x-httpd-php ,则返回a.php
如果请求时使用的accept为: text/none ,找不到这种类型,则协商失败,apache返回406,并返回所有可用的类型列表,如:

Not Acceptable

An appropriate representation of the requested resource /a could not be found  on this server.

Available variants:

        

  • a.php , type application/x-httpd-php
  •     

  • a.txt , type text/plain
 Posted by at 上午 1:27
5月 202010
 

大网站一般都是用F5或其它的负载均衡设备,用户的每次请求落在那台real server都是不一定的,一次偶然的机会,发现相同的一个资源在两次访问时,http头中的etag是不相同的,主要是该资源一定是没有被修改的,于是想到etag的算法中可能使用到了资源在不同real server上可能不同的因子,于是参考了一下apache的文档(因为我们使用的是apache),文档地址:
http://lamp.linux.gov.cn/Apache/ApacheMenu/mod/core.html#fileetag

默认情况下,etag和资源的inode有关系,而inode在不同的real server上基本是不相同的,于是,这样的etag还不如没有,因为这样该资源是每次都要返回全部信息的,可以修改apache配置文件,添加如下配置:

FileETag MTime Size

这样修改后,如果文件没有发生变化,则只需要返回304就行,不需要返回所有内容了。
如果不做这样的设置,对于使用f5的情况,etag或许还不如没有。

关于apache中etag的算法,可以参考源码: src/main/http_core.c 中的函数set_etag_bits

那么,你的apache是怎么配置的呢?随便访问一个能返回etag的资源,etag类似:
ETag: "1e50006-22d0-4ba718e6"

如果是“-”分隔的3部分,字基本是包含inode、mtime、size了,如果是两部分,那就是其中的两项了,注意,每一项产生的长短不是固定的,不能按照长短来分析。

 Posted by at 上午 9:02
5月 162010
 

1. 多个连续的RewriteCond 之间默认是“与”的关系,可以通过 [OR]来说明是“或”的关系, RewriteCond只作用于紧接着的一个RewriteRule

2. RewriteCond的flag有两个可选值: NC: 不区分大小写; OR:表明是“或”的关系

3. RewriteRule的flag常见值有两个:
[P]: 说明
立即中断重写处理,然后把处理移交给mod_proxy模块, 如果说立即中断重写处理,则[P,L] 中的L又有什么意义呢?
[L]: 相当于break,不要再继续匹配了,如果这条规则匹配上了,则这就是最后一个匹配了
不常见的几个flag:
[N]: 相当于continue,用当前已经重写好的url再从头开始继续匹配;这样写很难理解,所以很少有这样用的。
[F]: 禁止访问
[R]: 强制重定向,而不是直接代理,对于下面情况R就是多余的,因为没有P标记,则一定是强制重定向:

 Posted by at 上午 4:26