Nginx模块fastcgi_cache的几个注意点 转

Nginx模块fastcgi_cache的几个注意点

去年年底,我对nginx的fastcgi_cache进行摸索使用。在我的测试过程中,发现一些wiki以及网络上没被提到的注意点,这里分享一下。

在web项目中,大家都已经非常熟悉其架构流程了。都说Cache是万金油,哪里不舒服抹哪里。这些流程中,几乎每个环节都会进行cache。从浏览器到webserver,到cgi程序,到DB数据库,会进行浏览器cache,数据cache,SQL查询的cache等等。对于fastcgi这里的cache,很少被使用。去年年底,我对nginx的fastcgi_cache进行摸索使用。在我的测试过程中,发现一些wiki以及网络上没被提到的注意点,这里分享一下。

从浏览器到数据库的流程图

这里是我的NGinx配置信息

  1. #增加调试信息
  2. add_header X-Cache-CFC "$upstream_cache_status - $upstream_response_time";
  3. fastcgi_temp_path /dev/shm/nginx_tmp;
  4. #cache设置
  5. fastcgi_cache_path   /dev/shm/nginx_cache  levels=1:2 keys_zone=cfcache:10m inactive=50m;
  6. fastcgi_cache_key "$request_method://$host$request_uri";
  7. fastcgi_cache_methods GET HEAD;
  8. fastcgi_cache   cfcache;
  9. fastcgi_cache_valid   any 1d;
  10. fastcgi_cache_min_uses  1;
  11. fastcgi_cache_use_stale error  timeout invalid_header http_500;
  12. 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,一次也没命中

程序代码是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. //strace -ff -tt -s 1000 -o xxx.log -p Nginx-PID
  5. 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参数使用的地方,在httpngx_http_upstream.c找到了。虽然不能很流程的读懂nginx的代码,但粗略的了解,根据了解的情况加以猜测,再动手测试实验,也得出了结论,确定了nginx的 fastcgi_cache的规则。

  1. //ngx_http_upstream.c
  2. //line 3136  当fastcgi响应包含set-cookie时,不缓存
  3. static ngx_int_t
  4. ngx_http_upstream_process_set_cookie(ngx_http_request_t *r, ngx_table_elt_t *h,
  5. ngx_uint_t offset)
  6. {
  7. #if (NGX_HTTP_CACHE)
  8. ngx_http_upstream_t  *u;
  9. u = r->upstream;
  10. if (!(u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_SET_COOKIE)) {
  11. u->cacheable = 0;
  12. }
  13. #endif
  14. return NGX_OK;
  15. }
  16. //line 3242 当响应头包含Expires时,如果过期时间大于当前服务器时间,则nginx_cache会缓存该响应,否则,则不缓存
  17. static ngx_int_t
  18. ngx_http_upstream_process_expires(ngx_http_request_t *r, ngx_table_elt_t *h,
  19. ngx_uint_t offset)
  20. {
  21. ngx_http_upstream_t  *u;
  22. u = r->upstream;
  23. u->hheaders_in.expires = h;
  24. #if (NGX_HTTP_CACHE)
  25. {
  26. time_t  expires;
  27. if (u->conf->ignore_headers & NGX_HTTP_UPSTREAM_IGN_EXPIRES) {
  28. return NGX_OK;
  29. }
  30. if (r->cache == NULL) {
  31. return NGX_OK;
  32. }
  33. if (r->cache->valid_sec != 0) {
  34. return NGX_OK;
  35. }
  36. expires = ngx_http_parse_time(h->value.data, h->value.len);
  37. if (expires == NGX_ERROR || expires < ngx_time()) {         u->cacheable = 0;
  38. return NGX_OK;
  39. }
  40. r->cache->valid_sec = expires;
  41. }
  42. #endif
  43. return NGX_OK;
  44. }
  45. //line 3199  当响应头包含Cache-Control时,#####如果####这里有如果啊。。。
  46. //【注意】如果Cache-Control参数值为no-cache、no-store、private中任意一个时,则不缓存...不缓存...
  47. //【注意】如果Cache-Control参数值为max-age时,会被缓存,且nginx设置的cache的过期时间,就是系统当前时间 + mag-age的值
  48. if (ngx_strlcasestrn(p, last, (u_char *) "no-cache", 8 - 1) != NULL
  49. || ngx_strlcasestrn(p, last, (u_char *) "no-store", 8 - 1) != NULL
  50. || ngx_strlcasestrn(p, last, (u_char *) "private", 7 - 1) != NULL)
  51. {
  52. u->cacheable = 0;
  53. return NGX_OK;
  54. }
  55. p = ngx_strlcasestrn(p, last, (u_char *) "max-age=", 8 - 1);
  56. if (p == NULL) {
  57. return NGX_OK;
  58. }
  59. ...
  60. 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 hit命中

FASTCGI_CACHE $upstream_cache_status 结果为miss,一次也没命中。

  1. //逐个测试,测试时,注释其他的
  2. header("Expires: ".gmdate("D, d M Y H:i:s", time()+10000).‘ GMT‘);
  3. header("Expires: ".gmdate("D, d M Y H:i:s", time()-99999).‘ GMT‘);
  4. header("X-Accel-Expires:30");
  5. header("Cache-Control: no-cache");
  6. header("Cache-Control: no-store");
  7. header("Cache-Control: private");
  8. header("Cache-Control: max-age=10");
  9. 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函数,翻阅源码找到了这些代码:

  1. //ext/session/session.c  line:1190 左右
  2. // ...
  3. CACHE_LIMITER_FUNC(private) /* {{{ */
  4. {
  5. ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
  6. CACHE_LIMITER(private_no_expire)(TSRMLS_C);
  7. }
  8. /* }}} */
  9. //再到这里3 或者上面几个 ##默认是nocache
  10. CACHE_LIMITER_FUNC(nocache) /* {{{ */
  11. {
  12. ADD_HEADER("Expires: Thu, 19 Nov 1981 08:52:00 GMT");
  13. /* For HTTP/1.1 conforming clients and the rest (MSIE 5) */
  14. ADD_HEADER("Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0");
  15. /* For HTTP/1.0 conforming clients */
  16. ADD_HEADER("Pragma: no-cache");
  17. }
  18. /* }}} */
  19. //这里2
  20. static php_session_cache_limiter_t php_session_cache_limiters[] = {
  21. CACHE_LIMITER_ENTRY(public)
  22. CACHE_LIMITER_ENTRY(private)
  23. CACHE_LIMITER_ENTRY(private_no_expire)
  24. CACHE_LIMITER_ENTRY(nocache)
  25. {0}
  26. };
  27. static int php_session_cache_limiter(TSRMLS_D) /* {{{ */
  28. {
  29. php_session_cache_limiter_t *lim;
  30. if (PS(cache_limiter)[0] == ‘\0‘) return 0;
  31. if (SG(headers_sent)) {
  32. const char *output_start_filename = php_output_get_start_filename(TSRMLS_C);
  33. int output_start_lineno = php_output_get_start_lineno(TSRMLS_C);
  34. if (output_start_filename) {
  35. 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);
  36. } else {
  37. php_error_docref(NULL TSRMLS_CC, E_WARNING, "Cannot send session cache limiter - headers already sent");
  38. }
  39. return -2;
  40. }
  41. for (lim = php_session_cache_limiters; lim->name; lim++) {
  42. if (!strcasecmp(lim->name, PS(cache_limiter))) {
  43. lim->func(TSRMLS_C);   //这里1
  44. return 0;
  45. }
  46. }
  47. return -1;
  48. }
  49. // ...

到了这里,知道原因了,是程序调用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=2514https://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”这个日期,会不会又成了一段奇闻佳话了呢?

原文链接:http://www.cnxct.com/several-reminder-in-nginx-fastcgi_cache-and-php-session_cache_limiter/

时间: 2024-10-05 07:23:56

Nginx模块fastcgi_cache的几个注意点 转的相关文章

FastDFS的php和nginx模块配置

一.FastDFS和php整合 1.安装php # 安装依赖包 yum -y install gcc gcc-c++ autoconf libjpeg libjpeg-devel libpng libpng-devel freetype freetype-devel libxml2 libxml2-devel zlib zlib-devel glibc glibc-devel glib2 glib2-devel bzip2 bzip2-devel ncurses ncurses-devel cu

【重要】Nginx模块之————Lua-Resty-Redis的参数介绍 (Lua-Nginx-Module 模块的Redis客户端驱动程序)

一.描述 这个Lua库是ngx_lua nginx模块的Redis客户端驱动程序:https://github.com/openresty/lua-nginx-module/#readme,这个Lua库利用ngx_lua的cosocket API,确保100%的非阻塞行为.请注意,至少需要ngx_lua 0.5.14或OpenResty 1.2.1.14. 二.方法介绍 除了所有的小写字母外,所有的Redis命令都有自己的方法.您可以在这里找到完整的Redis命令列表:http://redis.

nginx模块开发获取post参数

> 您好!>     我想请问下nginx模块里面怎么获取post参数,能有具体的代码更好!谢谢> 对于 "application/x-www-form-urlencoded" 格式的 POST 参数获取,可以参考 ngx_lua 模块的ngx.req.get_post_args() 函数以及 ngx_form_input 模块的实现: http://wiki.nginx.org/HttpLuaModule#ngx.req.get_post_args https://

nginx 模块nginx_upstream_check_module

我这里用到的nginx为最新版的nginx 所以我使用了最新的插件 nginx_upstream_check_module-master.zip cd nginx-1.7.1 patch -p1 </tmp/nginx_upstream_check_module-master/check_1.5.12+.patch nginx -V ./configure --prefix=/usr/local/nginx --with-http_stub_status_module --with-http_s

Nginx模块开发入门(转)

前言 Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%.与Apache相比,Nginx在高并发情况下具有巨大的性能优势. Nginx属于典型的微内核设计,其内核非常简洁和优雅,同时具有非常高的可扩展性.Nginx最初仅仅主要被用于做反向代理,后来随着HTTP核心的成熟和各种HTTP扩展模块的丰富,Nginx越来越多被用来取代Apache而单独承担HTTP Server的责任,例如目前淘宝内

linux下nginx模块开发入门

本文模块编写参考http://blog.codinglabs.org/articles/intro-of-nginx-module-development.html 之前讲了nginx的安装,算是对nginx有了最初步的了解,在配置完之后,我们就可以进行简单的nginx模块开发了. 下面本文展示一个简单的Nginx模块开发全过程,我们开发一个叫echo的handler模块,这个模块功能非常简单,它接收“echo”指令,指令可指定一个字符串参数,模块会输出这个字符串作为HTTP响应.例如,对ngi

Nginx模块开发入门

前言 Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%.与Apache相比,Nginx在高并发情况下具有巨大的性能优势. Nginx属于典型的微内核设计,其内核非常简洁和优雅,同时具有非常高的可扩展性.Nginx最初仅仅主要被用于做反向代理,后来随着HTTP核心的成熟和各种HTTP扩展模块的丰富,Nginx越来越多被用来取代Apache而单独承担HTTP Server的责任,例如目前淘宝内

Nginx模块开发入门(转)

前言 Nginx是当前最流行的HTTP Server之一,根据W3Techs的统计,目前世界排名(根据Alexa)前100万的网站中,Nginx的占有率为6.8%.与Apache相比,Nginx在高并发情况下具有巨大的性能优势. Nginx属于典型的微内核设计,其内核非常简洁和优雅,同时具有非常高的可扩展性.Nginx最初仅仅主要被用于做反向代理,后来随着HTTP核心的成熟和各种HTTP扩展模块的丰富,Nginx越来越多被用来取代Apache而单独承担HTTP Server的责任,例如目前淘宝内

nginx源码分析--nginx模块解析

nginx的模块非常之多,可以认为所有代码都是以模块的形式组织,这包括核心模块和功能模块,针对不同的应用场合,并非所有的功能模块都要被用到,附录A给出的是默认configure(即简单的http服务器应用)下被连接的模块,这里虽说是模块连接,但nginx不会像apache或lighttpd那样在编译时生成so动态库而在程序执行时再进行动态加载,nginx模块源文件会在生成nginx时就直接被编译到其二进制执行文件中,所以如果要选用不同的功能模块,必须对nginx做重新配置和编译.对于功能模块的选