Nginx+Varnish 實現動靜分離,為服務器分流,降低服務器負載


相必大家在看加快網站響應速度方面的文章時,都提過這么一條:動靜分離。那怎樣實現動靜分離呢,這里筆者就親自搭建相關服務實現動靜分離。

 

動靜分離是一種架構,就是把靜態文件,比如JS、CSS、圖片甚至有些靜態頁面交給獨立的服務器集群處理,從而進行分流,使服務器降低壓力。

 

上面說把一些靜態的文件分離出去,有讀者就會笑了,靜態文件能有多少,能消耗多少資源。

讀者以實際經驗告訴大家,千萬不要小瞧這些靜態文件,現在大部分網站都是以視頻、圖片為主,試想下天貓、淘寶、京東之類,文字能有多少,圖片、視頻又是多少,就知道這個量到底是多么的龐大,而動靜分離從筆者公司實際使用經驗來看,效果是杠杠的。

 

筆者這里的環境是: Centos6.4  Nginx Varnish , 其中Nginx響應請求,Varnish主要做緩存和反向代理(筆者這里說個題外話,很多人疑惑反向代理和正向代理,這兩個概念說的都有些玄幻,筆者有個簡單粗暴的解釋. 反向代理:隱藏真實服務器信息, 正向代理:隱藏真實客戶端信息)

 

筆者這里弄三台主機:192.168.138.10      192.168.138.3   192.168.138.4   (10安裝Varnish,3、4安裝Nginx) 

 

第一步:先在92.168.138.3   192.168.138.4 兩台機器上安裝Nginx,這個安裝相對容易,筆者就不做累贅了,只要你看到下面兩個頁面就OK了

 

第二步:在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',只要更換后面的版本號,緩存就會更新,當然了還有內部機制(有專門清理緩存的接口)。這樣動態文件在規定的服務器集群,靜態文件也在規定的服務器集群,相互分開,大大提升頁面加載速度,提高網站吞吐量。

 

好了,筆者不才,希望上面這些對大家有所幫助!


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM