最近给畅言加上了单点登录,也就是可以用网站的帐号来在畅言上发射评论。畅言会通过你设置的一个接口来获得用户名,头像等。但是我发现队友畅言会频繁请求这个接口,有时候会达到单个用户一秒钟好几次??

虽然在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。

proxy_cache_valid和proxy_ignore_headers根据实际情况可能会影响是否缓存,所以必要的时候要加上。

另外,因为body_filter_by_lua*是unbuffered的模式,每次有一个chunk到达output-body-filter阶段,这个filter就会被执行一次。所以我们得自己维护一个状态来记录当前的位置,因为我们只想在整个响应的头上和尾巴上加上字符串。其中ngx.arg[2]在输出到达结尾的时候为true,可以通过判断它来决定要不要加上末尾的括号。

另外,我们还可以用子请求的方式来完成这个功能: