最近给畅言加上了单点登录,也就是可以用网站的帐号来在畅言上发射评论。畅言会通过你设置的一个接口来获得用户名,头像等。但是我发现
虽然在php层有redis,压力不会很大,但是这样频繁的请求还是会中防CC的策略,影响用户正常的浏览。所以我决定在CDN上做个缓存。
对于请求/user/userinfo.php?callback=abcde,响应应该为:
abcde({一个json})
但是jquery生成的JSONP请求的callback是随机生成的,所以每次的响应需要根据这个callback的值来变化,没法直接缓存整个结果。
于是我们把proxy_cache_key设置成只与和用户相关的cookie有关,下面的例子里是cookie_usr,也就是客户端发送的Cookie: usr=xxxx。对于大多数未用户是未登录的,可以共享同一个cache。
然后我们用body_filter_by_lua*来操作响应。因为body_filter_by_lua*执行的阶段是在output-body-filter,是在proxy_pass和proxy_cache之后,所以我们可以用它来修改读取到缓存的内容再输出。另外用head_filter_by_lua*来修改Content-Type,使不带callback时返回的是一个json。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
location ~ /user/userinfo.php { set $_userinfo_bked http://backend/userinfo.php; proxy_pass $_userinfo_bked; proxy_cache_key userinfo-$cookie_usr; proxy_cache_valid 200 204 301 302 1h; proxy_cache_valid 404 500 502 503 504 1m; proxy_cache cache_main; proxy_set_header Host $http_host; proxy_ignore_headers Set-Cookie; header_filter_by_lua_block { if ngx.var.arg_callback ~= nil then ngx.header['content-type'] = "application/javascript" else ngx.header['content-type'] = "application/json" end } set $_userinfo_buf_start 0; body_filter_by_lua_block { if ngx.var.arg_callback ~= nil then if ngx.var._userinfo_buf_start == "0" then ngx.arg[1] = ngx.var.arg_callback .. "(" .. ngx.arg[1] ngx.var._userinfo_buf_start = 1 end if ngx.arg[2] then ngx.arg[1] = ngx.arg[1] .. ")" end end } } |
proxy_cache_valid和proxy_ignore_headers根据实际情况可能会影响是否缓存,所以必要的时候要加上。
另外,因为body_filter_by_lua*是unbuffered的模式,每次有一个chunk到达output-body-filter阶段,这个filter就会被执行一次。所以我们得自己维护一个状态来记录当前的位置,因为我们只想在整个响应的头上和尾巴上加上字符串。其中ngx.arg[2]在输出到达结尾的时候为true,可以通过判断它来决定要不要加上末尾的括号。
另外,我们还可以用子请求的方式来完成这个功能:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
location = /userinfo { internal; proxy_pass http://backend/userinfo.php; proxy_set_header Host $http_host; proxy_cache_key userinfo-$cookie_u2; proxy_cache_valid 200 204 301 302 15m; proxy_cache_valid 404 500 502 503 504 1m; proxy_ignore_headers Set-Cookie; proxy_cache sht_cache_main; } location ~ /user/userinfo.php { content_by_lua_block { local ret = "" local res = ngx.location.capture("/userinfo", {share_all_vars = true}) ret = res.body if ngx.var.arg_callback ~= nil then ngx.say(ngx.var.arg_callback .. "(" .. ret .. ")") else ngx.say(ret) end } } |