- 浏览: 567562 次
- 性别:
- 来自: 北京
文章分类
- 全部博客 (618)
- java (109)
- Java web (43)
- javascript (52)
- js (15)
- 闭包 (2)
- maven (8)
- 杂 (28)
- python (47)
- linux (51)
- git (18)
- (1)
- mysql (31)
- 管理 (1)
- redis (6)
- 操作系统 (12)
- 网络 (13)
- mongo (1)
- nginx (17)
- web (8)
- ffmpeg (1)
- python安装包 (0)
- php (49)
- imagemagic (1)
- eclipse (21)
- django (4)
- 学习 (1)
- 书籍 (1)
- uml (3)
- emacs (19)
- svn (2)
- netty (9)
- joomla (1)
- css (1)
- 推送 (2)
- android (6)
- memcached (2)
- docker、 (0)
- docker (7)
- go (1)
- resin (1)
- groovy (1)
- spring (1)
最新评论
-
chokee:
...
Spring3 MVC 深入研究 -
googleyufei:
很有用, 我现在打算学学Python. 这些资料的很及时.
python的几个实用网站(转的) -
hujingwei1001:
太好了找的就是它
easy explore -
xiangtui:
例子举得不错。。。学习了
java callback -
幻影桃花源:
太好了,謝謝
Spring3 MVC 深入研究
原文地址:http://www.cnxct.com/several-reminder-in-nginx-fastcgi_cache-and-php-session_cache_limiter/
在web项目中,大家都已经非常熟悉其架构流程了。都说Cache是万金油,哪里不舒服抹哪里。这些流程中,几乎每个环节都会进行cache。从浏览器到webserver,到cgi程序,到DB数据库,会进行浏览器cache,数据cache,SQL查询的cache等等。对于fastcgi这里的cache,很少被使用。去年年底,我对nginx的fastcgi_cache进行摸索使用。在我的测试过程中,发现一些WIKI以及网络上没被提到的注意点,这里分享一下。
从浏览器到数据库的流程图
从浏览器到数据库的流程图
这里是我的NGinx配置信息
01
#增加调试信息
02
add_header X-Cache-CFC "$upstream_cache_status - $upstream_response_time";
03
fastcgi_temp_path /dev/shm/nginx_tmp;
04
05
#cache设置
06
fastcgi_cache_path /dev/shm/nginx_cache levels=1:2 keys_zone=cfcache:10m inactive=50m;
07
fastcgi_cache_key "$request_method://$host$request_uri";
08
fastcgi_cache_methods GET HEAD;
09
fastcgi_cache cfcache;
10
fastcgi_cache_valid any 1d;
11
fastcgi_cache_min_uses 1;
12
fastcgi_cache_use_stale error timeout invalid_header http_500;
13
fastcgi_ignore_client_abort on;
配置这些参数时,注意每个参数的作用域,像fastcgi_cache_path参数,只能在http配置项里配置,而fastcgi_cache_min_uses这个参数,可以在http、server、location三个配置项里配置。这样更灵活的会每个域名、每个匹配的location进行选择性cache了。具体的参数作用域,参考FASTCGI模块的官方WIKI。我为了调试方便,添加了一个『X-Cache-CFC』的http响应头,$upstream_cache_status 变量表示此请求响应来自cache的状态,分别为:
MISS 未命中
EXPIRED – expired, request was passed to backend Cache已过期
UPDATING – expired, stale response was used due to proxy/fastcgi_cache_use_stale updating Cache已过期,(被其他nginx子进程)更新中
STALE – expired, stale response was used due to proxy/fastcgi_cache_use_stale Cache已过期,响应数据不合法,被污染
HIT 命中cache
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中
程序代码是Discuz!论坛, 随便开启测试了几下,发现/dev/shm/nginx_cache/下没有任何目录建立,也没有文件创建。调试的http header响应头里的X-Cache-CFC 结果一直是MISS。从服务器进程上来看,Nginx cache manager process 跟Nginx cache loader process 进程也正常运行:
1
root 3100 1 0 14:52 ? 00:00:00 nginx: master process /usr/sbin/nginx
2
www-data 3101 3100 0 14:52 ? 00:00:00 nginx: worker process
3
www-data 3102 3100 0 14:52 ? 00:00:00 nginx: cache manager process
4
www-data 3103 3100 0 14:52 ? 00:00:00 nginx: cache loader process
不知道为何会这样,为何没有cache成功,我以为我配置参数有问题,只好阅读WIKI。发现fastcgi_ignore_headers 参数下解释有这么一段
fastcgi_ignore_headers
Syntax: fastcgi_ignore_headers field …
Default:
Context: http
server
location
Reference: fastcgi_ignore_headers
This directive forbids processing of the named headers from the FastCGI-server reply. It is possible to specify headers like “X-Accel-Redirect”, “X-Accel-Expires”, “Expires” or “Cache-Control”.
也就是说这个参数的值,将会被忽略掉,同样被忽略掉的响应头比如”X-Accel-Redirect”, “X-Accel-Expires”, “Expires” or “Cache-Control”,而nginx配置中并没有fastcgi_ignore_headers参数的设定,那么问题会不会出现在FASTCGI响应结果里包含了类似”X-Accel-Redirect”, “X-Accel-Expires”, “Expires” or “Cache-Control”这几个响应头呢?用strace抓包,看了下nginx与fpm进程通讯的数据
1
####为了确保准确抓到处理该http请求的进程,我把nginx 、fpm都只开启了一个进程处理。
2
//strace -ff -tt -s 1000 -o xxx.log -p PHPFPM-PID
3
14:52:07.837334 write(3, "\1\6\0\1\0\343\5\0X-Powered-By: PHP/5.3.10-1ubuntu3.5\r\nExpires: Thu, 19 Nov 1981 08:52:00 GMT\r\nCache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\nPragma: no-cache\r\nContent-type: text/html\r\n\r\nHello cfc4n1362034327\0\0\0\0\0\1\3\0\1\0\10\0\0\0\0\0\0\0\0\0\0", 256) = 256
4
5
//strace -ff -tt -s 1000 -o xxx.log -p Nginx-PID
6
15:05:13.265663 recvfrom(12, "\1\6\0\1\0\343\5\0X-Powered-By: PHP/5.3.10-1ubuntu3.5\r\nExpires: Thu, 19 Nov 1981 08:52:00 GMT\r\nCache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\nPragma: no-cache\r\nContent-type: text/html\r\n\r\nHello cfc4n1362035113\0\0\0\0\0\1\3\0\1\0\10\0\0\0\0\0\0\0\0\0\0", 4023, 0, NULL, NULL) = 256
从抓取的数据包里可以看到,fpm确实返回了包含“Expires”、“Cache-Control”头的http 响应头信息。那么疑问来了:
nginx的fastcgi_cache没缓存这条http响应,是因为响应头里包含“Expires”、“Cache-Control”的原因吗?
程序里并没有输出“Expires”、“Cache-Control” http header的代码,这是谁输出的呢?
既然是fpm响应的时候,就已经有了,那么是php的core模块,还是其他拓展模块输出的?
“Expires:”时间为何是“Thu, 19 Nov 1981 08:52:00 GMT”?
疑问比较多,一个一个查起,先从Nginx的fastcgi_cache没缓存这条http响应查起。我根据测试环境nginx版本1.1.9(ubuntu 12.04默认的),到nginx官方下了对应版本的源码,搜索了fastcgi参数使用的地方,在http\ngx_http_upstream.c找到了。虽然不能很流程的读懂nginx的代码,但粗略的了解,根据了解的情况加以猜测,再动手测试实验,也得出了结论,确定了nginx的fastcgi_cache的规则。
01
//ngx_http_upstream.c
02
//line 3136 当fastcgi响应包含set-cookie时,不缓存
03
static ngx_int_t
04
ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
05
ngx_uint_t offset)
06
{
07
#if (NGX_HTTP_CACHE)
08
ngx_http_upstream_t *u;
09
10
u = r->upstream;
11
12
if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {
13
u->cacheable = 0;
14
}
15
#endif
16
17
return NGX_OK;
18
}
19
20
//line 3242 当响应头包含Expires时,如果过期时间大于当前服务器时间,则nginx_cache会缓存该响应,否则,则不缓存
21
static ngx_int_t
22
ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
23
ngx_uint_t offset)
24
{
25
ngx_http_upstream_t *u;
26
27
u = r->upstream;
28
u->headers_in.expires = h;
29
30
#if (NGX_HTTP_CACHE)
31
{
32
time_t expires;
33
34
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
35
return NGX_OK;
36
}
37
38
if (r->cache == NULL) {
39
return NGX_OK;
40
}
41
42
if (r->cache->valid_sec != 0) {
43
return NGX_OK;
44
}
45
46
expires = ngx_http_parse_time(h->value.data, h->value.len);
47
48
if (expires == NGX_ERROR || expires < ngx_time()) { u->cacheable = 0;
49
return NGX_OK;
50
}
51
52
r->cache->valid_sec = expires;
53
}
54
#endif
55
56
return NGX_OK;
57
}
58
59
//line 3199 当响应头包含Cache-Control时,#####如果####这里有如果啊。。。
60
//【注意】如果Cache-Control参数值为no-cache、no-store、private中任意一个时,则不缓存...不缓存...
61
//【注意】如果Cache-Control参数值为max-age时,会被缓存,且nginx设置的cache的过期时间,就是系统当前时间 + mag-age的值
62
if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL
63
|| ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL
64
|| ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL)
65
{
66
u->cacheable = 0;
67
return NGX_OK;
68
}
69
70
p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1);
71
72
if (p == NULL) {
73
return NGX_OK;
74
}
75
...
76
r->cache->valid_sec = ngx_time() + n;
也就是说,fastcgi响应http请求的结果中,响应头包括Expires、Cache-Control、Set-Cookie三个,都会可能不被cache,但不只有这些,别忘了nginx配置中fastcgi_ignore_headers参数设定的部分。以及ngxin的X-ACCEL X-Accel-Redirect、X-Accel-Expires、X-Accel-Charset、X-Accel-Buffering等nginx自定义的响应头。由于这几个不常用,我也没深入研究。通过对nginx的ngx_http_upstream模块代码模糊理解,加猜测,以及写了脚本测试验证,可以得到结论是正确的。即Nginx fastcgi_cache在缓存后端fastcgi响应时,当响应里包含“set-cookie”时,不缓存;当响应头包含Expires时,如果过期时间大于当前服务器时间,则nginx_cache会缓存该响应,否则,则不缓存;当响应头包含Cache-Control时,如果Cache-Control参数值为no-cache、no-store、private中任意一个时,则不缓存,如果Cache-Control参数值为max-age时,会被缓存,且nginx设置的cache的过期时间,就是系统当前时间 + mag-age的值。
nginx fastcgi_cache 响应expired
nginx fastcgi_cache 响应expired
nginx fastcgi_cache hit命中
nginx fastcgi_cache hit命中
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中。
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中。
01
//逐个测试,测试时,注释其他的
02
header("Expires: ".gmdate("D, d M Y H:i:s", time()+10000).' GMT');
03
header("Expires: ".gmdate("D, d M Y H:i:s", time()-99999).' GMT');
04
header("X-Accel-Expires:30");
05
header("Cache-Control: no-cache");
06
header("Cache-Control: no-store");
07
header("Cache-Control: private");
08
header("Cache-Control: max-age=10");
09
setcookie('cfc4n',"testaaaa");
10
echo 'Hello cfc4n',time();
到了这里,疑问1解决了。那么疑问2、3呢?程序里并没有输出“Expires”、“Cache-Control” http header的代码,这是谁输出的呢?既然是fpm响应的时候,就已经有了,那么是php的core模块,还是其他拓展模块输出的?我精简了代码,只输出一个“hello world”,发现也确实被缓存了。显然,php脚本程序中并没输出http header 的“Expires”、“Cache-Control”,多次测试,最终定位到session_start函数,翻阅源码找到了这些代码:
01
//ext/session/session.c line:1190 左右
02
// ...
03
CACHE_LIMITER_FUNC(private) /* {{{ */
04
{
05
ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
06
CACHE_LIMITER(private_no_expire)(TSRMLS_C);
07
}
08
/* }}} */
09
//再到这里3 或者上面几个 ##默认是nocache
10
CACHE_LIMITER_FUNC(nocache) /* {{{ */
11
{
12
ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
13
14
/* For HTTP/1.1 conforming clients and the rest (MSIE 5) */
15
ADD_HEADER("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
16
17
/* For HTTP/1.0 conforming clients */
18
ADD_HEADER("Pragma: no-cache");
19
}
20
/* }}} */
21
//这里2
22
static php_session_cache_limiter_t php_session_cache_limiters[] = {
23
CACHE_LIMITER_ENTRY(public)
24
CACHE_LIMITER_ENTRY(private)
25
CACHE_LIMITER_ENTRY(private_no_expire)
26
CACHE_LIMITER_ENTRY(nocache)
27
{0}
28
};
29
30
static int php_session_cache_limiter(TSRMLS_D) /* {{{ */
31
{
32
php_session_cache_limiter_t *lim;
33
34
if (PS(cache_limiter)[0] == '\0') return 0;
35
36
if (SG(headers_sent)) {
37
const char *output_start_filename = php_output_get_start_filename(TSRMLS_C);
38
int output_start_lineno = php_output_get_start_lineno(TSRMLS_C);
39
40
if (output_start_filename) {
41
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)", output_start_filename, output_start_lineno);
42
} else {
43
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent");
44
}
45
return -2;
46
}
47
48
for (lim = php_session_cache_limiters; lim->name; lim++) {
49
if (!strcasecmp(lim->name, PS(cache_limiter))) {
50
lim->func(TSRMLS_C); //这里1
51
return 0;
52
}
53
}
54
55
return -1;
56
}
57
// ...
到了这里,知道原因了,是程序调用session_start时,php的session拓展自己输出的。session.cache_limit参数来决定输出包含哪种Expires的header,默认是nocache,修改php.ini的session.cache_limit参数为“none”即可让session模块不再输出这些http 响应头。或在调用session_start之前,使用session_cache_limiter函数来指定下该参数值。那为什么要在使用session时,发Expires、Cache-Control的http response header呢?我猜测了下,需要session时,基本上是用户跟服务器有交互,那么,既然有交互,就意味着用户的每次交互结果也可能不一样,就不能cache这个请求的结果,给返回给这个用户。同时,每个用户的交互结果都是不一样的,nginx也就不能把包含特殊Cache-Control的个人响应cache给其他人提供了。
还有一个无聊的问题“Expires:时间为何是Thu, 19 Nov 1981 08:52:00 GMT”?我翻阅了session.c这段代码的添加时间,版本,作者信息,在php官方版本库中找到了这次提交的信息:
Revision 17092 – (view) (download) (as text) (annotate) – [select for diffs]
Modified Sun Dec 12 14:16:55 1999 UTC (13 years, 2 months ago) by sas
File length: 28327 byte(s)
Diff to previous 16964
Add cache_limiter and cache_expire options. Rename extern_referer_check
to referer_check.
对比session.c两个版本的变更,果然是这块代码。作者是sas,也就是Sascha Schumann, http://php.net/credits.php里可以看到他的大名。关于这个expires过期时间的问题,有人在stackoverflow也提问过,Why is “Expires” 1981?,别人说那天是他生日。这是真的么?如果那天是他生日的话,而他增加session.cache_limiter时是1999年,他才17岁,17岁呀。我17岁时在干嘛?还不知道电脑长啥样,正在玩『超级玛丽』呢。
好奇的不是我一个人,还有个帖子是epoch date — Expires: Thu, 19 Nov 1981 08:52:00也问了。另外两个地址虽然没问,也有人提到那天是他生日了。http://boinc.berkeley.edu/dev/forum_thread.php?id=2514、https://github.com/codeguy/Slim/issues/157,这些帖子都提到说原帖是http://www.phpbuilder.com/lists/php3-list/199911/3159.php ,我无法访问,被跳转到首页了。用http://web.archive.org找到了历史快照,发现上下文关系不大,也不能证明是他生日。 我更是好奇的发了两封邮件到他的不同邮箱里问他,不过,目前他还没回复。或许他没收到、没看到,或许懒得回了。N年后,“Expires:时间为何是Thu, 19 Nov 1981 08:52:00 GMT”这个日期,会不会又成了一段奇闻佳话了呢?
在web项目中,大家都已经非常熟悉其架构流程了。都说Cache是万金油,哪里不舒服抹哪里。这些流程中,几乎每个环节都会进行cache。从浏览器到webserver,到cgi程序,到DB数据库,会进行浏览器cache,数据cache,SQL查询的cache等等。对于fastcgi这里的cache,很少被使用。去年年底,我对nginx的fastcgi_cache进行摸索使用。在我的测试过程中,发现一些WIKI以及网络上没被提到的注意点,这里分享一下。
从浏览器到数据库的流程图
从浏览器到数据库的流程图
这里是我的NGinx配置信息
01
#增加调试信息
02
add_header X-Cache-CFC "$upstream_cache_status - $upstream_response_time";
03
fastcgi_temp_path /dev/shm/nginx_tmp;
04
05
#cache设置
06
fastcgi_cache_path /dev/shm/nginx_cache levels=1:2 keys_zone=cfcache:10m inactive=50m;
07
fastcgi_cache_key "$request_method://$host$request_uri";
08
fastcgi_cache_methods GET HEAD;
09
fastcgi_cache cfcache;
10
fastcgi_cache_valid any 1d;
11
fastcgi_cache_min_uses 1;
12
fastcgi_cache_use_stale error timeout invalid_header http_500;
13
fastcgi_ignore_client_abort on;
配置这些参数时,注意每个参数的作用域,像fastcgi_cache_path参数,只能在http配置项里配置,而fastcgi_cache_min_uses这个参数,可以在http、server、location三个配置项里配置。这样更灵活的会每个域名、每个匹配的location进行选择性cache了。具体的参数作用域,参考FASTCGI模块的官方WIKI。我为了调试方便,添加了一个『X-Cache-CFC』的http响应头,$upstream_cache_status 变量表示此请求响应来自cache的状态,分别为:
MISS 未命中
EXPIRED – expired, request was passed to backend Cache已过期
UPDATING – expired, stale response was used due to proxy/fastcgi_cache_use_stale updating Cache已过期,(被其他nginx子进程)更新中
STALE – expired, stale response was used due to proxy/fastcgi_cache_use_stale Cache已过期,响应数据不合法,被污染
HIT 命中cache
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中
程序代码是Discuz!论坛, 随便开启测试了几下,发现/dev/shm/nginx_cache/下没有任何目录建立,也没有文件创建。调试的http header响应头里的X-Cache-CFC 结果一直是MISS。从服务器进程上来看,Nginx cache manager process 跟Nginx cache loader process 进程也正常运行:
1
root 3100 1 0 14:52 ? 00:00:00 nginx: master process /usr/sbin/nginx
2
www-data 3101 3100 0 14:52 ? 00:00:00 nginx: worker process
3
www-data 3102 3100 0 14:52 ? 00:00:00 nginx: cache manager process
4
www-data 3103 3100 0 14:52 ? 00:00:00 nginx: cache loader process
不知道为何会这样,为何没有cache成功,我以为我配置参数有问题,只好阅读WIKI。发现fastcgi_ignore_headers 参数下解释有这么一段
fastcgi_ignore_headers
Syntax: fastcgi_ignore_headers field …
Default:
Context: http
server
location
Reference: fastcgi_ignore_headers
This directive forbids processing of the named headers from the FastCGI-server reply. It is possible to specify headers like “X-Accel-Redirect”, “X-Accel-Expires”, “Expires” or “Cache-Control”.
也就是说这个参数的值,将会被忽略掉,同样被忽略掉的响应头比如”X-Accel-Redirect”, “X-Accel-Expires”, “Expires” or “Cache-Control”,而nginx配置中并没有fastcgi_ignore_headers参数的设定,那么问题会不会出现在FASTCGI响应结果里包含了类似”X-Accel-Redirect”, “X-Accel-Expires”, “Expires” or “Cache-Control”这几个响应头呢?用strace抓包,看了下nginx与fpm进程通讯的数据
1
####为了确保准确抓到处理该http请求的进程,我把nginx 、fpm都只开启了一个进程处理。
2
//strace -ff -tt -s 1000 -o xxx.log -p PHPFPM-PID
3
14:52:07.837334 write(3, "\1\6\0\1\0\343\5\0X-Powered-By: PHP/5.3.10-1ubuntu3.5\r\nExpires: Thu, 19 Nov 1981 08:52:00 GMT\r\nCache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\nPragma: no-cache\r\nContent-type: text/html\r\n\r\nHello cfc4n1362034327\0\0\0\0\0\1\3\0\1\0\10\0\0\0\0\0\0\0\0\0\0", 256) = 256
4
5
//strace -ff -tt -s 1000 -o xxx.log -p Nginx-PID
6
15:05:13.265663 recvfrom(12, "\1\6\0\1\0\343\5\0X-Powered-By: PHP/5.3.10-1ubuntu3.5\r\nExpires: Thu, 19 Nov 1981 08:52:00 GMT\r\nCache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0\r\nPragma: no-cache\r\nContent-type: text/html\r\n\r\nHello cfc4n1362035113\0\0\0\0\0\1\3\0\1\0\10\0\0\0\0\0\0\0\0\0\0", 4023, 0, NULL, NULL) = 256
从抓取的数据包里可以看到,fpm确实返回了包含“Expires”、“Cache-Control”头的http 响应头信息。那么疑问来了:
nginx的fastcgi_cache没缓存这条http响应,是因为响应头里包含“Expires”、“Cache-Control”的原因吗?
程序里并没有输出“Expires”、“Cache-Control” http header的代码,这是谁输出的呢?
既然是fpm响应的时候,就已经有了,那么是php的core模块,还是其他拓展模块输出的?
“Expires:”时间为何是“Thu, 19 Nov 1981 08:52:00 GMT”?
疑问比较多,一个一个查起,先从Nginx的fastcgi_cache没缓存这条http响应查起。我根据测试环境nginx版本1.1.9(ubuntu 12.04默认的),到nginx官方下了对应版本的源码,搜索了fastcgi参数使用的地方,在http\ngx_http_upstream.c找到了。虽然不能很流程的读懂nginx的代码,但粗略的了解,根据了解的情况加以猜测,再动手测试实验,也得出了结论,确定了nginx的fastcgi_cache的规则。
01
//ngx_http_upstream.c
02
//line 3136 当fastcgi响应包含set-cookie时,不缓存
03
static ngx_int_t
04
ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
05
ngx_uint_t offset)
06
{
07
#if (NGX_HTTP_CACHE)
08
ngx_http_upstream_t *u;
09
10
u = r->upstream;
11
12
if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {
13
u->cacheable = 0;
14
}
15
#endif
16
17
return NGX_OK;
18
}
19
20
//line 3242 当响应头包含Expires时,如果过期时间大于当前服务器时间,则nginx_cache会缓存该响应,否则,则不缓存
21
static ngx_int_t
22
ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
23
ngx_uint_t offset)
24
{
25
ngx_http_upstream_t *u;
26
27
u = r->upstream;
28
u->headers_in.expires = h;
29
30
#if (NGX_HTTP_CACHE)
31
{
32
time_t expires;
33
34
if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
35
return NGX_OK;
36
}
37
38
if (r->cache == NULL) {
39
return NGX_OK;
40
}
41
42
if (r->cache->valid_sec != 0) {
43
return NGX_OK;
44
}
45
46
expires = ngx_http_parse_time(h->value.data, h->value.len);
47
48
if (expires == NGX_ERROR || expires < ngx_time()) { u->cacheable = 0;
49
return NGX_OK;
50
}
51
52
r->cache->valid_sec = expires;
53
}
54
#endif
55
56
return NGX_OK;
57
}
58
59
//line 3199 当响应头包含Cache-Control时,#####如果####这里有如果啊。。。
60
//【注意】如果Cache-Control参数值为no-cache、no-store、private中任意一个时,则不缓存...不缓存...
61
//【注意】如果Cache-Control参数值为max-age时,会被缓存,且nginx设置的cache的过期时间,就是系统当前时间 + mag-age的值
62
if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL
63
|| ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL
64
|| ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL)
65
{
66
u->cacheable = 0;
67
return NGX_OK;
68
}
69
70
p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1);
71
72
if (p == NULL) {
73
return NGX_OK;
74
}
75
...
76
r->cache->valid_sec = ngx_time() + n;
也就是说,fastcgi响应http请求的结果中,响应头包括Expires、Cache-Control、Set-Cookie三个,都会可能不被cache,但不只有这些,别忘了nginx配置中fastcgi_ignore_headers参数设定的部分。以及ngxin的X-ACCEL X-Accel-Redirect、X-Accel-Expires、X-Accel-Charset、X-Accel-Buffering等nginx自定义的响应头。由于这几个不常用,我也没深入研究。通过对nginx的ngx_http_upstream模块代码模糊理解,加猜测,以及写了脚本测试验证,可以得到结论是正确的。即Nginx fastcgi_cache在缓存后端fastcgi响应时,当响应里包含“set-cookie”时,不缓存;当响应头包含Expires时,如果过期时间大于当前服务器时间,则nginx_cache会缓存该响应,否则,则不缓存;当响应头包含Cache-Control时,如果Cache-Control参数值为no-cache、no-store、private中任意一个时,则不缓存,如果Cache-Control参数值为max-age时,会被缓存,且nginx设置的cache的过期时间,就是系统当前时间 + mag-age的值。
nginx fastcgi_cache 响应expired
nginx fastcgi_cache 响应expired
nginx fastcgi_cache hit命中
nginx fastcgi_cache hit命中
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中。
FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中。
01
//逐个测试,测试时,注释其他的
02
header("Expires: ".gmdate("D, d M Y H:i:s", time()+10000).' GMT');
03
header("Expires: ".gmdate("D, d M Y H:i:s", time()-99999).' GMT');
04
header("X-Accel-Expires:30");
05
header("Cache-Control: no-cache");
06
header("Cache-Control: no-store");
07
header("Cache-Control: private");
08
header("Cache-Control: max-age=10");
09
setcookie('cfc4n',"testaaaa");
10
echo 'Hello cfc4n',time();
到了这里,疑问1解决了。那么疑问2、3呢?程序里并没有输出“Expires”、“Cache-Control” http header的代码,这是谁输出的呢?既然是fpm响应的时候,就已经有了,那么是php的core模块,还是其他拓展模块输出的?我精简了代码,只输出一个“hello world”,发现也确实被缓存了。显然,php脚本程序中并没输出http header 的“Expires”、“Cache-Control”,多次测试,最终定位到session_start函数,翻阅源码找到了这些代码:
01
//ext/session/session.c line:1190 左右
02
// ...
03
CACHE_LIMITER_FUNC(private) /* {{{ */
04
{
05
ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
06
CACHE_LIMITER(private_no_expire)(TSRMLS_C);
07
}
08
/* }}} */
09
//再到这里3 或者上面几个 ##默认是nocache
10
CACHE_LIMITER_FUNC(nocache) /* {{{ */
11
{
12
ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
13
14
/* For HTTP/1.1 conforming clients and the rest (MSIE 5) */
15
ADD_HEADER("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
16
17
/* For HTTP/1.0 conforming clients */
18
ADD_HEADER("Pragma: no-cache");
19
}
20
/* }}} */
21
//这里2
22
static php_session_cache_limiter_t php_session_cache_limiters[] = {
23
CACHE_LIMITER_ENTRY(public)
24
CACHE_LIMITER_ENTRY(private)
25
CACHE_LIMITER_ENTRY(private_no_expire)
26
CACHE_LIMITER_ENTRY(nocache)
27
{0}
28
};
29
30
static int php_session_cache_limiter(TSRMLS_D) /* {{{ */
31
{
32
php_session_cache_limiter_t *lim;
33
34
if (PS(cache_limiter)[0] == '\0') return 0;
35
36
if (SG(headers_sent)) {
37
const char *output_start_filename = php_output_get_start_filename(TSRMLS_C);
38
int output_start_lineno = php_output_get_start_lineno(TSRMLS_C);
39
40
if (output_start_filename) {
41
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent (output started at %s:%d)", output_start_filename, output_start_lineno);
42
} else {
43
php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent");
44
}
45
return -2;
46
}
47
48
for (lim = php_session_cache_limiters; lim->name; lim++) {
49
if (!strcasecmp(lim->name, PS(cache_limiter))) {
50
lim->func(TSRMLS_C); //这里1
51
return 0;
52
}
53
}
54
55
return -1;
56
}
57
// ...
到了这里,知道原因了,是程序调用session_start时,php的session拓展自己输出的。session.cache_limit参数来决定输出包含哪种Expires的header,默认是nocache,修改php.ini的session.cache_limit参数为“none”即可让session模块不再输出这些http 响应头。或在调用session_start之前,使用session_cache_limiter函数来指定下该参数值。那为什么要在使用session时,发Expires、Cache-Control的http response header呢?我猜测了下,需要session时,基本上是用户跟服务器有交互,那么,既然有交互,就意味着用户的每次交互结果也可能不一样,就不能cache这个请求的结果,给返回给这个用户。同时,每个用户的交互结果都是不一样的,nginx也就不能把包含特殊Cache-Control的个人响应cache给其他人提供了。
还有一个无聊的问题“Expires:时间为何是Thu, 19 Nov 1981 08:52:00 GMT”?我翻阅了session.c这段代码的添加时间,版本,作者信息,在php官方版本库中找到了这次提交的信息:
Revision 17092 – (view) (download) (as text) (annotate) – [select for diffs]
Modified Sun Dec 12 14:16:55 1999 UTC (13 years, 2 months ago) by sas
File length: 28327 byte(s)
Diff to previous 16964
Add cache_limiter and cache_expire options. Rename extern_referer_check
to referer_check.
对比session.c两个版本的变更,果然是这块代码。作者是sas,也就是Sascha Schumann, http://php.net/credits.php里可以看到他的大名。关于这个expires过期时间的问题,有人在stackoverflow也提问过,Why is “Expires” 1981?,别人说那天是他生日。这是真的么?如果那天是他生日的话,而他增加session.cache_limiter时是1999年,他才17岁,17岁呀。我17岁时在干嘛?还不知道电脑长啥样,正在玩『超级玛丽』呢。
好奇的不是我一个人,还有个帖子是epoch date — Expires: Thu, 19 Nov 1981 08:52:00也问了。另外两个地址虽然没问,也有人提到那天是他生日了。http://boinc.berkeley.edu/dev/forum_thread.php?id=2514、https://github.com/codeguy/Slim/issues/157,这些帖子都提到说原帖是http://www.phpbuilder.com/lists/php3-list/199911/3159.php ,我无法访问,被跳转到首页了。用http://web.archive.org找到了历史快照,发现上下文关系不大,也不能证明是他生日。 我更是好奇的发了两封邮件到他的不同邮箱里问他,不过,目前他还没回复。或许他没收到、没看到,或许懒得回了。N年后,“Expires:时间为何是Thu, 19 Nov 1981 08:52:00 GMT”这个日期,会不会又成了一段奇闻佳话了呢?
发表评论
-
[nginx] nginx缓存cache的几种方式
2014-09-15 13:57 808原文地址:http://bbs.linuxtone.org/t ... -
NGINX的流媒体插件 nginx-rtmp-module
2014-09-15 12:03 648http://www.oschina.net/p/nginx- ... -
nginx 自定义 header
2014-09-15 11:56 794$http_HEADER The value of the H ... -
nginx反向代理proxy_set_header自定义header头无效
2014-09-15 11:52 1363原文地址:http://www.ttlsa.com/nginx ... -
nginx 自定义协议 扩展模块开发
2014-09-15 09:33 995原文地址:http://blog.chin ... -
Nginx模块开发入门
2014-09-14 10:05 636原文地址:http://kb.cnblogs.com/page ... -
nginx log_format 记录自定义header信息
2014-09-12 17:53 2233原文地址:http://notelifes.com/2013/ ... -
在Nginx中记录自定义Header
2014-09-12 17:48 839原文地址:http://gunner.me ... -
nginx log 记录请求的头信息
2014-09-12 14:44 1082记录访问的log,为了在 ... -
nginx官网文档地址
2014-09-10 10:33 571原文地址:http://wiki.nginx.org/Ngin ... -
Nginx配置反向代理时cache缓存的使用方法
2014-09-10 10:32 911原文地址:http://www.server110.com/n ... -
Windows下Nginx+PHP5(FastCgi)安装配置详解
2014-08-25 16:46 858源文地址:http://www.china ... -
nginx 多域名虚拟主机配置 (nginx如何绑定多个域名)
2014-08-25 14:33 396原文地址:http://wenku.baidu.com/lin ... -
实例讲解Nginx下的rewrite规则
2014-06-17 14:44 783一.正则表达式匹配, ... -
使用nginx的proxy_cache做网站缓存
2014-02-25 18:08 810使用nginx的proxy_cache做网站缓存 2012年1 ... -
nginx cache静态化+tmpfs 高性能cdn方案 原创-胡志广
2014-02-25 18:04 851nginx cache静态化+tmpfs 高性能cdn方案 原 ...
相关推荐
ngx_cache_purge 是 nginx 模块,此模块可以清理 nginx 的 FastCGI、proxy、 SCGI 和 uWSGI 的缓存。配置指令(相同位置语法)fastcgi_cache_purgesyntax: fastcgi_cache_purge on|off|<method> [from all|<ip> [.....
在这个配置中,我们定义了一个名为`my_cache`的缓存区域,并设置了一个专门用于清除缓存的Location `/purge`,只有特定的IP才能访问该URL来清除缓存。 通过这样的配置,当需要更新特定资源时,可以通过发送一个请求...
patch -p0 < ../nginx_upstream_check_module-master/check_1.9.2+.patch ./configure --prefix=/usr/local/nginx \ --with-http_ssl_module --with-http_stub_status_module --with-pcre \ --with-...
Nginx_mod_h264_streaming是Nginx的一个扩展模块,专为支持H.264编码的流媒体内容设计。该模块允许Nginx直接处理H.264编码的视频流,提供HTTP Live Streaming (HLS)或MPEG-DASH服务,这两种协议广泛应用于移动端和...
但是,原始的`upstream`模块并不具备健康检查功能,这就是`nginx_upstream_check_module`的用武之地。 ### Nginx Upstream Check Module特性 1. **健康检查**:该模块可以定期向后端服务器发送探测请求,如...
本文将深入探讨`nginx_upstream_hash-0.3.1`这个第三方模块的功能、配置以及实际应用场景。 `nginx_upstream_hash`模块是Nginx的扩展,它允许我们基于请求的某个特定部分(如URL、客户端IP等)生成哈希值,并以此...
proxy_cache配置项主要包含以下几个部分: 1. proxy_cache_path: 这是用于定义缓存文件存储路径的关键配置项,其中可以指定缓存根目录、缓存目录层级、共享内存区域、缓存最大空间、缓存文件的临时路径等参数。 - ...
在此记录下Nginx服务器nginx.conf的配置文件说明, 部分注释收集与网络. #运行用户 user www-data; #启动进程,通常设置成和cpu的数量相等 worker_processes 1; #全局错误日志及PID文件 error_log /var/log/...
标题 "nginx-1.0.12_ngx-cache-purge1.5.tar.gz" 指的是一个包含 Nginx 版本 1.0.12 与 ngx_cache_purge 模块 1.5 的软件包。这个压缩文件(.tar.gz格式)通常用于在 Linux 或类 Unix 系统中分发开源软件,因为它...
这个版本2.4.2的压缩包包含了ngx_cache_purge模块的所有源代码及相关文件,以便开发者在自己的Nginx环境中集成和使用。 Nginx是一款高性能、轻量级的Web服务器和反向代理服务器,广泛应用于互联网服务。其内置的...
在部署nginx_upstream_hash模块时,需要注意以下几点: 1. 配置文件中需要正确引入模块,通常在http、server或location上下文中启用。 2. 要确保所有参与哈希的变量在所有请求中都可预测,以避免请求被错误地路由...
ngx_req_status用来展示nginx请求状态信息,类似于apache的status,nginx自带的模块只能显示连接数等等信息,我们并不能知道到底有哪些请求、以及各url域名所消耗的带宽是多少。ngx_req_status提供了这些功能. 1、按...
nginx自带是没有针对负载均衡后端节点的健康检查的,但是可以通过默认自带的ngx_http_proxy_module 模块和ngx_http_upstream_module模块中的相关指令来完成当后端节点出现故障时,自动切换到健康节点来提供访问。
以上仅是Nginx配置的冰山一角,实际应用中还可以结合正则表达式、if条件语句、自定义模块等实现更多高级功能。理解并熟练掌握这些基本配置,能够帮助运维人员更好地管理和优化Nginx服务器,提升网站性能和稳定性。
在实际应用中,需要注意以下几点: 1. **性能优化**:由于Web推送可能导致大量并发连接,需要合理设置Nginx的连接数限制和缓冲区大小,避免服务器资源耗尽。 2. **安全性**:确保推送的数据经过加密,防止中间人...
现在,我们来详细探讨FastDFS-nginx-module的几个关键知识点: 1. **FastDFS概述**:FastDFS是一个高性能、轻量级的分布式文件系统,主要解决大容量存储和负载均衡问题。它支持文件上传、下载、删除等操作,并且...
安装fastdfs-nginx-module_v1.16.tar.gz的过程包括以下几个步骤: 1. 解压源码包:使用tar命令解压fastdfs-nginx-module_v1.16.tar.gz,得到源代码目录。 2. 配置编译环境:确保系统已经安装了Nginx和FastDFS,并且...
`nginx_upload_module`是一个用于Nginx服务器的第三方模块,它允许处理和存储上传的文件。结合Lua脚本,我们可以实现更灵活和强大的上传功能。这篇博文(链接:)可能详细介绍了如何在Nginx中集成`nginx_upload_...
tar zxvf Nginx_upstream_hash-0.3.tar.gz ``` 3. **创建Nginx用户** 创建一个名为nginx的用户,通常用于运行Nginx进程,以提高安全性。 ``` useradd nginx ``` 4. **安装PCRE库** 编译并安装PCRE库,它是...