memcache集羣方案

1.方案選型

緩存方案的基本要求:避免單點故障;較好的性能和穩定性;便於運維管理。

常見方案如下:
A. 客戶端直接訪問多個memcache 實例
優點:簡單,未引入新的節點;
缺點:維護不方便,未實現集中管理;性能不滿足,實例宕機後不能自動踢出(hash到該實例的請求都要等到超時才能轉到其他正常實例)。
B. magent代理
優點:簡單,滿足緩存對代理的大部分要求;
缺點:無成熟案例;性能不滿足,實例宕機後不能自動踢出。
C. moxi代理
優點:功能豐富,提供本地緩存、Memcache實例數據複製等。
缺點:無成熟案例;代碼很久沒更新。
D. twemproxy代理
優點:twitter的成熟案例。
缺點:主要配合是twemproxy+redis,與memcache配合使用的案例較少;不支持緩存的複製功能。
E. mcrouter
優點:facebook的成熟案例;功能強大,支持Memcache實例分組、實例複製功能,實例宕機後可自動踢出。

缺點:一般用於ubuntu平臺,在centos上安裝較複雜。

2.mcrouter介紹

Mcrouter是一個支持memcached實例的代理,被facebook用於遍佈全球的數十個集羣幾千個服務器之間控制流量。它適用於大規模的緩存處理中,在峯值的時候,mcrouter處理接近50億的請求/秒。


2.1.mcrouter主要特點

1.支持標準開源的memcached ASCII編碼協議
支持memcahed協議的所有客戶端無需做任何修改即可被mcrouter支持。
2.多種hash算法
支持consistent hashing算法,集羣擴容只有少量數據需要遷移。也可根據需要自定義其他hash算法:weighted ch3,crc32,HostIdRoute 等。
3.前綴路由
mcrouter可以根據key前綴把客戶端分配到不同的memcahed池,從而實現集羣中的memcache實例分組。
4.memcached池備份
通過將add\set\delete分發到兩個池,而get只從其中一個池中獲取數據,實現兩個池的備份。當其中一個池有實例宕機時,可以從備份池中獲取數據。
5.熱加載
mcrouter監控它所有的配置文件,一旦檢測到任何配置文件被修改,mcrouter的一個後臺線程將自動的重新加載並分析這些文件。該過程對客戶端完全透明。
6.自動故障轉移
一旦某實例無響應,mcrouter將直接將相關請求轉移到另一個可用的實例。同時,後臺繼續向無響應實例發送心跳請求,只要該實例恢復正常,mcrouter將會重新啓用這個實例。該過程對客戶端完全透明。

2.2.自動故障轉移策略

Memcache實例異常將被mcrouter標記爲"TKO" ("technical knockout")狀態。
根據不同情況分爲Soft TKO和Hard TKP兩種狀態:
Soft TKO:連續3次請求超時(failures_until_tko控制);
Hard TKO:發生1次connect error或1次connect timeout。
Memcache實例被標記爲TKO狀態後,原本應該Hash到該實例的請求將轉發到”FailoverRouter”指定的備份實例;同時,mcrouter通過探針以一定頻率(初始probe_delay_initial_ms 默認10000,以50%左右的幅度增長,最大probe_delay_max_ms 默認60000)檢測TKO狀態的實例,只要該實例恢復正常,mcrouter將會重新啓用這個實例。該過程對客戶端完全透明。

3.功能測試

3.1.數據切分

3.1.1.數據分佈

測試數據在Memcache實例上的分佈。
配置文件:

{
  "pools": {
    "A": {
      "servers": [
        "192.168.188.150:11211",
        "192.168.188.150:11212",
        "192.168.188.150:11213",
        "192.168.188.150:11214",
        "192.168.188.150:11215",
        "192.168.188.150:11216",
        "192.168.188.150:11217",
        "192.168.188.150:11218",
        "192.168.188.150:11219",
      ]
    }
  },
  "route": "PoolRoute|A"
}


測試結果:
10000個數據,9個Memcache實例,每個實例的數據量分別是:
1096, 1116, 1110, 1075, 1180, 1058, 1129, 1138, 1098

3.1.2.一致性HASH

測試新增兩個實例後,數據在Memcache實例上的分佈變化情況。
配置文件:

{
  "pools": {
    "A": {
      "servers": [
        "192.168.188.150:11211",
        "192.168.188.150:11212",
        "192.168.188.150:11213",
        "192.168.188.150:11214",
        "192.168.188.150:11215",
        "192.168.188.150:11216",
        "192.168.188.150:11217",
        "192.168.188.150:11218",
        "192.168.188.150:11219",
        "192.168.188.150:11220",
        "192.168.188.150:11221",
      ]
    }
  },
  "route": "PoolRoute|A"
}


測試結果:
新增實例後數據量移動比例:新增機器數 / 調整後服務器總數 = 2 / 11
新增2個Memcache實例後,get miss 數:1805 (10000* 2/11 = 1818)
注:新增實例在配置文件中往後順延,不能插入現存的的實例配置之前。

3.2.實例分組

測試不同前綴的KV能夠分發到不同實例分組中。
前綴p1,p2,p3的key分別分發到pool1,pool2,pool3中,其他默認到common_cache中,每個 pool由2個memcache構成。

{
  "pools": {
    "pool1": { "servers": [ "192.168.188.150:11211","192.168.188.150:11212" ] },
    "pool2": { "servers": [ "192.168.188.150:11213","192.168.188.150:11214" ] },
    "pool3": { "servers": [ "192.168.188.150:11215","192.168.188.150:11216" ] },
    "common_cache": { "servers": [ "192.168.188.150:11217","192.168.188.150:11218" ] }
  },
  "route": {
    "type": "PrefixSelectorRoute",
    "policies": {
      "p1": "PoolRoute|pool1",
      "p2": "PoolRoute|pool2",
      "p3": "PoolRoute|pool3"
    },
    "wildcard": "PoolRoute|common_cache"
  }
}

測試結果:
p1,p2,p3爲前綴的key自動分發到相應的pool中,其他key分發到默認pool中。

3.3.實例故障檢測

測試TKO狀態的運行機制。
配置文件

{
  "pools": {
    "p1": {
      "servers": [
        "192.168.188.150:11211"
      ]
    },
    "p2": {
      "servers": [
        "192.168.188.150:11212"
      ]
    }
  },
  "route": "PoolRoute|p1",
  "route": "PoolRoute|p2",
  "route": {
    "type": "FailoverRoute",
    "children": [ "PoolRoute|p1","PoolRoute|p2" ],
    "failover_errors": [ "tko" ]
  }
}

3.3.1.soft TKO

測試結果:

模擬超時:iptables -I INPUT -p tcp --dport 11211 --tcp-flags PSH PSH -j DROP
取消超時:iptables -D INPUT 1

3.3.2.hard TKO

測試結果:   


3.4.不同代理分發的一致性

兩個代理都用默認的一致性hash算法,分發到同1個由8個Memcache實例組成的集羣(配置文件相同)。以一個代理set 1000個不同的key,再用另一個代理get,可以正常get到所有結果。

3.5.Memcache命令支持

客戶端:java_memcached-release_2.6.6
服務器:mcrouter-0.6.0
客戶端對代理的以下操作正常:

//add
public boolean add(String key, Object val)
public boolean add(String key, Object val, Date expireDate)
public boolean add(String key, Object val, Integer hashCode)
public boolean add(String key, Object val, Date expireDate, Integer hashCode)
    
//set
public boolean set(String key, Object val)
publicboolean set(String key, Object val, Date expireDate)
publicboolean set(String key, Object val, Integer hashCode)
publicboolean set(String key, Object val, Date expireDate, Integer hashCode)
    
//get
public Object get(String key)
publicObject get(String key, Integer hashCode)
publicObject get(String key, Integer hashCode, Boolean asString)
    
//delete
public boolean delete(String key)
    
//gets and cas
public MemcachedItem gets(String key)
publicMemcachedItem gets(String key, Integer hashCode)
publicboolean cas(String key, Object val, long casUnique)
publicboolean cas(String key, Object val, Date expireDate, long casUnique)
publicboolean cas(String key, Object val, Integer hashCode, long casUnique)
publicboolean cas(String key, Object val, Date expireDate, Integer hashCode, long casUnique)

//incr and decr
public long incr(String key)
publiclong incr(String key, long inc)
publiclong incr(String key, long inc, Integer hashCode)
publiclong decr(String key)
publiclong decr(String key, long inc)
publiclong decr(String key, long inc, Integer hashCode)
    
//thread safe way to incr and decr
public long addOrIncr(String key)
publiclong addOrIncr(String key, long inc)
publiclong addOrIncr(String key, long inc, Integer hashCode)
publiclong addOrDecr(String key)
publiclong addOrDecr(String key, long inc)
publiclong addOrDecr(String key, long inc, Integer hashCode)

4.性能測試

測試環境

  • 客戶端
硬件:物理機一臺:16C,144G
軟件:shell調用java執行jar文件:java -jar mc.jar;
每個shell中最多起100個java線程,10個shell併發可進行1000併發測試;
每個線程死循環不停set或get;
SET與GET操作比1:10,KV大小30與KV大小500之比1:10。

  • 代理
硬件:虛擬機一臺:4C,4G
軟件:mcrouter-0.6,啓動4線程(線程數量=CPU核數量)。
  • Memcache
硬件:虛擬機一臺,4C,4G
軟件:memcache-1.4,根據需要創建N個實例。

4.1.代理對性能的影響

測試方法:每個線程連續set 10000個kv,key\value大小分別爲4字節、60字節。
  • 性能比較

客戶端併發數

memcache

mcrouter

請求數比較

memcache/mcrouter

每秒請求數

CPU負載

每秒請求數

CPU負載

1

3500

4%

1500

5%

2

10

20000

13%

9600

22%

2

50

43000

30%

23000

32%

2

100

104000

60%

40300

48%

3

500

320000

89%

64600

55%

5

1000

323000

90%

87000

82%

4


注:“客戶端併發數”不是指緩存只能支持這麼多併發,而是指在這麼多併發下,壓力測試出相應的每秒請求數。
測試結果:
壓力不同的情況下,memcache處理能力是mcrouter的2-5倍;
假設生產環境下服務器CPU的正常負載規劃不得超過30%,可認定memcache的性能是mcrouter的2倍。

  • 性能下降原因
以50個併發壓力測試以下三種情況下的性能:與訪問memcache比較,每個請求耗時依次增加9微秒、11微妙。

訪問方式

每秒請求數

每個請求耗時(微秒)

遠程訪問memcache

43000

23

本地訪問mcrouter

31300

32

遠程訪問mcrouter

23000

43


測試結果:
性能下降由代理自身和增加網絡節點導致,兩者性能消耗各50%左右。

4.2.不同實例數對性能的影響

測試方法:
以50個併發壓力測試,逐步增加集羣中的Memcache實例數。

Memcache實例數

每秒請求數

性能下降

代理服務器CPU負載

1

25564

0%

23%

2

24667

4%

30%

4

24093

6%

23%

8

24473

4%

32%

16

23290

9%

33%

32

21810

15%

23%


測試結果:
集羣中實例數增加後,CPU負載在一定範圍波動,性能逐步小範圍下降。

4.3.連接數

4.3.1.代理最大併發連接數

測試最佳吞吐量負載下的不同連接數的性能情況。
通過客戶端併發線程和每個線程set後的sleep時間,實現單個代理最佳吞吐量:每秒20000個請求。

客戶端併發線程

sleep時間(秒)

每秒請求數

Socket連接

平均延遲(毫秒)

CPU負載

5000

0.25

20000

4900

1

23%

10000

0.5

20000

5600

1.6

24%

20000

1

20000

7500

1.1

23%

30000

1.5

20000

14800

7.4

30%


測試結果:
單個代理在每秒20000請求的負載下,10000個客戶端併發連接可正常工作。

4.3.2.客戶端連接數設置

java memcached client中關於連接數的設置:
setInitConn:無效
setMinConn:無效
setMaxConn:有效
測試客戶端setMaxConn爲5時的每秒請求數:

單個客戶端併發線程

每秒請求

1

1512

5

5785

10

5928

50

5524

100

5979


測試結果:
最大連接設置爲5時,可爲客戶端提供每秒5000個請求,滿足要求。

4.4.穩定性

持續測試滿4天,mcrouter性能與負載穩定,具體數據如下:
GET平均79000次/秒,SET平均8700次/秒,合計平均87000次/秒。
服務器網絡吞吐量:平均進26MB/S,出23MB/S,合計49MB/S。
CPU負載:平均使用率82%。
內存、IO消耗忽略不計。

5.高可用

兩臺Mcrouter服務器分別爲應用A、應用B提供服務,通過keepalived組成集羣,形成互備。
兩臺服務器各安裝1個Mcrouter實例,端口相同。Keepalived監測Mcrouter端口11200,判斷是否需要漂移VIP。

 
檢測腳本:
vrrp_script chk_11200 {
    script "</dev/tcp/127.0.0.1/11200"       --檢測端口
    interval 2                           --監測頻率2秒                
    weight 2                            --監測成功權重+2
    fall 3                               --端口連續3次不通返回失敗
    rise 3                              --失敗後,端口連續3次正常返回成功
}

測試:每秒set一次,關閉mcrouter後,6秒內請求失敗,之後恢復正常。
16:08:02 set k9 true
16:08:03 set k10 true
16:08:04 set k11 true
11081 [main] ERROR com.danga.MemCached.MemCachedClient - ++++ exception thrown while writing bytes to server on set
11081 [main] ERROR com.danga.MemCached.MemCachedClient - 您的主機中的軟件中止了一個已建立的連接。
java.io.IOException: 您的主機中的軟件中止了一個已建立的連接。
……
16:08:05 set k12 false
16:08:06 set k13 false
16:08:08 set k14 false
16:08:10 set k15 true
16:08:17 set k16 true
16:08:18 set k17 true

6.實施

6.1.環境部署

  • 物理機:5臺
  • 虛擬機:11臺
  • mcrouter:2個實例
  • memcache:9個實例(8個組成pool1,1個作爲poolbak)
部署關係如下:

 

6.2.安裝配置

6.2.1.mcrouter

  • 操作系統
版本:Centos6.5-64bit
硬件配置:4C4G
軟件配置:關閉iptables,selinux
參數調整:

/etc/security/limits.conf
*               soft    nofile          102400
*               hard    nofile          102400
*               soft    nproc           102400
*               hard    nproc           102400
/etc/pam.d/login
session    required     /lib64/security/pam_limits.so
/etc/sysctl.conf
net.ipv4.ip_local_port_range = 9000 65500
net.ipv4.tcp_keepalive_time = 600
  • mcrouter
版本:v0.6.0
啓動命令:
cd /root/mcrouter
nohup mcrouter myflavor &
啓動選項:

cat myflavor-standalone 
{
  "libmcrouter_options": {
    "config_file": "/root/mcrouter/mcrouter.conf",
    "async_spool": "/root/mcrouter/spool",
    "num_proxies": "4",
    "reset_inactive_connection_interval": "0"
  },
  "standalone_options": {
    "log_file": "/root/mcrouter/mcrouter.log",
    "ports": "11200"
  }
}

配置文件:

{
  "pools": {
    "pool1": {
      "servers": [
        "192.168.188.150:11211",
        "192.168.188.150:11212",
        "192.168.188.150:11213",
        "192.168.188.150:11214",
        "192.168.188.150:11215",
        "192.168.188.150:11216",
        "192.168.188.150:11217",
        "192.168.188.150:11218"
      ]
    },
    "pool1_bak": {
      "servers": [
        "192.168.188.150:11219",
      ]
    }
  },
  "route": {
    "type": "FailoverRoute",
    "default_policy": "PoolRoute|pool1",
    "children": [ "PoolRoute|pool1", "PoolRoute|pool1_bak" ],
    "failover_errors": [ "tko" ]
  }
}

6.2.2.memcache

  • 操作系統
版本:Centos6.5-64bit
硬件配置:4C4G
軟件配置:關閉iptables,selinux
  • memcache
版本:libevent-1.3,memcached-1.4.24
啓動命令:
memcached -d -l 192.168.188.150 -p 11211 -m 3200 -c 4096 -u nobody -v >> /root/memcache_log/11211.log 2>&1

6.3.key規劃

Key包含5個字節的前綴,如“p1aaa”:
前兩位指定前綴路由,如p1作爲前綴的轉發到pool1中(目前未使用該特性,設計key便於以後使用);
後三位作爲功能模塊的標識,確保不同功能的緩存不會衝突,同時便於運維管理,有問題的key可立即定位來源,維護清單:

前綴

項目

功能模塊

分配日期

p1aaa

項目A  

  主站session共享

20150929

p1aab

項目B  

  主站session共享

20150929

7.監控

通過Zabbix實時監控操作系統的CPU、內存、網絡負載,以及軟件本身的關鍵指標,分別用於實時報警和負載分析。

7.1.mcrouter

可用性 </dev/tcp/127.0.0.1/11200;echo $?
異常Memcache實例 echo "stats suspect_servers" | nc 0 11200 | grep -v END
每秒SET次數 echo "stats" | nc 0 11200 | grep cmd_set_count | awk '{print $3}'
每秒GET次數 echo "stats" | nc 0 11200 | grep cmd_get_count | awk '{print $3}'
連接數 echo "stats" | nc 0 11200 | grep num_client | awk '{print $3}'

7.2.memcache

每秒SET次數 echo "stats" | nc ip 11211 | grep cmd_set | awk '{print $3}'
每秒GET次數 echo "stats" | nc ip 11211 | grep cmd_get | awk '{print $3}'
每秒GET命中次數 echo "stats" | nc ip 11211 | grep get_hits | awk '{print $3}'
每秒被踢出的未過期KV數量 echo "stats" | nc ip 11211 | grep evictions | awk '{print $3}'
每秒回收的已到期KV數量 echo "stats" | nc ip 11211 | grep “ reclaimed “ | awk '{print $3}'
各SLAB中“最老KV的存活時間“的最小值 echo "stats items" | nc ip 11211 | grep age | awk '{print $3}' | sort | head -1

8.問題處理

8.1.對象序列化

mcrouter只支持assci協議,不支持binary協議,因此對象要序列化之後再存儲(未測試驗證);

8.2.超時配置

tomcat session共享,延遲至少設置爲3秒,默認100毫秒會超時
<Manager className="de.javakaffee.web.msm.MemcachedBackupSessionManager" 
         memcachedNodes="n1:192.168.n.n:11200" 
         requestUriIgnorePattern= ".*\.(png|gif|jpg|css|js)$" 
         sessionBackupAsync= "false" 
         sessionBackupTimeout= "3000"    --至少設置爲3000毫秒
         transcoderFactoryClass="de.javakaffee.web.msm.JavaSerializationTranscoderFactory"/>

否則會報錯:
警告: Could not store session 12B8D86FC987FBAA902B559E735451B5-n1 in memcached.
net.spy.memcached.internal.CheckedOperationTimeoutException: Timed out waiting for operation - failing node: /192.168.n.n:11200

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章