前言
大家都知道cache對于網站性能的重要性,提高使用者響應速度,減輕後端壓力,節省資源等等。一談到cache其實是個很大話題,從前端浏覽器到最終響應請求的伺服器可能要經過很多次跳轉,每次跳轉經過的伺服器都有機會提供cache。單從成本上而言,越靠近使用者的cache越經濟,實際情況中都需要根據目前線上的業務部署情況,開發成本收益比,可維護性等因素考慮采取在哪個地方cache,如何cache。粗線條一點根據cache的位置,一般會有浏覽器,web伺服器,CDN和應用伺服器cache等。這裡我結合最近自己完成的nginx伺服器上的cache工作,談一下nginx提供了哪些實用的cache服務。
Nginx承擔反向代理伺服器的工作,一般它處理的業務都是比較通用的處理,或者說一般不會處理很個性化的請求。比如安全,解壓縮功能對所有請求都是通用的,這是nginx可以搞定的,它一般不會終結業務類的處理,而是将他們交給後面的應用伺服器。正是因為Nginx的這種特性,如果在nginx節點上做cache,一般cache的是整個http請求的response。自然cache的key也一般從http請求的url和參數得來。目前我接觸到兩種nginx cache方式:
- 本地檔案系統cache (proxy_cache )
- 集中的記憶體cache (srcache-nginx-module)
Proxy_cache
這是Nginx原生的ngx_http_proxy_module自帶的cache解決方案。http response的内容以檔案的形式存在了本地的檔案系統,同時開了一個share memory備援存key值可以快速判斷是否cache命中。一般會這麼部署。
如何配置nginx使能proxy cache,可以參考NGINX CONTENT CACHING 和 A Guide to Caching with NGINX
因為這個方案是本地檔案系統存cache,可以比較容易了解具有以下缺點:
- cache備援,不同的nginx node間分别存cache。
- cache一緻性,因為是備援的,各個節點間cache的失效時間是不同步的。
- cache通路速度慢,讀磁盤自然會慢一些。
- cache效率低,因為各個node各有自己的cache不共享,即使某個node cache裡存了某個請求,如果另外的nginx node來處理請求還是cache miss。
基于上面的問題,有更好的方案,比如agentzhang的srcache-nginx-module子產品。
srcache-nginx-module
這個子產品旨在建構一個cache中間層,cache的存儲媒體是集中的,一般使用memcached或者redis。這樣解決了備援,也就解決了上面的各種問題,同時速度也快很多。對比着,它是這樣部署的。另外集中的cache存儲可以選擇redis或者memcached(memc-nginx-module提供API),memcached叢集方案需要nginx配置配合解決。
順便感謝agentzhang将Lua引入到nginx。nginx出色的擴充性已經很靈活了,lua-nginx-module将腳本語言處理文本的便捷性直接引入nginx,使得開發人員可以友善的在http request/response處理的各個階段都能嵌入定制的功能。比如我可以很友善的使用lua子產品在rewrite階段根據url和args生成定制的cache key--刨除一些參數(比如不影響response結果的那些參數)。附上兩篇不錯的lua-nginx-module的資料:
lua-nginx-module
OpenResty最佳實踐
使用memc-nginx和srcache-nginx子產品建構高效透明的緩存機制
另外附上自己的nginx.conf以備後查。
#user nobody;
worker_processes 1;
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log logs/error.log info;
pid logs/nginx.pid;
events {
worker_connections 1024;
}
http {
include mime.types;
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
#
# cache server 1
upstream memcache1 {
server 127.0.0.1:11211;
#keepalive 512 single;
}
# cache server 2
#upstream memcache2 {
# server 127.0.0.1:11211;
#}
upstream_list memcache_servers memcache1;
server {
listen 8080;
server_name localhost;
#memc-nginx-module
location /memc {
internal;
memc_connect_timeout 100ms;
memc_send_timeout 100ms;
memc_read_timeout 100ms;
set $memc_key $query_string;
set $memc_exptime 30;
set_hashed_upstream $backend memcache_servers $memc_key;
memc_pass $backend;
}
location /s {
# key gerneration for cache
set $args_for_key '';
rewrite_by_lua '
ngx.var.args_for_key = string.gsub(ngx.var.args, "queryid=%d+","queryid=0")
';
#echo $args_for_key;
srcache_fetch GET /memc $uri$args_for_key;
srcache_store PUT /memc $uri$args_for_key;
proxy_pass http://192.168.1.100:8000;
}
location / {
root /var/www;
index index.html index.htm index.php;
}
}
server {
listen 8081;
location / {
default_type text/html;
content_by_lua '
ngx.say("<p>hello, world</p>")
';
}
}
}