如何集成varnish到已有的網站架構
如何集成varnish到已有的網站架構
在我們現有的架構中通常是已經成熟穩定的架構,如何將高性能的緩存服務器部署在已有的環境上呢,同時部署容易,如何始終讓用戶看到的是最新的內容,即便是緩存命中的狀態?
因此,我們直接解決上面兩個問題!
如何將高性能的緩存服務器部署在已有的環境上呢?
通過實際使用情況,將varnish部署上線通常有兩種拓撲圖:
1、user---->web--->varnish--->application_server 這種情況更適合網站已經使用了cdn的情況,varnish主要充當緩存網頁頁面的功能。
or
2、user---->varnish--->web--->application_server 這種情況適合通常性的小網站,并沒有富裕的資金,或者說為了充分的節約開支,畢竟如果效果理想,這部分的資源完全可以作為程序員薪水的部分(wan quan bu ke neng!)
對于如何安裝varnish,請參考官方文檔或者之前的blog。兩種方式無外乎在于如何調度網站流量,以及配置源server而已。
如何始終讓用戶看到的是最新的內容,即便是緩存命中的狀態?
有了上面的兩種結構,我們來說如何更新緩存的問題,這也是基本所有問題的所在。
舉個實際例子,通常作為一個cms站點,都會有訪問域名(www)和管理域名(console),這里我們簡稱前臺(客服妹子)和后臺(diaosi chengxuyuan),并且使用上面的第二種架構。 那么我們的架構就是這樣的:
前 臺用戶通過internet訪問您的網站,首先經過varnish的處理,如果cache中hit,ok,返給用戶,訪問結束,如果miss,進行和之前 沒有使用varnish的訪問流程。(注:此處只畫出必要的部分。當然像redis緩存等其他組件這些東西包含在上面的架構也是沒有任何問題的。)
后臺編輯用戶,通過后臺發布更新,操作數據,并且更新到數據庫中。
現在問題來了(wa jue ji shu na jia qiang?!):
在沒有使用緩存時,用戶每次都通過應用服務器獲取到最新的內容。后臺修改也是直接更新到數據庫。 對比 使用緩存后,實際某篇文章被緩存后,并且在有效期內,用戶的請求是不會到達web服務器,更別說應用服務器。這就是說即便你的編輯在后臺更新了十萬次該文章,如果文章仍然在緩存期內,前臺用戶是不會有任何變化。
解決這個問題通常有兩種方式:
1、對緩存對象設置一個較小的緩存失效時間。(被動更新) 2、通過varnish的接口對已經緩存的對象進行操作。(主動更新)
可以衡量二者的優劣,
被動更新簡單維護量小會存在用戶得到非最新資源的情況; 主動更新效果顯著維護量較被動更新更大,不過向筆者這種大神級別高端玩家(da gao wan)來說,被動更新顯然是不能忍受的額。
實際例子
實際上我們通過后臺管理系統可以向varnish發送更新某資源的請求即可,如下圖:
具體到配置:我們的前后臺web服務器均使用openresty,具體就不多介紹了,引用一句話"春哥是我見過開源精神最強的人"
首 先對于我們需要修改的文章都是有對應的文章id,在后臺編輯更新都會發起post請求,或者get請求。通過后臺openresty在收到更新文章請求 時,發送一個同步的非阻塞的子請求到前臺varnish,并在varnish中配置用于接收該請求,執行varnish的ban操作。
我們來看配置文件。
后臺的openresty:
server { listen 80; server_name console.example.com; location ~ /articles/(\d+)/[change_status|edit] { error_log logs/error.log debug; proxy_set_header X-Real-IP $http_x_forwarded_for; proxy_set_header X-Forwarded-For $http_x_forwarded_for; proxy_set_header Host $host; set $article_id $1; rewrite_by_lua ' ngx.location.capture("/console.example.com/foo/"..ngx.var.article_id, { args = { cmd = "update" } }) '; proxy_pass http://127.0.0.1:9090; }
當然這只是最核心能說明問題的代碼,不了解的可以查閱ngx.location.capture ,通過在該location中加入rewritebylua,具體它和proxy_pass的執行順序是rewrite在前。筆者在實際測試中發現,nginx的第三方模塊echo也提供發起了子請求的功能,但實際始終在proxy_pass之后,并不能實現需要的功能。
前臺的varnish,同樣取核心部分:
sub vcl_recv { if (req.method == "GET" && client.ip ~ "192.168.11.11" && req.url ~ "/console.example.com/foo/\d+" ) { set req.http.purge-url = regsub(req.url,"/console.example.com/foo/(\d+).*","\1.html"); ban("req.url ~ " + req.http.purge-url); set req.backend_hint = default; return(pass); } }
這里主語clint.ip當然你可以設置為一個集群,這個參考官方文檔。
acl ban { "localhost"; "192.168.11.168"; }
這樣后端更新時會通知varnish更新相關的資源,主動更新功能實現。 由于設置了return(pass),在前端的web中最好配置一段location來匹配該請求,可以簡單的return 200即可。
前端web:
server{ ... location = /console.example.com/foo/\d+ { return 200; } }
對于有需要更新圖片等靜態資源的情況。可以編寫一個通用的ban或者purge接口。具體可參考官方文章。
后續
配置文件基于varnish4,建議需要上生成環境的,多閱讀官方文檔。當然也可以瞅瞅我的部分博客,總之,多測試!!word is cheap ,show me your code。
來自:http://my.oschina.net/monkeyzhu/blog/512965