最近給暢言加上了單點登錄,也就是可以用網站的帳號來在暢言上發射評論。暢言會通過你設置的一個接口來獲得用戶名,頭像等。但是我發現隊友暢言會頻繁請求這個接口,有時候會達到單個用戶一秒鐘好幾次??

雖然在php層有redis,壓力不會很大,但是這樣頻繁的請求還是會中防CC的策略,影響用戶正常的瀏覽。所以我決定在CDN上做個緩存。

對於請求/user/userinfo.php?callback=abcde,響應應該為:

abcde({一個json})

但是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,可以通過判斷它來決定要不要加上末尾的括號。

另外,我們還可以用子請求的方式來完成這個功能: