libmemcached1.0.2 C/C++ API使用实例、测试及修改

来源:互联网 发布:苹果windows系统触摸板 编辑:程序博客网 时间:2024/06/04 18:22

        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使用及测试实例如下:

    #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) ;


2)重新启动memcached server,测试重新连接情况:

        在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版本。