天天看點

libmemcached1.0.2 C/C++ API使用執行個體、測試及修改

memcached是一個分布式的緩存系統,且其分布式是一種“輕量級”的分布式,完全依賴用戶端庫來實作,libmemcached就是一個開源的C/C++庫。

        使用libmemcached的C/C++ API用戶端庫資料及官方資料都很少,且網絡上存在的C/C++ libmemcached執行個體都是采用的MOD的分布式算法,其缺點顯而易見,當存在失效的memcached server或重新加入新的server時,容易造成系統的“震蕩”。libmemcached本身已經支援一緻性hash算法,一緻性算法在處理“加入”或“删除”server方面具有優良的特性,這裡就不具體分析了,請查閱一緻性算法相關資料。

        今天對libmemcached-1.0.2版本的使用進行一個簡單的測試,以使應用支援dead server的自動隔離和自動連接配接。

        libmemcached的C/C++ API使用及測試執行個體如下:

[cpp] view plaincopy

#include <stdio.h>    

#include <stdlib.h>   

#include <string.h>  

#include <time.h>  

#include <unistd.h>  

#include <libmemcached/memcached.h>   

int main(int argc, char *argv[])   

{   

  memcached_st *memc;   

  memcached_return rc;   

  memcached_server_st *servers;   

  //connect multi server   

  memc = memcached_create(NULL);   

  servers = memcached_server_list_append(NULL, (char*)"localhost", 11211, &rc);   

  servers = memcached_server_list_append(servers, (char*)"localhost", 30000, &rc);   

  rc = memcached_server_push(memc, servers);  

  memcached_server_free(servers);    

  memcached_behavior_set(memc,MEMCACHED_BEHAVIOR_DISTRIBUTION,MEMCACHED_DISTRIBUTION_CONSISTENT);  

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, 20) ;  

  //  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_REMOVE_FAILED_SERVERS, 1) ;  // 同時設定MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT 和 MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS  

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, 5) ;  

  memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS, true) ;  

  int time_sl = 0 ;    

  int times = 0 ;  

  while(times++<100000)  

  {  

      //save data   

      const char  *keys[]= {"key1", "key2", "key3","key4"};   

      const  size_t key_length[]= {4, 4, 4, 4};   

      char *values[] = {"This is 1 first value", "This is 2 second value", "This is 3 third value"," this is 4 forth value"};   

      size_t val_length[]= {21, 22, 21, 22};  

      int i = 0;  

      for (; i < 4; i++)        

      {  

          rc = memcached_set(memc, keys[i], key_length[i], values[i], val_length[i], (time_t)180,(uint32_t)0);     printf("key: %s  rc:%s\n", keys[i], memcached_strerror(memc, rc));   // 輸出狀态        

      }   

    printf("time: %d\n", time_sl++) ;  

    sleep(1) ;  

  }   

     //free   

      memcached_free(memc);   

      return 0;   

}  

        執行個體非常簡單,但包含了使用libmemcached的基本流程:

        1)建立memcached_st結構;

        2)添加memcached server;

        3)設定libmemcached庫的一些屬性(hash算法,重試次數及重試時間等);

        4)調用基本的API(如get,set等)...;

        5)釋放memcached_st結構;

       本執行個體的關鍵在于3)中設定libmemcached庫的屬性。

        隻有一緻性算法支援dead server的自動隔離和自動連接配接,參看run_distribution函數中隻有分布式算法為MEMCACHED_DISTRIBUTION_CONSISTENT*類的才調用update_continuum進行更新,其它都沒有任何操作,是以這裡設定了memcached_behavior_set(memc,MEMCACHED_BEHAVIOR_DISTRIBUTION,MEMCACHED_DISTRIBUTION_CONSISTENT);

        run_distribution函數為自動隔離dead server的代碼,其主要調用在backoff_handling函數中:

調用條件同時滿足:server->server_failure_counter >= server->root->server_failure_limit 和memcached_st結構的flag設定了MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS标志,如果不設定MEMCACHED_BEHAVIOR_AUTO_EJECT_HOSTS标志,則每次set/get等操作都試圖連接配接dead server。

     在函數update_continuum中,

next_retry小于目前時間時,則表示标志目前server有效,如果不設定預設為0,在backoff_handling函數中設定為1,永遠小于目前時間,是以即使失效也不會自動剔除。MEMCACHED_BEHAVIOR_DEAD_TIMEOUT标志就是設定它的重試時間。

實驗過程:

一、初始階段:在同一虛拟機開啟2個memcached server(端口分别為11211和30000)

             # memcached -vv -u root -p 11211

            #  memcached -vv -u root -p 30000

二、啟動測試程式

其中下面兩行,表示目前servers包括2個:分别為:localhost:11211和localhost:30000,1和160分别表示weight和在一緻性算法中每個server在圓上的點數。

     ketama_weighted:localhost|11211|1|160

     ketama_weighted:localhost|30000|1|160

 同樣可以看出:key1、key2和key3映射到localhost:11211上,key4映射到localhost:30000上。

正常情況下,會一直處于此狀态下,下面模拟存在server down掉。

三、memcached server 失效和恢複

        起始是使用libmemcached-1.0.2進行的測試,發現存在明顯的錯誤(至少2處),如果還沒有更新或使用1.0.2版本的,那麼可以直接使用1.0.3版本即可(寫此文時的最新版本),否則需要盡快更新,很快官方釋出了libmemcached-1.0.3版本,測試基本正常。

1、libmemcached-1.0.2版本測試圖:

1)關閉memcached server,測試set失敗情況

       在time:1339時刻停止了memcached server localhost:30000伺服器,memcached_set結果傳回SERVER HAS FAILED AND IS DISABLED UNTIL TIMED RETRY(使用memcached_strerror轉換的錯誤資訊),此時set、get等并不能實作重新映射到新的server,是以對于之前映射到localhost:11211上的資料,如果此server不恢複,将一直會set失敗,get同樣失敗,不能實作dead server的自動剔除(這是個問題)。

2)重新啟動memcached server,測試重新連接配接情況:

        在time:1372時刻啟動memcached server,在大緻time:1381或1382時刻重新set key4成功,時間大緻為20s,即MEMCACHED_BEHAVIOR_RETRY_TIMEOUT設定時間,也就是它的逾時重試時間,當到達這個時間後會重新連接配接dead的server。

3)重新關閉memcached server,測試永久失效

       在time:1386時刻,關閉localhost:30000,此時key4設定失敗,直到time:1471時刻,ketama_weighted:localhost|11211|1|160表示目前隻有一個memcached server有效,且localhost:30000被标記為DEAD,之後key4映射到localhost:11211上。經過時間大緻為:1471-1386=85s。

        經測試,之後即使重新啟動localhost:30000伺服器,永遠也不能自動連接配接進來了(這是個問題)。

2、源碼修改之後測試圖:

      1)關閉memcached server,測試重新映射情況

        在time:10時刻,關閉localhost:30000,則key4設定失敗,從失敗次數可以看出失敗5次後,localhost:30000被自動剔除,key4重新映射到localhost:11211上,這裡的5就是測試用例中設定的:

             memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_SERVER_FAILURE_LIMIT, 5) ;

        在time:561時刻啟動localhost:30000,在time:580時刻,localhost:30000自動連接配接,key4重新映射到其上,時間大概20s,即為設定的 memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_RETRY_TIMEOUT, 20) ;

        經測試,即使localhost:30000失效更長的時候,每個MEMCACHED_BEHAVIOR_RETRY_TIMEOUT時間都會重新嘗試連接配接它,如果連接配接成功則自動恢複其映射。

3)在重試次數(這裡為5次)内,如果memcached server恢複,則直接set/get資料成功

上圖:time:548時刻,關閉localhost:30000; 中圖:time:552時刻,重試4次時,重新開機啟動localhost:30000; 下圖:time:552時刻,成功連接配接到localhost:30000,set成功;

基于上面1.0.2版本提到的2個問題(或許有更多,或許稱為不足),經過簡單修改:

        第一個問題:backoff_handling函數中 server->server_failure_counter隻增不減,導緻一旦到達重試次數,它狀态将不能恢複。這裡将其重置為0:server->server_failure_counter= 0;

        是以這裡的重試次數與通常我們了解的重試次數是不同的,這裡是重試時間的次數,1.0.2版本中在”重試時間*重試次數“時間段内,任何映射到dead server上面的資料都失敗,經過”重試時間*重試次數“的時間後,server自動隔離,此時設定成功的時候為”重試時間“,是以多數情況下,這些資料都處于不可用狀态,導緻性能很低。

        對第二個問題修改的總的原則是:快隔離,慢恢複!快隔離:如果存在server失效,則迅速将其自動剔除;避免set及get等操作失敗持續時間較長(通常memcached應用于密集型操作set/get,是以快隔離還算合理)。慢恢複:失效的server啟動後,并不是立即進行連接配接,需要經過MEMCACHED_BEHAVIOR_RETRY_TIMEOUT時間,以避免剛剛重新映射的資料立即失效(從理論來講也不一定好,需要具體問題具體分析)。然後将”重試次數“了解為set、get等操作失敗的次數,我的修改方案并不優雅,隻是功能實作而已,這裡就不貼圖了,如果需要請自行查閱修改後的源碼。

       上述版本在源碼1.0.3版本中并不能獲得預期的效果,在上述代碼基礎上,需要設定如下參數:

            memcached_behavior_set(memc, MEMCACHED_BEHAVIOR_DEAD_TIMEOUT, 20) ;

        具體的測試類似于1.0.2版本。