PHP与Memcached服务器交互的分布式实现源码分析

前段 时间 ,因为一个 项目 的关系,研究了php通过调用 memcache memcached  PECL扩展库的接口存储到分布式缓存 服务器 的机制,在此做我根据他们各自的 源码 进行分析,希望能对这方面感兴趣的人有些帮助。
本篇文章我会针对php和memcache扩展库的交互根据源码展开分析。
PHP 调用memcache的接口通常会是如下过程:
  1. <?php  
  2. $mmc = new Memcache();  
  3. $mmc->addServer('node1', 11211);  
  4. $mmc->addServer('node2', 11211, MemcacheConfig::MEMCACHE_PERSISTENT, 2);  
  5. $mmc->set('key''value');  
  6. echo $mmc->get('key');  
  7. $mmc->delete('key');  
短短几行 代码 ,一个缓存key的生命周期就已经完整层现。从Memcache的初始化,到addServer添加两个服务器节点,接着set一个key到服务器上,然后get到这个key 输出 ,最后delete这个key。在这个生命周期里,Memcache在底层究竟做了哪些事情,保证了 数据 存储服务器的均匀分布,数据的完整性?
接下来,我会根据上述生命周期的顺序,循序渐进的分析(由于主题是分布式算法的分析,所以接下来不相干的代码我会略去,很多分析我会直接备注在源码上)。


1. Memcache的初始化
对应PHP的代码:
  1. $mmc = new Memcache();  
对应C的代码:// Memcache类对应的方法名已经实际在c中实现过程的 函数 名,在接下来的分析中会用到。忽略不会分析到的方法。
  1. static zend_function_entry php_memcache_class_functions[] = {  
  2. PHP_FALIAS(addserver, memcache_add_server, NULL)  
  3. PHP_FALIAS(set, memcache_set, NULL)  
  4. PHP_FALIAS(get, memcache_get, NULL)  
  5. PHP_FALIAS(delete, memcache_delete, NULL)  
  6. ......  
  7. };  
  8. PHP_MINIT_FUNCTION(memcache)  
  9. {  
  10. // 初始化Memcache类实体,给类定在php空间中的调用名称以及类所拥有的方法  
  11. zend_class_entry memcache_class_entry;  
  12. INIT_CLASS_ENTRY(memcache_class_entry, "Memcache", php_memcache_class_functions);  
  13. memcache_class_entry_ptr = zend_register_internal_class(&memcache_class_entry TSRMLS_CC);  
  14. ......  
  15. }  

以上过程是在Module Initialization的环节已经做好,在new的过程中,并无其余处理。
2. 添加缓存服务器,使之成为分布式存储

对应PHP的代码:

  1. $mmc->addServer('node1', 11211);  
  2. $mmc->addServer('node2', 11211, MemcacheConfig::MEMCACHE_PERSISTENT, 2);  

由上面的php_memcache_class_functions结构可以看出,addServer方法对应的是memcache_add_server函数,因此对应C的代码:
  1. PHP_FUNCTION(memcache_add_server)  
  2. {  
  3. zval **connection, *mmc_object = getThis(), *failure_callback = NULL;  
  4. // 整个Memcache中最重要的一个结构mmc_pool_t  
  5. mmc_pool_t *pool;  
  6. // 当前新添服务器的结构变量  
  7. mmc_t *mmc;  
  8. ......  
  9. // 如果pool之前没有初始化过,则初始化  
  10. if (zend_hash_find(Z_OBJPROP_P(mmc_object), "connection"sizeof("connection"), (void **) &connection) == FAILURE) {  
  11. // 调用mmp_pool_new完成初始化  
  12. pool = mmc_pool_new(TSRMLS_C);  
  13. ......  
  14. }  
  15. else {  
  16. ......  
  17. }  
  18. //将新增服务器添加到pool中  
  19. mmc_pool_add(pool, mmc, weight);  
  20. RETURN_TRUE;  
  21. }  

来看下mmc_pool_t结构的定义:

  1. typedef struct mmc_pool {  
  2. mmc_t **servers; // 所有服务器的状态  
  3. int num_servers; // 服务器数量  
  4. mmc_t **requests; // 根据get的array key请求顺序返回的服务器数组状态  
  5. int compress_threshold; // 待存储的数据压缩的下限值  
  6. double min_compress_savings; // 待存储的数据最小的压缩百分比  
  7. zend_bool in_free; // 标记该pool是否被释放  
  8. mmc_hash_t *hash; // hash策略容器  
  9. void *hash_state; // hash函数  
  10. } mmc_pool_t;  
然后我们看下mmc_hash_t的结构,再接下去的分析中会用到:// 结构定义中包含了四种抽象函数,作为基本结构,用于定义子结构
  1. typedef struct mmc_hash {  
  2. mmc_hash_create_state create_state; // 创建hash策略状态,主要是接纳了hash函数算法  
  3. mmc_hash_free_state free_state; // 释放hash策略状态  
  4. mmc_hash_find_server find_server; // 根据key和分布式算法定位到某台服务器  
  5. mmc_hash_add_server add_server; // 根据hash策略、算法以及权重值添加服务器资源  
  6. } mmc_hash_t;  

接着我们追踪memcache_add_server函数中的mmc_pool_new函数调用方法:

  1. typedef struct mmc_hash {  
  2. mmc_hash_create_state create_state; // 创建hash策略状态,主要是接纳了hash函数算法  
  3. mmc_hash_free_state free_state; // 释放hash策略状态  
  4. mmc_hash_find_server find_server; // 根据key和分布式算法定位到某台服务器  
  5. mmc_hash_add_server add_server; // 根据hash策略、算法以及权重值添加服务器资源  
  6. } mmc_hash_t;  

现在初始化hash算法已经逐渐显露,继续追踪mmc_pool_init_hash函数:

  1. static void mmc_pool_init_hash(mmc_pool_t *pool TSRMLS_DC) /* {{{ */  
  2. {  
  3. mmc_hash_function hash;// 初始化hash函数  
  4. // 根据php.ini中的memcache.hash_strategy配置选择hash存储策略,默认为标准hash存储策略  
  5. switch (MEMCACHE_G(hash_strategy)) {  
  6. case MMC_CONSISTENT_HASH:  
  7. pool->hash = &mmc_consistent_hash;// 采用持久化hash存储策略  
  8. break;  
  9. default:  
  10. pool->hash = &mmc_standard_hash;// 采用标准hash存储策略  
  11. }  

// 根据php.ini中的memcache.hash_function配置选择hash函数,默认为crc32算法
  1. switch (MEMCACHE_G(hash_function)) {  
  2. case MMC_HASH_FNV1A:  
  3. hash = &mmc_hash_fnv1a; // 采用fnv1a算法  
  4. break;  
  5. default:  
  6. hash = &mmc_hash_crc32; // 采用crc32算法  
  7. }  
  8. // hash策略中根据选择的hash函数创建对应的状态  
  9. pool->hash_state = pool->hash->create_state(hash);  
  10. }  

根据上面的两个switch可以知道,在create_state的时候,是有两种策略选择的可能性,接着传入的hash参数也存在两种可能性,这里我先分析标准hash存储策略,以及对应的两种hash算法,然后再分析持久化hash策略。
先看下mmc_consistent_hash结构:// 根据mmc_hash_t的定义包含了四种具体函数实现
  1. mmc_hash_t mmc_standard_hash = {  
  2. mmc_standard_create_state,  
  3. mmc_standard_free_state,  
  4. mmc_standard_find_server,  
  5. mmc_standard_add_server  
  6. };  

由上可知,pool->hash->create_state的函数调用实际是对mmc_standard_create_state的函数调用,继续看mmc_standard_create_state函数代码的实现:

  1. // hash策略状态  
  2. typedef struct mmc_standard_state {  
  3. int num_servers; // 服务器数量  
  4. mmc_t **buckets; // 哈希桶,和权重值相关  
  5. int num_buckets; // 哈系桶的数量  
  6. mmc_hash_function hash; // hash算法  
  7. } mmc_standard_state_t;  
  8.   
  9. void *mmc_standard_create_state(mmc_hash_function hash) /* {{{ */  
  10. {  
  11. // 初始化状态  
  12. mmc_standard_state_t *state = emalloc(sizeof(mmc_standard_state_t));  
  13. memset(state, 0, sizeof(mmc_standard_state_t));  
  14. // 选择的hash函数赋给hash属性  
  15. state->hash = hash;  
  16. return state;  
  17. }  

crc的算法实现:

  1. static unsigned int mmc_hash_crc32(const char *key, int key_len) /* CRC32 hash {{{ */  
  2. {  
  3. unsigned int crc = ~0;  
  4. int z;  
  5.   
  6. for (z=0; z<key_len; z++) {  
  7. CRC32(crc, key[z]);  
  8. }  
  9.   
  10. return ~crc;  
  11. }  

有关CRC32再深入的实现可以参考 Cyclic redundancy check

然后来看看fnv算法实现:

  1. /* 32 bit magic FNV-1a prime and init */  
  2. #define FNV_32_PRIME 0x01000193  
  3. #define FNV_32_INIT 0x811c9dc5  
  4. static unsigned int mmc_hash_fnv1a(const char *key, int key_len) /* FNV-1a hash {{{ */  
  5. {  
  6. unsigned int hval = FNV_32_INIT;  
  7. int z;  
  8.   
  9. for (z=0; z<key_len; z++) {  
  10. hval ^= (unsigned int)key[z];  
  11. hval *= FNV_32_PRIME;  
  12. }  
  13.   
  14. return hval;  
  15. }  

具体fnv算法的深入实现可以参考 Fowler–Noll–Vo hash function

最后我们看看mmc_consistent_hash结构:

  1. mmc_hash_t mmc_consistent_hash = {  
  2. mmc_consistent_create_state,  
  3. mmc_consistent_free_state,  
  4. mmc_consistent_find_server,  
  5. mmc_consistent_add_server  
  6. };  

一样是四个函数,看下对应的create_state中的mmc_consistent_create_state的实现:

  1. /* number of precomputed buckets, should be power of 2 */  
  2. #define MMC_CONSISTENT_BUCKETS 1024  
  3.   
  4. typedef struct mmc_consistent_point {  
  5. mmc_t *server; // 服务器状态  
  6. unsigned int point; // 对应的指针  
  7. } mmc_consistent_point_t;  
  8.   
  9. typedef struct mmc_consistent_state {  
  10. int num_servers; // 服务器数量  
  11. mmc_consistent_point_t *points; // 持久化服务器指针  
  12. int num_points; // 指针数量  
  13. mmc_t *buckets[MMC_CONSISTENT_BUCKETS]; // 哈希桶  
  14. int buckets_populated; //标记哈希桶是否计算过  
  15. mmc_hash_function hash; // hash函数  
  16. } mmc_consistent_state_t;  
  17.   
  18. void *mmc_consistent_create_state(mmc_hash_function hash) /* {{{ */  
  19. {  
  20. // 初始化state  
  21. mmc_consistent_state_t *state = emalloc(sizeof(mmc_consistent_state_t));  
  22. memset(state, 0, sizeof(mmc_consistent_state_t));  
  23. // 将hash函数赋值给hash属性  
  24. state->hash = hash;  
  25. return state;  
  26. }  

至此,memcache_add_server中mmc_pool_new函数流程结束,接着来看mmc_pool_add函数:

  1. void mmc_pool_add(mmc_pool_t *pool, mmc_t *mmc, unsigned int weight) /* {{{ */  
  2. {  
  3. /* add server and a preallocated request pointer */  
  4. if (pool->num_servers) {  
  5. pool->servers = erealloc(pool->servers, sizeof(mmc_t *) * (pool->num_servers + 1));  
  6. pool->requests = erealloc(pool->requests, sizeof(mmc_t *) * (pool->num_servers + 1));  
  7. }  
  8. else {  
  9. pool->servers = emalloc(sizeof(mmc_t *));  
  10. pool->requests = emalloc(sizeof(mmc_t *));  
  11. }  
  12.   
  13. pool->servers[pool->num_servers] = mmc;  
  14. pool->num_servers++;  
  15. // 根据pool状态,当前要添加的服务器状态和权重调用add_server函数  
  16. pool->hash->add_server(pool->hash_state, mmc, weight);  
  17. }  

由上面的说明可知add_server在标准hash模式下对应mmc_standard_add_server函数:

  1. void mmc_standard_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */  
  2. {  
  3. mmc_standard_state_t *state = s;  
  4. int i;  
  5.   
  6. // 哈希桶初始化或重新分配相应的权重数值对应的空间  
  7. if (state->num_buckets) {  
  8. state->buckets = erealloc(state->buckets, sizeof(mmc_t *) * (state->num_buckets + weight));  
  9. }  
  10. else {  
  11. state->buckets = emalloc(sizeof(mmc_t *) * (weight));  
  12. }  
  13. // 在某个区间内为哈希桶赋予服务器状态  
  14. for (i=0; i<weight; i++) {  
  15. buckets[state->num_buckets + i] = mmc;  
  16. }  
  17.   
  18. state->num_buckets += weight;  
  19. state->num_servers++;  
  20. }  

在持久化hash模式下,对应的是mmc_consistent_add_server函数:

  1. #define MMC_CONSISTENT_POINTS 160 /* points per server */  
  2.   
  3. void mmc_consistent_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */  
  4. {  
  5. mmc_consistent_state_t *state = s;  
  6. int i, key_len, points = weight * MMC_CONSISTENT_POINTS;  
  7.   
  8. /* buffer for "host:port-i\0" */  
  9. char *key = emalloc(strlen(mmc->host) + MAX_LENGTH_OF_LONG * 2 + 3);  
  10.   
  11. /* add weight * MMC_CONSISTENT_POINTS number of points for this server */  
  12. state->points = erealloc(state->points, sizeof(mmc_consistent_point_t) * (state->num_points + points));  
  13.   
  14. // 将区块内的server赋予当前服务器状态,point赋予hash函数处理后的值  
  15. for (i=0; i<points; i++) {  
  16. key_len = sprintf(key, "%s:%d-%d", mmc->host, mmc->port, i);  
  17. state->points[state->num_points + i].server = mmc;  
  18. state->points[state->num_points + i].point = state->hash(key, key_len);  
  19. MMC_DEBUG(("mmc_consistent_add_server: key %s, point %lu", key, state->points[state->num_points + i].point));  
  20. }  
  21.   
  22. state->num_points += points;  
  23. state->num_servers++;  
  24.   
  25. // 新增加服务器后需重新计算buckets顺序  
  26. state->buckets_populated = 0;  
  27.   
  28. efree(key);  
  29. }  



以上代码有持久化hash算法的赋值实现,具体深入的了解请看 Consistent hashing 和国内大侠charlee翻译的小日本的文章 memcached全面剖析–PDF总结篇
Consistent hashing 算法最大的特点是当你的缓存服务器数量变更的时候,它能够最大化的保留原有的缓存不变,而不需要重新分布原有缓存的服务器位置。
至此,整个memcache_add_server流程结束。
3. 向缓存服务器保存数据

对应PHP的代码:

  1. $mmc->set('key''value');  

由上面的分析可知,set方法对应的是memcache_set函数:

  1. /* {{{ proto bool memcache_set( object memcache, string key, mixed var [, int flag [, int expire ] ] ) 
  2. Sets the value of an item. Item may exist or not */  
  3. PHP_FUNCTION(memcache_set)  
  4. {  
  5. // Memcache对象中的add,set和replace皆会走该函数  
  6. php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, "set", sizeof("set") - 1);  
  7. }  

看php_mmc_store函数:

  1. static void php_mmc_store(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) /* {{{ */  
  2. {  
  3. mmc_pool_t *pool;  
  4. ......  
  5. // 获得pool  
  6. if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {  
  7. RETURN_FALSE;  
  8. }  
  9. // 对不同的存储的值类型进行不同的处理  
  10. switch (Z_TYPE_P(value)) {  
  11. // 字符串类型  
  12. case IS_STRING:  
  13. result = mmc_pool_store(  
  14. pool, command, command_len, key_tmp, key_tmp_len, flags, expire,   
  15. Z_STRVAL_P(value), Z_STRLEN_P(value) TSRMLS_CC);  
  16. break;  
  17. // 长整型,浮点型,布尔型  
  18. case IS_LONG:  
  19. case IS_DOUBLE:  
  20. case IS_BOOL: {  
  21. ......  
  22. result = mmc_pool_store(  
  23. pool, command, command_len, key_tmp, key_tmp_len, flags, expire,   
  24. Z_STRVAL(value_copy), Z_STRLEN(value_copy) TSRMLS_CC);  
  25.   
  26. zval_dtor(&value_copy);  
  27. break;  
  28. }  
  29. // 默认为数组类型  
  30. default: {  
  31. ......  
  32. result = mmc_pool_store(  
  33. pool, command, command_len, key_tmp, key_tmp_len, flags, expire,   
  34. buf.c, buf.len TSRMLS_CC);  
  35. }  
  36. }  
  37. ......  
  38. }  

由上代码可以看出,存储数据主要是交由mmc_pool_store处理:

  1. int mmc_pool_store(mmc_pool_t *pool, const char *command, int command_len, const char *key, int key_len, int flags, int expire, const char *value, int value_len TSRMLS_DC) /* {{{ */  
  2. {  
  3. /* 该省略过程处理数据压缩,处理待发送的请求数据 */  
  4. ......  
  5.   
  6. // 通过key确定待保存的服务器  
  7. while (result < 0 && (mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC)) != NULL) {  
  8. // 向缓存服务器发送请求,保存数据  
  9. if ((result = mmc_server_store(mmc, request, request_len TSRMLS_CC)) < 0) {  
  10. mmc_server_failure(mmc TSRMLS_CC);  
  11. }  
  12. }  
  13.   
  14. if (key_copy != NULL) {  
  15. efree(key_copy);  
  16. }  
  17. if (data != NULL) {  
  18. efree(data);  
  19. }  
  20. efree(request);  
  21. return result;  
  22. }  

接着我们看下mmc_pool_find是处理的

  1. #define mmc_pool_find(pool, key, key_len) \  
  2. pool->hash->find_server(pool->hash_state, key, key_len)  

原来是再次多态调用了find_server函数,由之前的分析可以得知find_server在标准hash模式中的函数为mmc_standard_find_server,在持久化hash模式中的函数为mmc_consistent_find_server,一样先看

  1. mmc_standard_find_servermmc_t *mmc_standard_find_server(void *s, const char *key, int key_len TSRMLS_DC) /* {{{ */  
  2. {  
  3. mmc_standard_state_t *state = s;  
  4. mmc_t *mmc;  
  5.   
  6. if (state->num_servers > 1) {  
  7. // 用设定的hash函数算法,找到对应的服务器  
  8. unsigned int hash = mmc_hash(state, key, key_len), i;  
  9. mmc = state->buckets[hash % state->num_buckets];  
  10.   
  11. // 如果获取到的服务器状态有问题,则重新hash遍历寻找到可用的缓存服务器为止   
  12. for (i=0; !mmc_open(mmc, 0, NULL, NULL TSRMLS_CC) && MEMCACHE_G(allow_failover) && i<MEMCACHE_G(max_failover_attempts); i++) {  
  13. char *next_key = emalloc(key_len + MAX_LENGTH_OF_LONG + 1);  
  14. int next_len = sprintf(next_key, "%d%s", i+1, key);  
  15. MMC_DEBUG(("mmc_standard_find_server: failed to connect to server '%s:%d' status %d, trying next", mmc->host, mmc->port, mmc->status));  
  16.   
  17. hash += mmc_hash(state, next_key, next_len);  
  18. mmc = state->buckets[hash % state->num_buckets];  
  19.   
  20. efree(next_key);  
  21. }  
  22. }  
  23. else {  
  24. mmc = state->buckets[0];  
  25. mmc_open(mmc, 0, NULL, NULL TSRMLS_CC);  
  26. }  
  27.   
  28. return mmc->status != MMC_STATUS_FAILED ? mmc : NULL;  
  29. }  


再看

  1. mmc_consistent_find_servermmc_t *mmc_consistent_find_server(void *s, const char *key, int key_len TSRMLS_DC) /* {{{ */  
  2. {  
  3. mmc_consistent_state_t *state = s;  
  4. mmc_t *mmc;  
  5.   
  6. if (state->num_servers > 1) {  
  7. unsigned int i, hash = state->hash(key, key_len);  
  8. // 如果哈希桶没有进行过排序,则进行圆环排序操作  
  9. if (!state->buckets_populated) {  
  10. mmc_consistent_populate_buckets(state);  
  11. }  
  12. mmc = state->buckets[hash % MMC_CONSISTENT_BUCKETS];  
  13.   
  14. // 如果获取到的服务器状态有问题,则重新hash遍历寻找到可用的缓存服务器为止   
  15. for (i=0; !mmc_open(mmc, 0, NULL, NULL TSRMLS_CC) && MEMCACHE_G(allow_failover) && i<MEMCACHE_G(max_failover_attempts); i++) {  
  16. char *next_key = emalloc(key_len + MAX_LENGTH_OF_LONG + 1);  
  17. int next_len = sprintf(next_key, "%s-%d", key, i);  
  18. MMC_DEBUG(("mmc_consistent_find_server: failed to connect to server '%s:%d' status %d, trying next", mmc->host, mmc->port, mmc->status));  
  19.   
  20. hash = state->hash(next_key, next_len);  
  21. mmc = state->buckets[hash % MMC_CONSISTENT_BUCKETS];  
  22.   
  23. efree(next_key);  
  24. }  
  25. }  
  26. else {  
  27. mmc = state->points[0].server;  
  28. mmc_open(mmc, 0, NULL, NULL TSRMLS_CC);  
  29. }  
  30.   
  31. return mmc->status != MMC_STATUS_FAILED ? mmc : NULL;  
  32. }  
  33. // 持久化哈希算法的核心部分  
  34. static void mmc_consistent_populate_buckets(mmc_consistent_state_t *state) /* {{{ */  
  35. {  
  36. unsigned int z, step = 0xffffffff / MMC_CONSISTENT_BUCKETS;  
  37.   
  38. qsort((void *)state->points, state->num_points, sizeof(mmc_consistent_point_t), mmc_consistent_compare);  
  39. for (z=0; z<MMC_CONSISTENT_BUCKETS; z++) {  
  40. state->buckets[z] = mmc_consistent_find(state, step * z);  
  41. }  
  42.   
  43. state->buckets_populated = 1;  
  44. }  
  45. static int mmc_consistent_compare(const void *a, const void *b) /* {{{ */  
  46. {  
  47. if (((mmc_consistent_point_t *)a)->point < ((mmc_consistent_point_t *)b)->point) {  
  48. return -1;  
  49. }  
  50. if (((mmc_consistent_point_t *)a)->point > ((mmc_consistent_point_t *)b)->point) {  
  51. return 1;  
  52. }  
  53. return 0;  
  54. }  
  55. static mmc_t *mmc_consistent_find(mmc_consistent_state_t *state, unsigned int point) /* {{{ */  
  56. {  
  57. int lo = 0, hi = state->num_points - 1, mid;  
  58.   
  59. while (1) {  
  60. /* point is outside interval or lo >= hi, wrap-around */  
  61. if (point <= state->points[lo].point || point > state->points[hi].point) {  
  62. return state->points[lo].server;  
  63. }  
  64.   
  65. /* test middle point */  
  66. mid = lo + (hi - lo) / 2;  
  67. MMC_DEBUG(("mmc_consistent_find: lo %d, hi %d, mid %d, point %u, midpoint %u", lo, hi, mid, point, state->points[mid].point));  
  68.   
  69. /* perfect match */  
  70. if (point <= state->points[mid].point && point > (mid ? state->points[mid-1].point : 0)) {  
  71. return state->points[mid].server;  
  72. }  
  73.   
  74. /* too low, go up */  
  75. if (state->points[mid].point < point) {  
  76. lo = mid + 1;  
  77. }  
  78. else {  
  79. hi = mid - 1;  
  80. }  
  81. }  
  82. }  

至此,memcache_set过程结束。


4. 向缓存服务器获得已保存的数据

对应PHP的代码:

  1. echo $mmc->get('key');  

由上面的分析可知,get方法对应的是memcache_get函数:

  1. PHP_FUNCTION(memcache_get)  
  2. {  
  3. ......  
  4. // 获得pool  
  5. if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {  
  6. RETURN_FALSE;  
  7. }  
  8. // 当key不为数组的情况下处理  
  9. if (Z_TYPE_P(zkey) != IS_ARRAY) {  
  10. // 检查key的合法性  
  11. if (mmc_prepare_key(zkey, key, &key_len TSRMLS_CC) == MMC_OK) {  
  12. // 获取key获取value  
  13. if (mmc_exec_retrieval_cmd(pool, key, key_len, &return_value, flags TSRMLS_CC) < 0) {  
  14. zval_dtor(return_value);  
  15. RETVAL_FALSE;  
  16. }  
  17. }  
  18. else {  
  19. RETVAL_FALSE;  
  20. }  
  21. // 为数组的情况下处理  
  22. else if (zend_hash_num_elements(Z_ARRVAL_P(zkey))){  
  23. //根据数据key获取数组值  
  24. if (mmc_exec_retrieval_cmd_multi(pool, zkey, &return_value, flags TSRMLS_CC) < 0) {  
  25. zval_dtor(return_value);  
  26. RETVAL_FALSE;  
  27. }  
  28. else {  
  29. RETVAL_FALSE;  
  30. }  
  31. }  

接着看mmc_exec_retrieval_cmd和mmc_exec_retrieval_cmd_multi函数:

  1. int mmc_exec_retrieval_cmd(mmc_pool_t *pool, const char *key, int key_len, zval **return_value, zval *return_flags TSRMLS_DC) /* {{{ */  
  2. {  
  3. mmc_t *mmc;  
  4. char *command, *value;  
  5. int result = -1, command_len, response_len, value_len, flags = 0;  
  6.   
  7. MMC_DEBUG(("mmc_exec_retrieval_cmd: key '%s'", key));  
  8.   
  9. command_len = spprintf(&command, 0, "get %s", key);  
  10. // 遍历寻找到key对应的value值  
  11. while (result < 0 && (mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC)) != NULL) {  
  12. ......  
  13. }  
  14.   
  15. if (return_flags != NULL) {  
  16. zval_dtor(return_flags);  
  17. ZVAL_LONG(return_flags, flags);  
  18. }  
  19.   
  20. efree(command);  
  21. return result;  
  22. }  
  23. static int mmc_exec_retrieval_cmd_multi(mmc_pool_t *pool, zval *keys, zval **return_value, zval *return_flags TSRMLS_DC) /* {{{ */  
  24. {  
  25. ......  
  26. do {  
  27. result_status = num_requests = 0;  
  28. zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);  
  29.   
  30. // 遍历key得到所有key对应的服务器资源存入pool->requests中  
  31. while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&zkey, &pos) == SUCCESS) {  
  32. if (mmc_prepare_key(*zkey, key, &key_len TSRMLS_CC) == MMC_OK) {  
  33. /* schedule key if first round or if missing from result */  
  34. if ((!i || !zend_hash_exists(Z_ARRVAL_PP(return_value), key, key_len)) &&  
  35. // 根据key寻找到服务器  
  36. (mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC)) != NULL) {  
  37. if (!(mmc->outbuf.len)) {  
  38. smart_str_appendl(&(mmc->outbuf), "get"sizeof("get")-1);  
  39. pool->requests[num_requests++] = mmc;  
  40. }  
  41.   
  42. smart_str_appendl(&(mmc->outbuf), " ", 1);  
  43. smart_str_appendl(&(mmc->outbuf), key, key_len);  
  44. MMC_DEBUG(("mmc_exec_retrieval_cmd_multi: scheduled key '%s' for '%s:%d' request length '%d'", key, mmc->host, mmc->port, mmc->outbuf.len));  
  45. }  
  46. }  
  47.   
  48. zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);  
  49. }  
  50.   
  51. ......  
  52.   
  53. while (result_status < 0 && MEMCACHE_G(allow_failover) && i++ < MEMCACHE_G(max_failover_attempts));  
  54.   
  55. ......  
  56.   
  57. return result_status;  
  58. }  

由上可见分布式hash的核心函数皆为mmc_pool_find,首先找到key对应的服务器资源,然后根据服务器资源请求数据。
至此,memcache_get的过程结束。
5.向缓存服务器删除已保存的数据
对应的php代码:
  1. $mmc->delete('key');  

由之前的分析可知,delete对应的为

  1. memcache_delete:/* {{{ proto bool memcache_delete( object memcache, string key [, int expire ]) 
  2. Deletes existing item */  
  3. PHP_FUNCTION(memcache_delete)  
  4. {  
  5. mmc_t *mmc;  
  6. mmc_pool_t *pool;  
  7. int result = -1, key_len;  
  8. zval *mmc_object = getThis();  
  9. char *key;  
  10. long time = 0;  
  11. char key_tmp[MMC_KEY_MAX_SIZE];  
  12. unsigned int key_tmp_len;  
  13.   
  14. if (mmc_object == NULL) {  
  15. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os|l", &mmc_object, memcache_class_entry_ptr, &key, &key_len, &time) == FAILURE) {  
  16. return;  
  17. }  
  18. }  
  19. else {  
  20. if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, &time) == FAILURE) {  
  21. return;  
  22. }  
  23. }  
  24.   
  25. if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {  
  26. RETURN_FALSE;  
  27. }  
  28.   
  29. if (mmc_prepare_key_ex(key, key_len, key_tmp, &key_tmp_len TSRMLS_CC) != MMC_OK) {  
  30. RETURN_FALSE;  
  31. }  
  32.   
  33. // 先获得服务器资源  
  34. while (result < 0 && (mmc = mmc_pool_find(pool, key_tmp, key_tmp_len TSRMLS_CC)) != NULL) {  
  35. // 根据资源向缓存服务器发送请求删除存储的数据   
  36. if ((result = mmc_delete(mmc, key_tmp, key_tmp_len, time TSRMLS_CC)) < 0) {  
  37. mmc_server_failure(mmc TSRMLS_CC);  
  38. }  
  39. }  
  40.   
  41. if (result > 0) {  
  42. RETURN_TRUE;  
  43. }  
  44. RETURN_FALSE;  
  45. }  
  46. /* }}} */  

至此,memcache_delete过程结束。

来自:http://blog.liubijian.com/php_memcache_code_analysis.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值