1. 程式人生 > >Nginx+Memcache+一致性hash算法 實現頁面分布式緩存(轉)

Nginx+Memcache+一致性hash算法 實現頁面分布式緩存(轉)

tps ons efi 策略 可擴展性 master () list roo

網站響應速度優化包括集群架構中很多方面的瓶頸因素,這裏所說的將頁面靜態化、實現分布式高速緩存就是其中的一個很好的解決方案...

技術分享

1)先來看看Nginx負載均衡

Nginx負載均衡依賴自帶的 ngx_http_upstream_module 、 ngx_http_memcached_module兩大功能模塊,其中一致性hash算法Nginx本身是不支持的,可以借助第三方模塊: ngx_http_upstream_consistent_hash

或者直接使用淘寶的Tengine:

http://tengine.taobao.org/document_cn/http_upstream_consistent_hash_cn.html

模塊的添加

unzip ngx_http_consistent_hash-master.zip
cd nginx-1.6.3 #進入nginx安裝原始文件夾
./configure --user=www --group=www --add-module=../ngx_http_consistent_hash-master --with-http_ssl_module --with-http_stub_status_module --prefix=/application/nginx-1.6.3/ #添加模塊
make #如果是生產環境添加模塊,切記不要make install,否則會覆蓋文件
cp objs/nginx /application/nginx/sbin/nginx #停掉nginx進程,覆蓋二進制文件
[[email protected]
/* */ sbin]# ./nginx -V #查看模塊是否添加成功 nginx version: nginx/1.6.3 built by gcc 4.4.7 20120313 (Red Hat 4.4.7-16) (GCC) TLS SNI support enabled configure arguments: --user=www --group=www --add-module=../ngx_http_consistent_hash-master --with-http_ssl_module --with-http_stub_status_module --prefix=/application/nginx-1.6.3/

2)調度算法選擇

如上圖,在我們確定要將數據采用分布式memcache服務器緩存起來後,就面臨一個問題:采用一種什麽方式去緩存和調度數據?很顯然,最簡單的策略就是將每一次Memcache請求都隨機發送到一臺Memcache服務器,但這種策略可能又會帶來兩個問題:一是同一份數據可能被存在不同機器上而造成數據冗余,二是可能某數據已經被緩存但是沒法命中。因此,隨機策略無論是時間效率還是空間效率都利用的不是很好。

url_hash算法

技術分享

url_hash算法是Nginx負載均衡動態調度算法中的一種,是將一個完整的URL經過哈希運算key=HASH($URL)%3(3是memcache節點臺數)得到一個key值,具有相同key值得URL也就是Memcache請求將會發往同一臺緩存服務器,比如,H=0發往Memcache_server01、H=1發往Memcache_server02、H=2發往Memcache_server03,避免了數據冗余和命中率低的兩個問題;

普通url_hash的缺點就是,計算公式key=HASH($URL)%N中,一旦緩存節點N數量發生變化,那麽所有數據都要重新按照公式key=HASH($URL)%(N-1)計算,之前計算得到的key值結果就會全部發生變化,意味著緩存節點下緩存的數據全部失效!要重新緩存全部節點數據。如果是高並發大數據量的話,對後端節點服務器的沖擊是致命的,很有可能會導致系統的全面癱瘓。這也是為什麽在使用大量的分布式緩存系統在遭遇系統重啟時,要對緩存預熱及嚴格遵守集群服務啟動順序的原因。

一致性HASH算法

一致性HASH算法的出現有效的解決了上面普通url_hash調度算法在節點變動後面臨全部緩存失效的問題

簡單地說,一致性哈希將整個哈希值空間組織成一個虛擬的圓環,如假設某空間哈希函數H的值空間是0-2^32-1(即哈希值是一個32位無符號整形),整個哈希空間如下:

技術分享

下一步將各個服務器使用H進行一個哈希計算,具體可以使用服務器的IP地址或者主機名作為關鍵字,這樣每臺機器能確定其在上面的哈希環上的位置了,並且是按照順時針排列,這裏我們假設三臺節點memcache經計算後位置如下

技術分享

接下來使用相同算法計算出數據的哈希值h,並由此確定數據在此哈希環上的位置

假如我們有數據A、B、C、D、4個對象,經過哈希計算後位置如下:

技術分享

根據一致性哈希算法,數據A就被綁定到了server01上,D被綁定到了server02上,B、C在server03上,是按照順時針找最近服務節點方法

這樣得到的哈希環調度方法,有很高的容錯性和可擴展性:

假設server03宕機

技術分享

可以看到此時A、C、B不會受到影響,只是將B、C節點被重定位到Server 1。一般的,在一致性哈希算法中,如果一臺服務器不可用,則受影響的數據僅僅是此服務器到其環空間中前一臺服務器(即順著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

考慮另外一種情況,如果我們在系統中增加一臺服務器Memcached Server 04:

技術分享

此時A、D、C不受影響,只有B需要重定位到新的Server 4。一般的,在一致性哈希算法中,如果增加一臺服務器,則受影響的數據僅僅是新服務器到其環空間中前一臺服務器(即順著逆時針方向行走遇到的第一臺服務器)之間數據,其它不會受到影響。

綜上所述,一致性哈希算法對於節點的增減都只需重定位環空間中的一小部分數據,具有較好的容錯性和可擴展性。

一致性哈希的缺點:在服務節點太少時,容易因為節點分部不均勻而造成數據傾斜問題。我們可以采用增加虛擬節點的方式解決。

為了解決這種數據傾斜問題,一致性哈希算法引入了虛擬節點機制,即對每一個服務節點計算多個哈希【具體可以這麽做:根據服務器的名字或者是ip計算節點hash的時候,可以加上編號,然後再計算哈希值】,在每個計算的結果位置都放置一個服務節點,稱為虛擬節點。這樣,數據再被存儲的時候,就不會因為服務器在環上的間距太大而導致“數據傾斜”了。

技術分享

同時數據定位算法不變,只是多了一步虛擬節點 到實際節點的映射,例如定位到“Memcached Server 1#1”、“Memcached Server 1#2”、“Memcached Server 1#3”三個虛擬節點的數據均定位到Server 1上。這樣就解決了服務節點少時數據傾斜的問題。在實際應用中,通常將虛擬節點數設置為32甚至更大,因此即使很少的服務節點也能做到相對均勻的數據分 布,避免出現雪崩的情況

3)Nginx配置

upstream memcached {
    consistent_hash $request_uri; #采用一致性哈希計算請求字段request_rui值
    server 172.16.2.11:11211; #節點服務器
    server 172.16.2.12:11212;
    server 172.16.2.13:11213;
}
 
server {
    listen       80;
    server_name  lichengbing.cn;
 
    location ^~ /cache/ { #匹配帶cache目錄時將請求轉發給計算好的哈希節點服務器
        set                     $enhanced_memcached_key $request_uri;
        enhanced_memcached_pass memcached;
    }
 
    error_page     404 502 504 = @fallback;
}
location @fallback {
    proxy_pass     http://backend;
}

4)修改示例PHP頁面

$htmlContent = file_get_contents(‘http://lichengbing.cn‘);
 
// 頁面過期時間
$expiresTime = 60 * 5;
 
// Last-Modified頭設置的時間
$lastModified = gmdate(‘D, d M Y H:i:s \G\M\T‘, time());
 
// Expires頭設置的時間
$expires = gmdate(‘D, d M Y H:i:s \G\M\T‘, time() + $expiresTime);
 
// 最終緩存的內容
$cacheContent = "EXTRACT_HEADERS
Content-Type: text/html
Cache-Control:max-age=$expiresTime
Expires:$expires
Last-Modified:$lastModified
 
$htmlContent";
     
// 獲取memcache實例
$memcached = new Memcache();
$memcached->addServer(‘172.16.2.11‘, 11211);
$memcached->addServer(‘172.16.2.12‘, 11212);
$memcached->addServer(‘172.16.2.13‘, 11213);
 
// 寫入緩存
$memcached->set(‘/cache/index.html‘, $cacheContent, $expiresTime);

至此,我們已經簡單的完成了使用Nginx和Memcached對緩存頁面的訪問,但這只是後端的簡單實現,在前端還需要實現對頁面緩存的管理等等的工作。

原文地址(http://www.tuicool.com/articles/BZBVfaM)

Nginx+Memcache+一致性hash算法 實現頁面分布式緩存(轉)