原創 Memcache分組和同步機制的實現
分享一下我老師大神的人工智慧教程!零基礎,通俗易懂!http://blog.csdn.net/jiangjunshow
也歡迎大家轉載本篇文章。分享知識,造福人民,實現我們中華民族偉大復興!
[原創] Memcache分組和同步機制的實現
作者:heiyeluren
部落格:http://blog.csdn.net/heiyeshuwu
時間:2007-06-25
【Memcache同步類的實現思想】
首先我們明確瞭解就是Memcache是一個簡單、快速、高效的分散式基於記憶體的快取工具,一般用於網站等資料庫資料快取、高速交換資訊的快取,比如Session資料等等。Memcache最主要的特點就是兩個:一是它是基於記憶體的高效Hash類快取方式,二是使用了LUR演算法來進行資料有效期控制,這兩點就能夠很好的滿足我們的普通網站的應用。(關於Memcache的使用和協議請參考我的另一篇文章:《Memcache的使用和協議分析詳解》http://blog.csdn.net/heiyeshuwu/archive/2006/11/13/1380838.aspx)
Memcache本身也存在一些不足,不能說是不足,至少是為了滿足它高效的存取,所以沒有去實現的功能,包括:一是本身沒有內建分散式功能,無法實現使用多臺Memcache伺服器來儲存不同的資料,最大程度的使用相同的資源;二是無法同步資料,容易造成單點故障。
那麼其實都可以通過我們的Memcache的客戶端程式來解決的,首先我們PHP的PECL中的Memcache擴充套件能夠有效的解決Memcache的分散式問題,主要的介面就是 addServer() 函式,具體關於addServer()函式的實現可以參考該擴充套件原始碼。那麼現在就存在第二個問題,就是說無法同步資料,可以理解為MySQL中Master/Slave的機制,就是說如果我們有多臺的Memcache伺服器,使用addServer函式的話,每個伺服器儲存的資料都是唯一的,那麼任何一臺伺服器宕機的話,那麼這臺伺服器上的儲存資料將丟失無法訪問,這樣肯定是無法充分發揮我們Memcache威力的,那麼我們就需要一個方式來解決這個問題,這個就是本文主要要探討解決的問題:關於在多臺Memcache伺服器中分組和同步資料的問題。
針對這個問題我構建了一個PHP寫的Class,大致的思想就是把多臺Memcache伺服器分成兩組,每組的伺服器數量可能是一樣的(如果總數是複數的話),數量不一樣也沒關係,寫資料的時候往兩組伺服器都寫,資料是一樣的,那麼A組伺服器的資料和B組伺服器的資料是一樣的,如果A組伺服器中某一臺機器宕機了,則能夠從B組伺服器中提取出資料來,這樣能夠有效的避免單點故障,特別是在高效能要求的網站當中。當然,相應的操作也是有開銷的,主要的開銷在於網路連線,就是說連線到A組伺服器中發現沒有資料,則去訪問B組伺服器。另外還有就是在訪問資料過程中,都是從A組開始的,那麼併發量很高的情況下,A組伺服器的壓力比較大,那麼我還實現了對A組和B組伺服器隨機訪問的機制,如果在隨機的組中沒有發現數據則訪問另外一個組,這樣A組和B組的壓力就是1/2了,能夠有效的面對高負載的情況。
同樣的,這個類是可以擴充套件的,比如你可以修改成為能夠滿足兩組以上的伺服器,但是這樣邏輯會更復雜,我基於簡便期間,目前就考慮了兩組伺服器的情況,畢竟一般公司來說,沒有很多機器去做Memcache的快取,所以兩組已經能夠滿足大部分要求了。
PS:如果有興趣的話,可以完全自己重新實現Memcache的addServer擴充套件,比如增加addGroup之類的方法,能夠更有效的解決同步和分組的問題。同樣的,如果想節省資源的使用,可以把Memcached當作執行緒的方式來執行。
【Memcache同步類的實現程式碼】
本類經過我大致的各種測試,執行還算穩定,當然,使用時還是請多加註意。
<? php
/* *
* Memcache 操作類 (支援同步和分組訪問)
*
* author : heiyeluren <http://blog.csdn.net/heiyeshuwu>
* created : 2007-06-21
* lastModifed: 2007-06-21
*/
/* *
* Memcache操作類
*/
class MemcacheOpt
{
// ---------------------
// 屬性定義
//---------------------
/* *
* 是否進行多組同步
*/
var $isGroup = true ;
/* *
* 多組同步情況下是否使用減輕負載的隨機存取
*/
var $isRandom = true ;
/* *
* 預設的Memcache伺服器埠
*/
var $mmcPort = 11211 ;
/* *
* 儲存原始組資訊
*/
var $groups = array ();
/* *
* 儲存第一、二組Memcache伺服器資訊
*/
var $group1 = array ();
var $group2 = array ();
/* *
* 儲存第一、二組連線物件
*/
var $mmc1 = '' ;
var $mmc2 = '' ;
// ---------------------
// 內部操作方法
//---------------------
/* *
* 顯示錯誤資訊
*
* @param string $msg 需要顯示訊息的內容
* @param string $type 訊息型別,error是普通錯誤訊息,fatal 是致命錯誤,將終止程式執行, message 是普通訊息,預設狀
態
* @return bool true
*/
function showMessage( $msg , $type ){
$msg .= " " ;
switch ( $type ){
case ' error ' :
echo ( " Memcache Error: " . $msg );
break ;
case ' fatal ' :
die ( " Memcache Fatal: " . $msg );
break ;
case ' message ' :
echo ( " Memcache Message: " . $msg );
break ;
default :
echo ( " Memcache Error: " . $msg );
}
return true ;
}
/* *
* 建構函式 (初始化分組和連線到伺服器)
*/
function MemcacheOpt( $hostArray , $hostArray2 = array ()){
if ( ! is_array ( $hostArray ) || empty ( $hostArray )){
$this -> showMessage( ' Memcache host list invalid ' , ' fatal ' );
}
$this -> groups = array_merge ( $hostArray , $hostArray2 );
$this -> splitGroup( $hostArray , $hostArray2 );
$this -> connect();
}
/* *
* 對組進行切分 (按照是否需要分組進行相應的切分)
*
* @param array $hostArray 主機陣列列表1
* @param array $hostArray2 主機陣列列表2
* @return void
*/
function splitGroup( $hostArray , $hostArray2 = array ()){
// 如果只有一臺機器則不使用分組
if ( count ( $hostArray ) < 2 && empty ( $hostArray2 )){
$this -> isGroup = false ;
}
// 使用分組
if ( $this -> isGroup){
if ( is_array ( $hostArray2 ) && ! empty ( $hostArray2 )){
$this -> group1 = $hostArray ;
$this -> group2 = $hostArray2 ;
} else {
$count = ceil ( count ( $hostArray ) / 2 );
$this -> group1 = array_splice ( $hostArray , 0 , $count );
$this -> group2 = array_splice ( $hostArray , 0 );
}
} else {
$this -> group1 = $hostArray ;
}
}
/* *
* 連線到Memcache伺服器
*/
function connect(){
if ( ! is_array ( $this -> group1) || empty ( $this -> group1)){
$this -> showMessage( " Memcache host1 array invalid " , ' error ' );
return false ;
}
// 連線第一組Memcache伺服器
$this -> mmc1 = new Memcache;
foreach ( $this -> group1 as $hosts ){
$tmp = explode ( " : " , $hosts );
$host = $tmp [ 0 ];
$port = ( ! isset ( $tmp [ 1 ]) || $tmp [ 1 ] == '' ) ? $this -> mmcPort : $tmp [ 1 ];
$this -> mmc1 -> addServer( $host , $port );
}
// 如果需要分組則連線第二組Memcache伺服器
if ( $this -> isGroup){
if ( ! is_array ( $this -> group2) || empty ( $this -> group2) ){
$this -> showMessage( " Memcache host2 array invalid " , ' error ' );
return false ;
}
$this -> mmc2 = new Memcache;
foreach ( $this -> group2 as $hosts ){
$tmp = explode ( " : " , $hosts );
$host = $tmp [ 0 ];
$port = ( ! isset ( $tmp [ 1 ]) || $tmp [ 1 ] == '' ) ? $this -> mmcPort : $tmp [ 1 ];
$this -> mmc2 -> addServer( $host , $port );
}
}
}
/* *
* 關閉Memcache伺服器連線
*/
function close(){
if ( is_object ( $this -> mmc1)){
$this -> mmc1 -> close();
}
if ( is_object ( $this -> mmc1)){
$this -> mmc1 -> close();
}
return true ;
}
/* *
* 資料操作核心函式
*
* @param string $optType 操作型別,主要有 add, set, replace, delete, flush
* @param string $key 關鍵字,如果是 add,set,replace,delete 需要提交key引數
* @param string $val 關鍵字對應的值,如果是 add, set,replace 需要提交value引數
* @param int $expire 資料有效期,如果是 add,set,replace需要提交expire引數
* @return mixed 不同的需要產生不同的返回
*/
function opt( $optType , $key = '' , $val = '' , $expire = '' ){
if ( ! is_object ( $this -> mmc1)){
$this -> showMessage( " Not availability memcache connection object " , ' fatal ' );
}
if ( $this -> isGroup && ! is_object ( $this -> mmc2)){
$this -> showMessage( " Group 2 memcache host connection object not availability " , ' error ' );
}
// 加入資料操作
if ( $optType == ' add ' || $optType == ' set ' || $optType == ' replace ' ){
$this -> mmc1 -> set( $key , $val , false , $expire );
if ( $this -> isGroup && is_object ( $this -> mmc2)){
$this -> mmc2 -> set( $key , $val , false , $expire );
}
return true ;
}
// 獲取資料操作
if ( $optType == ' get ' ){
// 預設獲取第一組資料
if ( ! $this -> isGroup || ! is_object ( $this -> mmc2)){
return $this -> mmc1 -> get( $key );
}
// 分組情況下逐組訪問
$num = ( $this -> isRandom ? rand ( 1 , 2 ) : 1 );
$obj = " mmc " . $num ;
$val = $this -> $obj -> get( $key );
// 如果沒有提取到資料,則訪問另外一組
if ( $val == "" ){
switch ( $num ){
case 1 : $val = $this -> mmc2 -> get( $key ); break ;
case 2 : $val = $this -> mmc1 -> get( $key ); break ;
default : $val = $this -> mmc1 -> get( $key );
}
}
return $val ;
}
// 刪除資料操作
if ( $optType == ' delete ' ){
$this -> mmc1 -> delete( $key , $expire );
if ( $this -> isGroup && is_object ( $this -> mmc2)){
$this -> mmc2 -> delete( $key );
}
return true ;
}
// 清空資料操作
if ( $optType == ' flush ' ){
$this -> mmc1 -> flush ();
if ( $this -> isGroup && is_object ( $this -> mmc2)){
$this -> mmc2 -> flush ();
}
return true ;
}
}
// ---------------------
// 外部操作方法
//---------------------
//增加一個元素
function add( $key , $val , $expire = '' ){
return $this -> opt( ' add ' , $key , $val , $expire );
}
// 增加一個元素
function set( $key , $val , $expire = '' ){
return $this -> opt( ' set ' , $key , $val , $expire );
}
// 替換一個元素
function replace( $key , $val , $expire = '' ){
return $this -> opt( ' replace ' , $val , $expire );
}
// 獲取一個元素
function get( $key ){
return $this -> opt( ' get ' , $key );
}
// 刪除一個元素
function delete( $key , $timeout = '' ){
return $this -> opt( ' delete ' , $key , '' , $timeout );
}
// 讓所有的元素過期 (本介面不要輕易使用)
function flush (){
return $this -> opt( ' flush ' );
}
/* *
* 獲取所有Memcache伺服器狀態
*/
function getStats(){
$status = array ();
// 單獨連線到每臺Memcache
foreach ( $this -> groups as $key => $hosts ){
$tmp = explode ( " : " , $hosts );
$host = $tmp [ 0 ];
$port = ( ! isset ( $tmp [ 1 ]) || $tmp [ 1 ] == '' ) ? $this -> mmcPort : $tmp [ 1 ];
$conn = new Memcache;
$conn -> connect( $host , $port );
$s = $conn -> getStats();
$s [ ' host ' ] = $host ;
$s [ ' port ' ] = $port ;
$status [ $key ] = $s ;
}
return $status ;
}
/* *
* 獲取所有Memcache伺服器版本號
*/
function getVersion(){
$version = array ();
$stats = $this -> getStats();
foreach ( $stats as $key => $s ){
$v [ ' host ' ] = $s [ ' host ' ];
$v [ ' port ' ] = $s [ ' port ' ];
$v [ ' version ' ] = $s [ ' version ' ];
$version [ $key ] = $v ;
}
return $version ;
}
}
?>
【Memcache同步類的測試】
在本機開啟多個Memcache守護程序:
[ ~ ] $ ps auxww | grep memcachedheiyeluren 98466 0.0 0.2 6088 5676 ?? SsJ 7 :50下午 0 : 00.04 /usr/local/memcache/bin/memcached -d -m 32 -l 10.62.240.9 -p 11214
heiyeluren 98437 0.0 0.2 6088 5676 ?? SsJ 7 :50下午 0 : 00.04 /usr/local/memcache/bin/memcached -d -m 32 -l 10.62.240.9 -p 11213
heiyeluren 98425 0.0 0.2 6088 5676 ?? SsJ 7 :50下午 0 : 00.05 /usr/local/memcache/bin/memcached -d -m 32 -l 10.62.240.9 -p 11212
heiyeluren 98228 0.0 0.2 6088 5676 ?? SsJ 7 :48下午 0 : 00.05 /usr/local/memcache/bin/memcached -d -m 32 -l 10.62.240.9 -p 11211
MemcacheOpt類測試程式碼:
<? php
require_once ( " MemcacheOpt.class.php " );
// 操作程式碼
$hostArray = array ( " 10.62.240.9 " , " 10.62.240.9:11212 " , " 10.62.240.9:11213 " , " 10.62.240.9:11214 " );
$m = new MemcacheOpt( $hostArray );
$m -> add( " key1 " , " key1_value " , 30 );
$m -> add( " key2 " , " key2_value " , 30 );
$m -> set( " key3 " , " key3_value " , 30 );
$m -> set( " key4 " , " key4)value " , 30 );
echo $m -> get( " key1 " ) . " " ;
echo $m -> get( " key2 " ) . " " ;
echo $m -> get( " key3 " ) . " " ;
echo $m -> get( " key4 " ) . " " ;
print_r (