原文地址: http://blog.wangjunfeng.com/a...json
使用varnish是一個很不錯的HTTP加速方案,挪威最大的在線報紙 Verdens Gang 使用3臺Varnish代替了原來的12臺Squid,性能比之前更好。然而varnish默認狀況下是以url進行hash,來標識緩存,因此對於jsonp這種帶有callback參數的請求,每一次callback都不同,極可能會生成大量重複數據,佔用內存空間,浪費資源。最近就遇到了這個問題,好在這個仍是有解決辦法的。後端
其實jsonp很簡單,就是json數據加一個callback和一對括號就能夠了,因此只要咱們取到沒有callback的json數據,並進行緩存,再把數據用標籤包起來就能夠了。是的,就是這麼簡單,可是如何實現呢? 緩存
其實實現起來也很簡單,在varnish 4的VCL裏面其實能夠使用synthetic來組合數據,可是這個函數又只能在vcl_synth和vcl_backend_error內使用,其中vcl_synth是用來處理錯誤的,而vcl_backend_error是用來處理後端服務器錯誤,因此咱們就必須先拋出錯誤,而後讀取json數據,再進行拼接,而後返回。 服務器
這裏拼接數據時還須要用到varnish的Edge Side Includes(ESI)。cookie
# # This is an example VCL file for Varnish. # # It does not do anything by default, delegating control to the # builtin VCL. The builtin VCL is called when there is no explicit # return statement. # # See the VCL chapters in the Users Guide at https://www.varnish-cache.org/docs/ # and https://www.varnish-cache.org/trac/wiki/VCLExamples for more examples. # Marker to tell the VCL compiler that this VCL has been adapted to the # new 4.0 format. vcl 4.0; # Default backend definition. Set this to point to your content server. # 後端服務器 backend default { .host = "127.0.0.1"; .port = "8000"; .connect_timeout = 8s; .first_byte_timeout = 8s; .between_bytes_timeout = 5s; } # varnish服務器 backend jsonp_template_backend { .host = "127.0.0.1"; .port = "80"; } sub vcl_recv { # 當地址是/JSONP-ESI-TEMPLATE時,則拋出760錯誤 if (req.url == "/JSONP-ESI-TEMPLATE") { return (synth(760, "Json")); } } sub vcl_recv { # Happens before we check if we have this in cache already. # # Typically you clean up the request here, removing cookies you don't need, # rewriting the request, etc. if (req.method != "GET") { return (pass); } if (req.url ~ "callback=") { # 保存callback參數,後續拼裝數據時會使用到 set req.http.X-Callback = regsub( req.url, ".*[\?&]callback=([\.A-Za-z0-9_]+).*", "\1" ); # 去除callback和_參數 set req.http.X-ESI-Url = regsub(req.url, "&?callback=[\.A-Za-z0-9_]+", ""); set req.http.X-ESI-Url = regsub(req.http.X-ESI-Url, "&?_=[\.A-Za-z0-9_]+", ""); set req.http.X-ESI-Url = regsub(req.http.X-ESI-Url, "\?$", ""); set req.http.X-ESI-Url = regsub(req.http.X-ESI-Url, "\?&", "?"); # 設置後端請求地址 set req.url = "/JSONP-ESI-TEMPLATE"; # 設置請求後端服務器 set req.backend_hint = jsonp_template_backend; return (pass); } return (hash); } sub vcl_backend_response { # 若是後端請求包含有X-ESI則啓用X-ESI if (beresp.http.X-ESI) { unset beresp.http.X-ESI; set beresp.do_esi = true; } } sub vcl_backend_response { # X-JSONP-Server means we need to clean up the response a bit if (beresp.http.X-JSONP-Server) { unset beresp.http.X-JSONP-Server; set beresp.http.Server = "JSONP-Server"; } } sub vcl_synth { # 處理760錯誤,這裏設置相關參數,後續請求後端要用到 if (resp.status == 760) { set resp.http.X-ESI = "1"; set resp.http.X-JSONP-Server = "1"; # 設置狀態碼爲200 set resp.status = 200; # 數據拼接,拼接後直接返回 synthetic({"<esi:include />"} + {"/**/"} + req.http.X-Callback + {"(<esi:include src="} + req.http.X-ESI-Url + {" />)"}); return(deliver); } } sub vcl_backend_response { # Happens after we have read the response headers from the backend. # # Here you clean the response headers, removing silly Set-Cookie headers # and other mistakes your backend does. unset beresp.http.set-cookie; if (beresp.ttl <= 0s) { set beresp.ttl = 120s; } set beresp.do_gzip = true; return (deliver); } sub vcl_deliver { # Happens when we have all the pieces we need, and are about to send the # response to the client. # # You can do accounting or modifying the final object here. }
這裏的代碼是varnish 4.*版本的代碼示例,若是使用了其餘版本的varnish配置可能有所不一樣。若是使用較低版本,varnish 4裏面有些語法已經變動。使用時可能須要修改一下。
原文地址: http://blog.wangjunfeng.com/a...app