吐槽memcached proxy 之memagent

前言:

本人在網絡上並未找到magent的實際應用的例子,都是一些測試,本人想通過java調用代理magent來解決單點故障的問題,但是沒有相關的文檔API介紹,後來採用java memcached client直接調用magent服務,居然可以的。其實本人在學習memcached與magent之後,發現網絡上,很多人並沒有把magent理解對,並不知道它是什麼一個角色,只知道是個memcached代理,然後就互相抄襲拷貝對方的文章,有的壓根都沒有自己親手測試過。就算自己真正動手測試過magent的,但是並沒有把它運用到實際的項目中,起碼在本人看來,沒有相關的客戶端API,但是magent其實跟memcached的調用方法一樣,magent的簡介裏面也介紹說,調用magent服務的方法跟調用memcached服務的方法一樣,而且我們可以看到magent的命令模式與memcached的都差不多。

其實對於magent,大家可以把它理解成是memcached客戶端的一個擴展,它同樣有java client、spymemcached、xmemcached它們的功能,支持ketama一致性hash算法,多了一個memcached備份功能而已。在linux系統下,我們可以用telnet進入magent啓動的進程服務set、get值的,寫代碼的時候,可以直接用java memcached client調用magent,只需要把調用memcached服務的地址改成magent的服務地址

 

很多人都理解成了,我只要在memcached服務器上配置好magent,客戶端還是採用如memcached-client-for-java來調用memcached,當set、get值的時候,magent會自動分配並自動把值保存到備份服務器上,其實錯了,既然你還是採用的memcached-client-for-java,那麼就是運用的memcached-client-for-java裏面的一致性hash算法了,而不是採用的magent裏面的一致性hash算法了。這樣做,其實還是撇開了magent。所以必須改成調用magent服務

 

這也是:http://www.oschina.net/question/858822_77565 中提出:

但是測試結果告訴,當A或者B,或者AB都當掉之後,取值爲null。也就是說,magent代理服務並沒有將值set到備份服務上。

的答案

 

就如一個公司的創始人(客戶端-java memcached client)一開始有很多任務(key value)要分配(set、get)給各員工(memcached服務器)做,後來請來了一個CEO(magent),想讓他來代理分配 (set、get) 工作,解決員工離職(單點故障)問題。所以正常情況下,應該是,創始人(客戶端)把所有的任務(key value)直接交給CEO(magent)就行了,而不是創始人(客戶端-java memcached client)還親自再分配工作給員工(memcached服務器),應該直接讓CEO(magent-java memcached client)自己去分配工作,解決員工離職(單點故障)。所以只需要創始人(客戶端-java memcached client)指示CEO(magent-java memcached client)去做就行了,也就是java memcached client調用magent就行了,其他的都不用管了

 

 

magent優於java memcached client、spymemcached、xmemcached解決了單點故障, 但是它本身很不完善,譬如一個緩存服務器memcached上面存了(1,2,3這些值),現在它宕機了,但magent還能通過備份memcached服務器上獲取到(1,2,3)數據。但是把宕機的memcached服務器重啓後,雖然備份的memcached服務器上有(1,2,3)數據,但magent會再次去重新啓動的memcached服務器上取數據,卻又由於重啓的memcached服務器數據丟失,所以取不到相應的數據了。所以說其應用在實際項目中並不現實

以上純屬個人的理解,技術有限,如有錯誤,還望大家指出糾正,互相學習

 

-------------------------------------------------------------------------------------------------------------------------------------

memcached不提供集羣功能,因爲集羣的要素是負載均衡和單點恢復;
memcached在server端之間是不會進行通訊的,目前比較流行的有下面第幾種替代的解決方案,雖不是很完美,但是能滿足一些基本需求。

 

1、通過客戶端進行hash算法 存到不同的mamcached server上
  該方法是在客戶端存值之前先對key進行hash,把算出的hash對應的不同的memcached server上,這樣保證了系統中的數據是存放到不同的mamcached server上,分散了在單臺機器上的風險,提高了性能,缺點是單一臺機器down後,它上面的數據將會丟失,沒法恢復,不能動態增加機器,動態增加機器後,本地key對應server會發生改變,以前的老數據將不能取到,只能系統全部重啓
 當前的memcached java client就提供了該功能,代碼如下
  String[] serverlist = {"10.10.9.116:11211","10.10.9.116:11212"}; //定義了兩臺cache機器
  SockIOPool pool = SockIOPool.getInstance();
  pool.setServers(serverlist);

  Integer weights[]=new Integer{1,3};//可以根據每臺機器的性能設置不同的權重

  pool.setWeights(weights);
  pool.setInitConn(initConn);
  pool.setMinConn(minConn);
  pool.setMaxConn(maxConn);
  pool.setMaintSleep(mainSleep);
  pool.setNagle(false);
  pool.setFailover( true );
  pool.initialize();
  instance = this;

2、現在通用的使用一致hash算法 ,可以最大限度避免第一條中的缺點,可以動態的增加或減少機器而對現有的其他機器上的數據保持不變,只會影響小部分的數據的存取。
目前 java client、spymemcached、xmemcached都支持一致hash,同時還採用了虛擬節點的一致性hash算法,使memcached儘量的負載均衡,可以參考:http://blog.csdn.net/fdipzone/article/details/7170045
下面是memcached java client的一段代碼,需要設置的幾個關鍵屬性:
  pool.setNagle(true);//這是開啓一個nagle 算法。改算法避免網絡中充塞小封包,提高網絡的利用率 ;
  pool.setHashingAlg(SockIOPool.CONSISTENT_HASH);//設置爲一致性hash算法,在memcached集羣時使用
  pool.setFailover( true ); //集羣用  設置池的故障轉移的標誌  當一個memcached服務器失效的時候客戶端默認會   failover另一個服務去.如果失效的服務器恢復運行,客戶端會返回到原來連接的服務器.一般不要使用該功能
  pool.setAliveCheck(true);//表示在使用Socket以前是否先檢查Socket狀態

3、使用memagent

上面1、2都是針對客戶端在訪問時候進行key的hash算法,而3的方法是基於在memcached服務端安裝代理進行分發,它可以進行數據的備份,當某臺機器down後會自動從備份機器取對應的數據有如下特點:

     A、和每個memcache server保持多個長連接,效果是減少memcache server保持的連接數量及創建銷燬連的開銷。不過,memcache本身就支持大併發連接,這個功能也就沒什麼特別的說道。

     B、支持memcache的binary協議命令,實現請求的轉發。

     C、和memcache一樣,基於libevent的事件驅動來處理IO。

     D、支持ketama 的一致性hash算法。

     E、支持memcache backup集羣,當memcache集羣有機器掛了,memagent會將get請求轉向memcache backup集羣。這個功能對於cache的穩定性要求高的場景下會有用武之地。

magent是一款開源的Memcached代理服務器軟件,其項目網址爲:
http://code.google.com/p/memagent/


優點:系統自動備份,當一臺機器down後自動切換到備份機器

 

------------------------------------------------------------------------------------------------------------------------------------

測試環境:
四臺機器:192.168.1.105、192.168.1.151、192.168.1.152、192.168.1.153
192.168.1.105爲主機,151、152、153爲在105上安裝的虛擬機
四臺機器上均安裝了memcached與magent

1.151、152、153三臺機器上,分別啓動兩個memcached進程,端口均爲11211、11212
2.現以105機器爲代理機器和備份機器,即在105的機器上啓動一個備份的memcached服務,端口爲11213,同時再啓動一個magent代理進程服務,端口爲10000,做主magent服務。
3.另外在151機器上再啓動一個magent服務,作爲備份的magent服務,萬一105機器宕機,則可以啓動備份的magent服務,實際配屬配置需根據實際應用環境來定,我這邊只是測試一下該集羣的工作機制與邏輯。


在151機器上啓動兩個memcached進程:
memcached -m 1 -u root -d -l 192.168.1.151 -p 11211
memcached -m 1 -u root -d -l 192.168.1.151 -p 11212
在152機器上啓動兩個memcached進程:
memcached -m 1 -u root -d -l 192.168.1.152 -p 11211
memcached -m 1 -u root -d -l 192.168.1.152 -p 11212
在153機器上啓動兩個memcached進程:
memcached -m 1 -u root -d -l 192.168.1.153 -p 11211
memcached -m 1 -u root -d -l 192.168.1.153 -p 11212
在105機器上啓動一個memcached進程:備份用
memcached -m 1 -u root -d -l 192.168.1.105 -p 11213
在105機器上啓動一個magent進程:
magent -u root -n 51200 -l 192.168.1.105 -p 10000 -s 192.168.1.151:11211 -s 192.168.1.151:11212 -s 192.168.1.152:11211 -s 192.168.1.152:11212 -s 192.168.1.153:11211 -s 192.168.1.153:11212 -b 192.168.1.105:11213
在151機器上啓動一個magent進程:備份用
magent -u root -n 51200 -l 192.168.1.151 -p 10000 -s 192.168.1.151:11211 -s 192.168.1.151:11212 -s 192.168.1.152:11211 -s 192.168.1.152:11212 -s 192.168.1.153:11211 -s 192.168.1.153:11212 -b 192.168.1.105:11213

在105機器上:
[root@localhost ~]# memcached -m 1 -u root -d -l 192.168.1.105 -p 11213
[root@localhost ~]# magent -u root -n 51200 -l 192.168.1.105 -p 10000 -s 192.168.1.151:11211 -s 192.168.1.151:11212 -s 192.168.1.152:11211 -s 192.168.1.152:11212 -s 192.168.1.153:11211 -s 192.168.1.153:11212 -b 192.168.1.105:11213
[root@localhost ~]# telnet 192.168.1.105 10000
Trying 192.168.1.105...
Connected to 192.168.1.105.
Escape character is '^]'.
set key1 0 0 1
1
STORED
set key2 0 0 1
2
STORED
set key3 0 0 1
3
STORED
set key4 0 0 1
4
STORED
set key5 0 0 1
5
STORED
set key6 0 0 1
6
STORED
set key7 0 0 1
7
STORED
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.151 11211
Trying 192.168.1.151...
Connected to 192.168.1.151.
Escape character is '^]'.
get key1
END
get key2
END
get key3
END
get key4
VALUE key4 0 1
4
END
get key5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.151 11212
Trying 192.168.1.151...
Connected to 192.168.1.151.
Escape character is '^]'.
get key1
END
get key2
END
get key3
END
get key4
END
get key5
VALUE key5 0 1
5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.152 11211
Trying 192.168.1.152...
Connected to 192.168.1.152.
Escape character is '^]'.
get key1
END
get key2
END
get key3
END
get key4
END
get key5
END
get key6
VALUE key6 0 1
6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.152 11212
Trying 192.168.1.152...
Connected to 192.168.1.152.
Escape character is '^]'.
get key1
VALUE key1 0 1
1
END
get key2
END
get key3
END
get key4
END
get key5
END
get key6
END
get key7
VALUE key7 0 1
7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.153 11211
Trying 192.168.1.153...
Connected to 192.168.1.153.
Escape character is '^]'.
get key1
END
get key2
VALUE key2 0 1
2
END
get key3
END
get key4
END
get key5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.153 11212
Trying 192.168.1.153...
Connected to 192.168.1.153.
Escape character is '^]'.
get key1
END
get key2
END
get key3
VALUE key3 0 1
3
END
get key4
END
get key5
END
get key6
END
get key7
END
quit
Connection closed by foreign host.
[root@localhost ~]# telnet 192.168.1.105 11213
Trying 192.168.1.105...
Connected to 192.168.1.105.
Escape character is '^]'.
get key1
VALUE key1 0 1
1
END
get key2
VALUE key2 0 1
2
END
get key3
VALUE key3 0 1
3
END
get key4
VALUE key4 0 1
4
END
get key5
VALUE key5 0 1
5
END
get key6
VALUE key6 0 1
6
END
get key7
VALUE key7 0 1
7
END
quit
Connection closed by foreign host.

 

telnet :No route to host
解決方法:
在目標機器上執行:iptables -F


以上只測試了各值的分佈情況,看值是否正確的存進去了,確認無誤。至於宕機與重啓還有調用備份的magent服務的測試這邊不貼出來了。不過接着需要解決一個問題,那就是宕機情況下,可以從備份的機器上獲取到宕機上原有的數據,可是宕機重啓後,該重啓機器上的值爲空了,獲取不到對應的值,該集羣系統不會自動再去備份的機器上去取相關的數據了

 

magent:處理了備份數據,若其中一臺緩存服務器宕機,magent通過訪問備份的緩存服務器,同樣能獲取到數據。但是重啓了緩存服務器,magent會再次請求此緩存服務器,不再請求備份服務器,卻由於重啓的緩存服務器上面的數據丟失,從而取不到相應的數據了。 

 

附:

magent的操作與memcache的操作一致,但具體部分返回結果所有差別

啓動命令
下表僅列出比較常用的參數,更多參數使用memcached –h進行查看



 1.寫入數據
<command name> <key> <flags> <exptime> <bytes>\r <data block>\r
其中“\r”表示回車換行
<command name> 可以是”set”, ”add”, ”replace”
“set”表示按照相應的<key>存儲該數據,沒有的時候增加,有的覆蓋。
“add”表示按照相應的<key>添加該數據,但是如果該<key>已經存在則會操作失敗。
“replace”表示按照相應的<key>替換數據,但是如果該<key>不存在則操作失敗。
<key> 客戶端需要保存數據的key
<flags> 是一個16位的無符號的整數(以十進制的方式表示)
該標誌將和需要存儲的數據一起存儲,並在客戶端get數據時返回。
客戶可以將此標誌用做特殊用途,此標誌對服務器來說是不透明的。
<exptime> 過期的時間
若爲0表示存儲的數據永遠不過時(但可被服務器算法:LRU等替換)。
如果非0(unix時間或者距離此時的秒數),當過期後,服務器可以保證用戶得不到該數據(以服務器時間爲標準)。
<bytes> 需要存儲的字節數(不包含最後的”\r”)
當用戶希望存儲空數據時,<bytes>可以爲0,最後客戶端需要加上”\r”作爲”命令頭”的結束標誌。
<data block>\r 數據內容
緊接着”命令頭”結束之後就要發送數據塊(即希望存儲的數據內容),最後加上”\r”作爲此次通訊的結束。
2.獲取數據
get <key>*\r
<key>* 表示一個或者多個key(以空格分開)
“\r” 命令頭的結束
3.刪除數據
delete <key> <time>\r
<key> 需要被刪除數據的key
<time> 客戶端希望服務器將該數據刪除的時間(unix時間或者從現在開始的秒數)
“\r” 命令頭的結束

  • 8332e680-fb25-339d-bf30-8a398bc31091-thumb.jpg
  • 大小: 108.1 KB
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章