2009年8月28日 星期五

app engine 自建"Last-Modified" 的快取控制

一般來說在網頁伺服器中 為了節省頻寬
如果是存取靜態的檔案 Server端會主動加上header ['Last-Modified'] 紀錄上次的修改時間
而Client端的瀏覽器收到帶有['Last-Modified']的檔案 下次再向同一網址要求檔案時
會自動加上header ['If-Modified-Since'] , ( ['If-Modified-Since'] 的值就是最近一次從Server端回應中得到的['Last-Modified'] 的值)
所以Server端檢查這兩個時間就可以知道Client端所保留的快取是不是最新版本
如果是最新版那就不用回傳值 直接返回http代碼304 這樣使用者端的瀏覽器就會用自身的快取顯示

在app engine中預設沒有支援這樣的機制 因為所有的回傳都是app engine動態程式碼產生的
不過透過以下這樣的程式碼可以自己在app engine上實現這樣的機制 減低要處理的流量
(尤其現在app engine流量每天都只有1g 要節省用)

#範例是一個產生圖片的web service
#服務本身有更新時 會將最後修改的時間ㄏ一併寫入到memcache裡
#每次接收請求都會檢查最後的更新時間 如果沒有更新版本 就叫使用者端用自身的快取
if self.request.get("action")=="getimg" and self.request.get("img")!="":
lastmodify_date = memcache.get("updatetime") + datetime.timedelta(hours=-8)
lastmodify_str = lastmodify_date.strftime("%a, %d %b %Y %H:%M:%S GMT")
if self.request.headers.has_key("If-Modified-Since"): #檢查快取時間
modifysince_str = self.request.headers.get("If-Modified-Since").split(";")[0]
modifysince_date = datetime.datetime.strptime(modifysince_str, "%a, %d %b %Y %H:%M:%S GMT")
if lastmodify_date <= modifysince_date + datetime.timedelta(seconds=1):
self.error(304)
return
self.response.headers['Content-Type'] = "image/png"
self.response.headers['Last-Modified'] = lastmodify_str
self.response.out.write(memcache.get(self.request.get("img")))