相必你們在看加快網站響應速度方面的文章時,都提過這麼一條:動靜分離。那怎樣實現動靜分離呢,這裏筆者就親自搭建相關服務實現動靜分離。php
動靜分離是一種架構,就是把靜態文件,好比JS、CSS、圖片甚至有些靜態頁面交給獨立的服務器集羣處理,從而進行分流,使服務器下降壓力。css
上面說把一些靜態的文件分離出去,有讀者就會笑了,靜態文件能有多少,能消耗多少資源。html
讀者以實際經驗告訴你們,千萬不要小瞧這些靜態文件,如今大部分網站都是以視頻、圖片爲主,試想下天貓、淘寶、京東之類,文字能有多少,圖片、視頻又是多少,就知道這個量究竟是多麼的龐大,而動靜分離從筆者公司實際使用經驗來看,效果是槓槓的。python
筆者這裏的環境是: Centos6.4 Nginx Varnish , 其中Nginx響應請求,Varnish主要作緩存和反向代理(筆者這裏說個題外話,不少人疑惑反向代理和正向代理,這兩個概念說的都有些玄幻,筆者有個簡單粗暴的解釋. 反向代理:隱藏真實服務器信息, 正向代理:隱藏真實客戶端信息)nginx
筆者這裏弄三臺主機:192.168.138.10 192.168.138.3 192.168.138.4 (10安裝Varnish,三、4安裝Nginx) web
第一步:先在92.168.138.3 192.168.138.4 兩臺機器上安裝Nginx,這個安裝相對容易,筆者就不作累贅了,只要你看到下面兩個頁面就OK了vim
第二步:在192.168.138.10上安裝Varnish後端
這裏筆者從官網上下載了比較新的 Varnish-3.0.7, 筆者準備使用源碼編譯安裝,安裝在/usr/local/varnish 目錄下緩存
tar -zxvf varnish-3.0.7.tar.gz
安裝以前仍是老話,依賴先安裝上,筆者在其官方網站上找到了其依賴的包服務器
https://www.varnish-cache.org/docs/4.0/installation/install.html
yum install -y autoconf automake jemalloc-devel libedit-devel libtool ncurses-devel pcre-devel pkgconfig python-docutils python-sphinx
而後進行編譯前檢查
./configure \ --prefix=/usr/local/varnish/ \ --enable-dependency-tracking \ --enable-developer-warnings \ --enable-debugging-symbols
檢查無誤後開始編譯安裝
make && make install
第三步:開始配置varnish
其配置文件位於 /usr/local/varnish/etc/varnish/default.vcl 能夠看到裏面已經有些範例
筆者在這裏先要對其架構及一些經常使用函數加以說明,否則後面都看不懂在配置寫什麼玩意
varnish使用了一種叫VCL的語言去對其進行配置,至於這語言啥玩意,就不用過多關心了,大體瞭解下就OK了,其語法能夠看裏面的相關範例,裏面有些函數,下面一一列出
vcl_recv 函數: 接收和處理請求,當請求到達併成功接收後被調用,判斷請求的數據並決定如何處理請求;該函數返回值有這麼些關鍵字-》
pass 表示進入pass模式,將請求交給vcl_pass 函數
pipe 表示進入pipe模式,將請求交個vcl_pipe函數
error code[reason] 表示返回'code'給客戶端並放棄請求, 'code'是錯誤標示例如200 405等,'reason'是錯誤緣由
vcl_pipe函數:該函數在進入pipe模式時被調用,用於將請求直接傳遞給後端主機,在請求和返回內容沒有改變的狀況下,將不變的內容返回給客戶端,直到這個連接關閉;該函數有這麼些返回值=>
error code[reason] 同vcl_recv
pipe
vcl_pass函數: 該函數進入pass模式被調用,用於直接將請求發送給後端主機,後端主機響應後發送給客戶端,不進行任何緩存,每次都返回最新內容,該函數有幾個返回值=>
error code[reason] 同vcl_recv
pass
lookup函數: 表示在緩存中查找到被請求的對象,而且根據查找的結果交給vcl_hit函數(命中)或vcl_miss(未命中)函數處理
vcl_hit函數:在執行lookup後,若是在緩存中找到對象,該函數將會被自動調用,該函數有如下幾個返回值 =>
deliver 表示找到內容發送給客戶端,把控制權交給vcl_deliver函數
error code[reason] 同vcl_recv
pass
vcl_miss函數: 在執行lookup後,在緩存中沒有找到對象,該函數被調用,該函數可用於判斷是否從後端請求內容,該函數有如下幾個返回值 =>
fetch 表示從後端獲取內容,並把控制權交給vcl_fetch函數
error code[reason] 同vcl_recv
pass
vcl_fetch函數:從後端主機更新緩存獲取內容後調用該函數,接着判斷獲取的內容是放入緩存仍是直接給客戶端,該函數有如下幾個返回值=》
error code[reason] 同vcl_recv
pass
deliver
vcl_deliver函數:將在緩存中找到的內容發送給客戶端調用的方法,該函數有如下幾個返回值=》
error code[reason] 同vcl_recv
deliver
vcl_timeout函數:在緩存內容到期前調用該函數,該函數有如下幾個返回值=》
discard 表示中緩存中清除該內容
fetch
vcl_discard函數:緩存內容到期後或者緩存空間不夠時自動被調用,該函數有如下幾個返回值=》
keep 表示在內容中保留該緩存
discard
VCL處理流程能夠用下面這張圖表示
Receive狀態:請求處理的入口狀態,根據VCL規則判斷該請求應該在Pass或Pipe,仍是進入Lookup(到本地緩存中查詢)
Lookup狀態:進入此狀態後,會在hash表中查詢數據,若是找到則進入Hit狀態,不然進入Miss狀態
Pass狀態:在此狀態下會進入後端獲取內容,既進入Fetch狀態
Fetch狀態:從後端獲取請求,發送請求,得到數據,並存儲到本地
Deliver狀態:將獲取的數據發送給客戶端,而後完成本次請求
VCL裏面還有些變量,能夠用在VCL函數中,用於邏輯處理
請求到達後可使用的內置公用變量:
req.backend => 指定對應的後端主機
server.ip => 表示服務器端IP
client.ip => 表示客戶端IP
req.request => 指定請求的類型,如GET POST PUT DELETE
req.url => 知道請求的URL
req.proto => 表示客戶端發起請求的HTTP協議版本
req.http.header => 表示請求中的頭部信息
req.restarts => 表示請求重啓的次數,默認最大值爲4
VCL向後端主機請求時使用的內置變量
beresp.request => 指定請求的類型,如GET POST
beresp.url => 指定請求的地址
beresp.proto => 指定請求的協議版本號
beresp.http.header => 對應請求的HTTP頭信息
beresp.ttl => 表示緩存的週期,單位秒
從cache或後端主機獲取內容後可使用的內置變量
obj.status => 表示返回的狀態碼,如200 404 500 等
obj.cacheable => 表示返回的內容是否能夠緩存
obj.valid => 表示是不是有效的HTTP應答
obj.response => 表示應答的狀態信息
obj.ttl => 表示返回內容的生存週期,單位秒
obj.lastuse => 表示上次請求到如今的間隔,單位秒
客戶端應答時可使用的變量
resp.status => 表示返回給客戶端的狀態碼
resp.proto => 表示返回給客戶端的HTTP協議版本
resp.http.header => 表示返回給客戶端的頭信息
resp.response => 表示返回給客戶端的狀態信息
上面可能不全,具體的能夠到其官方網站上面查看
下面筆者有個配置例子,裏面有註釋,能夠瞅瞅
#配置兩臺後端主機 backend myserver3 { .host = "192.168.138.3"; .port = "80"; } #配置兩臺後端主機 backend myserver4 { .host = "192.168.138.4"; .port = "80"; } #這裏定義一個myserver的director director myserver round-robin { {.backend = myserver3;} {.backend = myserver4;} } acl purge { "localhost"; "127.0.0.1"; } #recv狀態 sub vcl_recv { #若是訪問 laiwojia.la 或者 www.laiwojia.la 就使用該varnish進行代理 前提是你要在你本地配置laiwojia.la這個虛擬主機奧 if (req.http.host ~ "(www.)?laiwojia.la") { set req.backend = myserver; } if (req.restarts == 0) { if (req.http.x-forwarded-for) { set req.http.X-Forwarded-For = req.http.X-Forwarded-For + ", " + client.ip; } else { set req.http.X-Forwarded-For = client.ip; } } #若是請求的類型不是GET HEAD PUT POST TRACE OPTIONS DELETE 進入pipe模式 if (req.request != "GET" && req.request != "HEAD" && req.request != "PUT" && req.request != "POST" && req.request != "TRACE" && req.request != "OPTIONS" && req.request != "DELETE") { /* Non-RFC2616 or CONNECT which is weird. */ return (pipe); } #若是不是GET或者HEAD類型的請求 進入到pass模式 if (req.request != "GET" && req.request != "HEAD") { /* We only deal with GET and HEAD by default */ return (pass); } #有認證或者cookie存在 進入到pass模式 if (req.http.Authorization || req.http.Cookie) { /* Not cacheable by default */ return (pass); } #若是URL中含有.php .jsp .do 而且以此結尾或者後面還有?或者# 進入pass模式 if (req.url ~ "\.(php|jsp|do)($|\?|#)") { return (pass) } if (req.request == "PURGE") { if (!client.ip ~ purge) { error 405 "Not allowed."; } return (lookup); } return (lookup); } ub vcl_pipe { # # Note that only the first request to the backend will have # # X-Forwarded-For set. If you use X-Forwarded-For and want to # # have it set for all requests, make sure to have: # # set bereq.http.connection = "close"; # # here. It is not set by default as it might break some broken web # # applications, like IIS with NTLM authentication. return (pipe); } # sub vcl_pass { return (pass); } # sub vcl_hash { hash_data(req.url); if (req.http.host) { hash_data(req.http.host); } else { hash_data(server.ip); } return (hash); } sub vcl_hit { if (req.request == "PURGE") { purge; error 200 "Purged."; } return (deliver); } # sub vcl_miss { if (req.request == "PURGE") { purge; error 200 "Purged."; } return (fetch); } sub vcl_fetch { #若是請求的類型是GET 請求url以 html png gif jpeg 等等結尾 進行緩存 緩存時間600s if (req.request == "GET" && req.url ~ "\.(html|png|gif|jpeg|pdf|ppt|doc|docx|rar|zip|bmp|swf|mp3|mp4|rmvb|mov|avi|css|js|jpeg|htm)$") { set beresp.ttl = 600s; } return (deliver); } sub vcl_deliver { #給HTTP頭上加上標示 用於區分是否命中緩存 if (obj.hits > 0) { set resp.http.X-Cache = "HIT from www.laiwojia.la"; } else { set resp.http.X-Cache = "MISS from www.laiwojia.la"; } return (deliver); } #當發生錯誤後返回給客戶端的頁面 sub vcl_error { set obj.http.Content-Type = "text/html; charset=utf-8"; set obj.http.Retry-After = "5"; synthetic {" <?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd"> <html> <head> <title>"} + obj.status + " " + obj.response + {"</title> </head> <body> <h1>Error "} + obj.status + " " + obj.response + {"</h1> <p>"} + obj.response + {"</p> <h3>Guru Meditation:</h3> <p>XID: "} + req.xid + {"</p> <hr> <p>Varnish cache server</p> </body> </html> "}; return (deliver); } sub vcl_init { return (ok); } # sub vcl_fini { return (ok); }
好了,上面配置就是這樣的,而後你要在本地加上 www.laiwojia.la laiwojia.la 的解析
vim /etc/hosts #添加 127.0.0.1 www.laiwojia.la laiwojia.la
千萬不要忘記在192.168.138.3 192.168.138.4 這兩臺機器的nginx上配置 laiwojia.la 這個虛擬機奧,這裏筆者貼出配置文件(nginx.conf 部分)
server{ listen 80; server_name laiwojia.la; root /usr/website/html/www.laiwojia.la; index index.html index.htm index.php; access_log /usr/website/logs/www.laiwojia.la/access.log; error_log /usr/website/logs/www.laiwojia.la/errors.log; }
固然上面的 /usr/website/html/www.laiwojia.la 這個目錄要有 而後在裏面建一個 test.html 裏面寫上內容,而後作好本地解析
vim /etc/hosts #添加 127.0.0.1 www.laiwojia.la laiwojia.la
好,確保兩臺機器上虛擬機運行沒有問題
最後一步:啓動varnish
/usr/local/varnish/sbin/varnishd -f /usr/local/varnish/etc/varnish/default.vcl -s malloc,1G -T 127.0.0.1:2000 -a 0.0.0.0:80
這個是筆者在其官方網站上找到的啓動方法 -f 就是配置文件 -a 就是IP地址加上端口 咱們這裏監聽80端口
OK,一切完結,咱們開始測試下
curl -I www.laiwojia.la
激動人心的時刻來了
HTTP/1.1 200 OK
Server: nginx/1.6.2
Content-Type: text/html
Last-Modified: Thu, 28 Jan 2016 11:55:24 GMT
ETag: "56aa01ac-f"
Content-Length: 15
Accept-Ranges: bytes
Date: Thu, 28 Jan 2016 12:27:33 GMT
X-Varnish: 100516502
Age: 0
Via: 1.1 varnish
Connection: keep-alive
X-Cache: MISS from www.laiwojia.la
這下是否是要哭,怎麼是 X-Cache: MISS from www.laiwojia.la 沒有擊中啊,不要擔憂,這是第一次,咱們再來一次
HTTP/1.1 200 OK
Server: nginx/1.6.2
Content-Type: text/html
Last-Modified: Thu, 28 Jan 2016 11:55:24 GMT
ETag: "56aa01ac-f"
Content-Length: 15
Accept-Ranges: bytes
Date: Thu, 28 Jan 2016 12:27:45 GMT
X-Varnish: 100516503 100516502
Age: 12
Via: 1.1 varnish
Connection: keep-alive
X-Cache: HIT from www.laiwojia.la
這下終因而久違的 X-Cache: HIT from www.laiwojia.la
OK,這裏,大致的配置就完了!
是否是有同窗要問了,尼瑪這怎麼就叫作動靜分離了?
想一想上面的laiwojia.la 這個域名 ,若是是你們的,你們能夠把它配置成 img.xxx.com 之類,文件都上傳到這個域名下面(也能夠將集羣裏面的靜態文件目錄定時複製到這個域名的主機下面),而後你們就能夠經過 img.xxx.com 這樣的域名訪問了。筆者公司是這樣作的,咱們的靜態文件都放在服務器 static 目錄裏,而後靜態文件服務器集羣每5分鐘複製下這個目錄,同時全部上傳都上傳到靜態文件服務器,全部靜態文件、圖片引用都使用靜態文件服務器前綴 img.xxx.com , 爲了管理方便,筆者公司裏面的靜態文件後面都有後綴 '?v=2016012801',只要更換後面的版本號,緩存就會更新,固然了還有內部機制(有專門清理緩存的接口)。這樣動態文件在規定的服務器集羣,靜態文件也在規定的服務器集羣,相互分開,大大提高頁面加載速度,提升網站吞吐量。
好了,筆者不才,但願上面這些對你們有所幫助!