nginx的cookie缓存问题及配置文件研究

 其实我很早就在考虑这个问题,nginx既然能缓存,为什么用户和用户间的缓存不会串呢?

直到OpenCDN的用户反馈上来存在用户和用户间的缓存互串问题,我才去研究。

首先,要注意的一点,nginx默认的缓存是不会考虑到cookie的,只根据URI,从配置文件的这条就一目了然了。

proxy_cache_key "$host$uri$is_args$args";

用于存储的key是用根据URL然后进行散列化生成的,而这个key和cookie无关。 这种配置下,两个cookie不同的用户缓存的key是一样的。于是我们就会想,应该如何让缓存和cookie相关呢?

1.proxy_cache_key加入cookie

然后去nginx的wiki上了这么一段配置文件

proxy_cache_key "$host$request_uri$cookie_user";

这个配置文件还存在一定的猫腻,这个cookie_user我一开始就简单的是用户的cookie,后来我才发现,我太天真了。原来user是字段名,这个指的是cookie中key为user那段对应的value。天呐,我怎么会知道一个CDN用户的网站的cookie中有哪些cookie字段? 所以,这个$cookie_xxx变量是必须要事先知道cookie的字段的,而$http_ccokie则不是,它里面存放的是完整的cookie值,而不是其中一个。

proxy_cache_key "$host$request_uri$http_cookie";


那么。是不是配置文件这样写就OK了呢?很好地让cookie成为了影响key的变量,这样两个cookie不用的用户缓存就不同了。其实,这才是麻烦的开始。

1.一旦采用这种方式来存放缓存,会把整个网站内的所有缓存全部割裂,包括图片(图片也携带cookie),一张图片100用户就因为cookie的不同要存放100份。

2.一些捣蛋份子会影响cookie,比如google分析,google分析会在网站中加入cookie来方便他追踪用户的访问记录。而浏览器在发包的时候,这个cookie也被携带着发给了nginx。于是一个静态站点也可能因为站点统计的原因,变成一个像携带着cookie的动态站,并且如果采用上面这条配置,所以的静态文件都会因为cookie而被割裂N份。

当然,似乎google统计还是为我们考虑过一些的,遵循一个自己YY的小规范:所有的key全部用下划线开头,以示区别。一开始我还以为用这种方式可以将cookie进行清理,只留下网站中真正影响登录状态的cookie,但是百度统计的BAIDUID的出现让我发现这只是一种美好的幻想,唉。而且还有第1点在,让整个cookie缓存方案陷入僵局。

小结:

一般情况下不推荐用cookie作为cache key进行存储,代价太大,简直让CDN服务器成了浏览器的后花园。而我们要做的是公园!!

不过,如果一个站点如果已经把图片、css这类的静态资源和主站分离,倒可以考虑用下这种方法。


2.通过mime类型判断是否缓存

好,让我们继续探索之旅,那么对于存在cookie的,到底要如何来做呢?贝奇哥给了我一种方案,所有的mime类型为image、text/css等等的这些静态文件,可以进行忽略cookie的缓存,而其他的全部回源。虽然我对于这种方案并不是很满意,但是还是去试了一下。结果不试不知道,一试却发现好多东西。

首先我写了这样一段来进行判断。

set $a 1;
if ($upstream_http_content_type ~* "image/"){
	set $a 0;
}
proxy_cache_bypass $a;


结果这段配置整整调了一个下午还是没有生效。后来发现$upstream_http_content_type居然是空的,百思不得其解。后来google groups上有tengine的文景大牛做了很言简意赅的解释,恍然大悟。

Nginx有请求的各个处理阶段,依次执行 (http://tengine.taobao.org/book/chapter_02.html#id10):


Rewrite
Access
Content
Log


if对应的是Rewrite阶段。$upstream_xxx这个变量是在Content执行阶段拿到后端响应的内容才可以使用,所以它在 Rewrite阶段是拿不到变量的,一般只能在记录访问日志的Log阶段才能用得到。


唉,我又天真了一次,我在rewrite阶段 YY Content阶段才有的东西,这种判断的写法是不行的。但是真的没有办法了吗?我们现在要解决的问题是如何在content阶段执行判断,而if是不能依靠。我找啊找啊,居然还真的被我找到了一个可以用的东西,他叫map,直接上春哥写的文档的片段 http://openresty.org/download/agentzh-nginx-tutorials-zhcn.html


在上面的例子中,我们还应当注意到 map 指令是在 server 配置块之外,也就是在最外围的 http 配置块中定义的。很多读者可能会对此感到奇怪,毕竟我们只是在 location /test 中用到了它。这倒不是因为我们不想把 map 语句直接挪到 location 配置块中,而是因为 map 指令只能在 http 块中使用!

很多 Nginx 新手都会担心如此“全局”范围的 map 设置会让访问所有虚拟主机的所有 location 接口的请求都执行一遍变量值的映射计算,然而事实并非如此。前面我们已经了解到 map 配置指令的工作原理是为用户变量注册 “取处理程序”,并且实际的映射计算是在“取处理程序”中完成的,而“取处理程序”只有在该用户变量被实际读取时才会执行(当然,因为缓存的存在,只在请求生命期中的第一次读取中才被执行),所以对于那些根本没有用到相关变量的请求来说,就根本不会执行任何的无用计算。

这种只在实际使用对象时才计算对象值的技术,在计算领域被称为“惰性求值”(lazy evaluation)。提供“惰性求值” 语义的编程语言并不多见,最经典的例子便是 Haskell. 与之相对的便是“主动求值” (eager evaluation)。我们有幸在 Nginx 中也看到了“惰性求值”的例子,但“主动求值”语义其实在 Nginx 里面更为常见。


于是,我们可以先写一个map在外面,然后利用惰性求值的特性再让他在content阶段才进行执行,同时由于map的缓存特性,在第一次求值完就可以固化住,我们也很容易验证map的判断效果,只要在配置文件里面加一条add_header把它打印出来就可以了。

http{
	...
	map $upstream_http_content_type $a{
		default			"1";
		"^*image/"		"0";
	}
}

location {
	...
	proxy_cache_bypass $a;
}

本来以为这样配置就搞定了,但是这次研究真心一点都不顺利啊。$upstream_http_content_type居然还是空的!这个时候我已经很淡定了,对这个空值也不惊慌,继续找文章。果然,找到了。http://nginx.2469901.n2.nabble.com/Cache-by-mime-type-td7580925.html

原来要使用proxy_no_cache来替代proxy_cache_bypass,那么试试吧。居然真的奏效了!

http{
	...
	map $upstream_http_content_type $a{
		default			"1";
		"^*image/"		"0";
	}
}

location {
	...
	proxy_no_cache $a;
}

然后我开始思考,为什么两个功能几乎一样的函数却不一样,唯一的猜想是,一个函数(proxy_cache_bypass)是反代前执行的,一个函数(proxy_no_cache)是反代后执行的,只有反代后才有$upstream_xxx变量,而并不是简单地在content阶段都有。但是让人郁闷的是,在nginx执行的时候会出警告,说proxy_no_cache功能被改变,建议和proxy_cache_bypass一起执行,但是如果两个一起执行的话,由于nginx惰性求值的缓存特性,必然是proxy_cache_bypass先被执行,然后被固化到了一个空值,然后proxy_no_cache再执行也是空值了,这个还待请教大牛。

小结:

通过map的惰性求值,使得content阶段也可以执行判断,解决问题。

关键点:必须要这个函数是在upstream后执行才能有效,如果这个函数在upstream前执行的话,那么一定是无解的。

大哭我才不会告诉你我本来想用mime类型来控制cache key的,在mime类型是image的时候,cache key不加cookie,而除此之外,cache key加入cookie。于是最要命的一点是prxoy_cache_key这个函数是upstream前执行的(因为毕竟要查缓存是否命中么)。然后这个逻辑就变成了颠倒了因果的逻辑:只有发出包才能获得mime类型,而如果不用cache_key来查缓存就发不出包。


3.奥卡姆剃刀

但是如果是只缓存image等类型的静态文件,这样的话cache-control和expires不就几乎没有用了吗?这个方案还是无法满足我。于是我继续探寻。这个时候才发现,似乎已经无路可走。要么做后花园(方案1),要么做大公园(方案2),根据cache-control等header头等来缓存的自助公园呢?(我自己杜撰的名词,不过大家应该可以理解我的意思吧)。

于是开始思路逆行,什么情况下才需要这么纠结于cookie呢?

有用户登录的情况。那么有用户登录的网站又有几类呢?

1.直接用现成的整站方案(Discuz!等)。2.找人或者自己开发的。

如果是第一类网站,需要我们担心吗?不需要!这种网站稳健地很!各种header头一应俱全,不该缓存的页面nginx也是绝对不会缓存的。

如果是第二类网站,需要我们担心吗?需要。那么要那担心什么呢?担心header头不标准吗?不担心!因为这类网站常常什么header都没有,反倒也不会被缓存。那么哪些是需要我们担心的?那些自己做的网站,又输出了错误了的header头,比如在私人页面上面输出缓存标记的。这样的网站多吗?不多!那么既然网站是你写的,解铃还须系铃人。自己乖乖地到CDN上来配你的缓存规则!

那么,还有什么cookie的缓存问题呢?没有了!

那我配置文件需要做什么改动吗?不用做任何改动!直接按照默认的来就行!

那你还让我读到这里,不会一开始就告诉我啊?发火  

好吧。。我错了。。。




  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
第1部分 Nginx服务器 第1章 Nginx的功能 第2章 Nginx的模块管理和进程管理 第3章 Nginx如何处理一个请求 第4章 服务器名字 第5章 协助用户操作Nginx的工具 第6章 5XX错误处理 第7章 使用TCMalloc优化Nginx 第8章 PCRE正则表达式 第9章 Nginx高可用的实现 第10章 10个QA 第2部分 Nginx服务器的功能 第11章 限制流量 第12章 限制用户并发连接数 第13章 修改或隐藏Nginx的版本号 第14章 配置FLV服务器 第15章 Nginx的访问控制 第16章 提供FTP下载 第17章 Nginx与编码 第18章 网页压缩传输 第19章 控制Nginx如何记录日志 第20章 map模块的使用 第21章 Nginx预防应用层DDoS攻击 第22章 为Nginx添加、清除或改写响应头 第23章 重写URI 第24章 Nginx与服务器端包含 第25章 Nginx与X-Sendfile 第26章 在Nginx的响应体之前或之后添加内容 第27章 Nginx与访问者的地理信息 第28章 Nginx的图像处理 第29章 location中随机显示文件 第30章 后台Nginx服务器记录原始客户端的IP地址 第31章 解决防盗链 第32章 Nginx提供HTTPS服务 第33章 监控Nginx的工作状态 第34章 使用empty_gif 第35章 Nginx对响应体内容的替换 第36章 Nginx的WebDAV 第37章 Nginx的Xslt模块 第38章 Nginx的基本认证方式 第39章 Nginxcookie 第40章 Nginx基于客户端请求头的访问分类 第41章 通过Upstream模块使得Nginx实现后台服务器集群 第42章 根据浏览器选择主页 第43章 关于Nginx提供下载.ipa或.apk文件的处理方法 第44章 SCGI 第45章 Expires与ETag 第46章 使用upstream_keepalive模块实现keep-live 第47章 后台服务器的健康检测 第48章 使用sticky模块实现粘贴性会话 第49章 Nginx实现对后台服务器实现“公平”访问 第50章 Nginx使用redis数据库 第51章 Nginx访问MongoDB 第52章 Nginx访问Mogilefs 第3部分 Nginx缓存 第53章 缓存技术――proxy_cache 第54章 缓存技术――proxy_store 第55章 缓存技术――Memcached 第56章 缓存技术――NCACHE 第57章 缓存技术――Varnish
根据引用\[1\]中的配置文件,可以看出nginx缓存配置如下: - 使用代理服务器作为缓存服务器,代理服务器的IP地址为121.5.180.193。 - 缓存路径为/etc/nginx/cache_temp,缓存级别为2:2,缓存区域为cache_zone,缓存大小为2g,非活跃时间为60分钟,不使用临时路径。 - 缓存服务器的上游服务器为121.42.11.34:1010和121.42.11.34:1020。 - 监听80端口,服务器名称为cache.lion.club。 - 配置了location指令,将请求代理转发到cache_server,并设置了缓存状态为200的请求缓存时长为5分钟,缓存文件的key为请求的URI,将缓存状态设置为响应头部信息。 根据引用\[2\]和引用\[3\]中的命令,可以通过执行curl命令来验证nginx缓存是否配置成功。例如,可以执行以下命令来验证缓存配置是否生效: - 验证POST请求的缓存配置:curl -s -d postvalue=123 http://SERVER-IP:12345/post - 验证GET请求的缓存配置:curl -s http://SERVER-IP:12345/ping?param=778 请注意,上述命令中的SERVER-IP应替换为实际的服务器IP地址,12345应替换为实际的端口号。执行命令后,可以查看6081端口服务的日志,以确认nginx缓存是否配置成功。 #### 引用[.reference_title] - *1* [Nginx缓存配置详解](https://blog.csdn.net/xiaochao_123/article/details/123640451)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] - *2* *3* [配置 Nginx 缓存](https://blog.csdn.net/yuntaoren/article/details/128045932)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^insertT0,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值