phpとmemcachedサーバのインタラクティブな分散実装ソース分析[memcache版]
この間、あるプロジェクトの関係でphpがmemcacheとmemcached PECL拡張ライブラリを呼び出すインタフェースを通じて分散キャッシュサーバに格納されるメカニズムを研究しました.ここでは、それぞれのソースコードに基づいて分析し、興味のある人に役立つことを望んでいます.
この文章ではphpとmemcache拡張ライブラリのインタラクションについてソースコードに基づいて分析を展開します.
PHPがmemcacheを呼び出すインタフェースは、通常、次の手順に従います.
わずか数行のコードで、キャッシュキーのライフサイクルが完全に表示されます.Memcacheの初期化からaddServerに2つのサーバノードを追加し、1つのkeyをサーバにセットし、getをこのkeyに出力し、最後にdeleteというkeyにします.このライフサイクルの中で、Memcacheは底層でいったいどんなことをして、データストレージサーバーの均一な分布、データの完全性を保証しましたか?
次に、上記のライフサイクルの順序に従って、逐次漸進的に分析します(テーマは分布式アルゴリズムの分析なので、次に関係のないコードは省略します.多くの分析はソースコードに直接注釈します).
1.Memcacheの初期化
PHP対応コード:
Cに対応するコード:
以上の過程はModule Initializationの一環ですでに完成しており,newの過程では残りの処理はない.
2.分散型ストレージとなるようにキャッシュサーバを追加
PHP対応コード:
上のphp_からmemcache_class_functions構造からaddServerメソッドはmemcache_に対応していることがわかる.add_server関数ですので、Cのコードに対応します.
次にmemcacheを追跡しますadd_server関数のmmc_pool_new関数呼び出し方法:
初期化hashアルゴリズムが徐々に現れ、mmc_を追跡し続けている.pool_init_hash関数:
上の2つのswitchから分かるようにcreate_stateの場合、2つのポリシーが選択される可能性があり、次に入力されるhashパラメータにも2つの可能性があります.ここでは、標準hashストレージポリシーと対応する2つのhashアルゴリズムを分析してから、永続化hashポリシーを分析します.
まずmmc_を見てみましょうconsistent_hash構造:
上から分かるようにpool->hash->create_stateの関数呼び出しは実際にはmmc_standard_create_stateの関数呼び出し、mmc_を見続けますstandard_create_state関数コードの実装:
具体的なfnvアルゴリズムの深い実現は参考にすることができる
Fowler–Noll–Vo hash function
最後にmmc_を見てみましょうconsistent_hash構造:
同じ4つの関数で、対応するcreateを見てみましょう.stateのmmc_consistent_create_stateの実装:
これでmemcache_add_server中mmc_pool_new関数の流れが終わり、次にmmc_を見てみましょう.pool_add関数:
永続化hashモードではmmc_に対応するconsistent_add_server関数:
以上のコードは永続化hashアルゴリズムの賦値実現があり、具体的にはConsistent hashingと国内の大侠charlee翻訳の小日本の文章memcached全面剖析–PDF総括編を参照してください.
Consistent hashingアルゴリズムの最大の特徴は、キャッシュ・サーバの数が変更された場合、既存のキャッシュを再分散する必要がなく、既存のキャッシュをそのまま保持できることです.
これでmemcache全体がadd_serverプロセスが終了しました.
3.キャッシュサーバへのデータ保存
PHP対応コード:
php_を見てmmc_store関数:
上のコードから分かるように、格納データは主にmmc_に渡されている.pool_store処理:
次にmmc_を見てみましょうpool_findは処理されています
再びマルチステート呼び出しfind_server関数、前の分析からfind_がわかりますserverの標準hashモードでの関数はmmc_standard_find_server、持続化hashモードでの関数はmmc_consistent_find_server、同じようにmmc_を先に見ますstandard_find_server
mmc_を見てconsistent_find_server
これでmemcache_setプロセスが終了します.
4.保存したデータをキャッシュサーバに取得する
PHP対応コード:
以上の解析から,getメソッドはmemcache_に対応することが分かった.get関数:
次にmmc_を見てexec_retrieval_cmdとmmc_exec_retrieval_cmd_Multi関数:
上から見える分布式hashのコア関数はすべてmmc_pool_findは、まずkeyに対応するサーバリソースを見つけ、サーバリソースに基づいてデータを要求します.
これでmemcache_getのプロセスは終了します.
5.保存したデータをキャッシュサーバに削除する
対応するphpコード:
以前の解析からdeleteはmemcache_に対応することが分かった.delete:
これでmemcache_deleteプロセスが終了しました.
当サイトの文章はすべて転載して、出典:http://blog.liubijian.com/php_memcache_code_analysis.html
... other posts by lbj
この文章ではphpとmemcache拡張ライブラリのインタラクションについてソースコードに基づいて分析を展開します.
PHPがmemcacheを呼び出すインタフェースは、通常、次の手順に従います.
- [cc lang="php"]
- $mmc = new Memcache();
- $mmc->addServer(‘node1′, 11211);
- $mmc->addServer(‘node2′, 11211, MemcacheConfig::MEMCACHE_PERSISTENT, 2);
- $mmc->set(‘key’, ‘value’);
- echo $mmc->get(‘key’);
- $mmc->delete(‘key’);
- [/cc]
わずか数行のコードで、キャッシュキーのライフサイクルが完全に表示されます.Memcacheの初期化からaddServerに2つのサーバノードを追加し、1つのkeyをサーバにセットし、getをこのkeyに出力し、最後にdeleteというkeyにします.このライフサイクルの中で、Memcacheは底層でいったいどんなことをして、データストレージサーバーの均一な分布、データの完全性を保証しましたか?
次に、上記のライフサイクルの順序に従って、逐次漸進的に分析します(テーマは分布式アルゴリズムの分析なので、次に関係のないコードは省略します.多くの分析はソースコードに直接注釈します).
1.Memcacheの初期化
PHP対応コード:
- [cc lang="php"]
- $mmc = new Memcache();
- [/cc]
Cに対応するコード:
- [cc lang="c"]
- // Memcache c , 。 。
- static zend_function_entry php_memcache_class_functions[] = {
- PHP_FALIAS(addserver, memcache_add_server, NULL)
- PHP_FALIAS(set, memcache_set, NULL)
- PHP_FALIAS(get, memcache_get, NULL)
- PHP_FALIAS(delete, memcache_delete, NULL)
- ……
- };
- PHP_MINIT_FUNCTION(memcache)
- {
- // Memcache , php
- zend_class_entry memcache_class_entry;
- INIT_CLASS_ENTRY(memcache_class_entry, "Memcache", php_memcache_class_functions);
- memcache_class_entry_ptr = zend_register_internal_class(&memcache_class_entry TSRMLS_CC);
- ……
- }
- [/cc]
以上の過程はModule Initializationの一環ですでに完成しており,newの過程では残りの処理はない.
2.分散型ストレージとなるようにキャッシュサーバを追加
PHP対応コード:
- [cc lang="php"]
- $mmc->addServer(‘node1′, 11211);
- $mmc->addServer(‘node2′, 11211, MemcacheConfig::MEMCACHE_PERSISTENT, 2);
- [/cc]
上のphp_からmemcache_class_functions構造からaddServerメソッドはmemcache_に対応していることがわかる.add_server関数ですので、Cのコードに対応します.
- [cc lang="c"]
- PHP_FUNCTION(memcache_add_server)
- {
- zval **connection, *mmc_object = getThis(), *failure_callback = NULL;
- // Memcache mmc_pool_t
- mmc_pool_t *pool;
- //
- mmc_t *mmc;
- ……
- // pool ,
- if (zend_hash_find(Z_OBJPROP_P(mmc_object), "connection", sizeof("connection"), (void **) &connection) == FAILURE) {
- // mmp_pool_new
- pool = mmc_pool_new(TSRMLS_C);
- ……
- }
- else {
- ……
- }
- // pool
- mmc_pool_add(pool, mmc, weight);
- RETURN_TRUE;
- }
- [/cc]
- mmc_pool_t :
- [cc lang="c"]
- typedef struct mmc_pool {
- mmc_t **servers; //
- int num_servers; //
- mmc_t **requests; // get array key
- int compress_threshold; //
- double min_compress_savings; //
- zend_bool in_free; // pool
- mmc_hash_t *hash; // hash
- void *hash_state; // hash
- } mmc_pool_t;
- [/cc]
- mmc_hash_t , :
- [cc lang="c"]
- // , ,
- typedef struct mmc_hash {
- mmc_hash_create_state create_state; // hash , hash
- mmc_hash_free_state free_state; // hash
- mmc_hash_find_server find_server; // key
- mmc_hash_add_server add_server; // hash 、
- } mmc_hash_t;
- [/cc]
次にmemcacheを追跡しますadd_server関数のmmc_pool_new関数呼び出し方法:
- [cc lang="c"]
- mmc_pool_t *mmc_pool_new(TSRMLS_D) /* {{{ */
- {
- //
- mmc_pool_t *pool = emalloc(sizeof(mmc_pool_t));
- pool->num_servers = 0;
- pool->compress_threshold = 0;
- pool->in_free = 0;
- pool->min_compress_savings = MMC_DEFAULT_SAVINGS;
- //
- mmc_pool_init_hash(pool TSRMLS_CC);
- return pool;
- }
- [/cc]
初期化hashアルゴリズムが徐々に現れ、mmc_を追跡し続けている.pool_init_hash関数:
- [cc lang="c"]
- static void mmc_pool_init_hash(mmc_pool_t *pool TSRMLS_DC) /* {{{ */
- {
- mmc_hash_function hash; // hash
- // php.ini memcache.hash_strategy hash , hash
- switch (MEMCACHE_G(hash_strategy)) {
- case MMC_CONSISTENT_HASH:
- pool->hash = &mmc_consistent_hash; // hash
- break;
- default:
- pool->hash = &mmc_standard_hash; // hash
- }
- // php.ini memcache.hash_function hash , crc32
- switch (MEMCACHE_G(hash_function)) {
- case MMC_HASH_FNV1A:
- hash = &mmc_hash_fnv1a; // fnv1a
- break;
- default:
- hash = &mmc_hash_crc32; // crc32
- }
- // hash hash
- pool->hash_state = pool->hash->create_state(hash);
- }
- [/cc]
上の2つのswitchから分かるようにcreate_stateの場合、2つのポリシーが選択される可能性があり、次に入力されるhashパラメータにも2つの可能性があります.ここでは、標準hashストレージポリシーと対応する2つのhashアルゴリズムを分析してから、永続化hashポリシーを分析します.
まずmmc_を見てみましょうconsistent_hash構造:
- [cc lang="c"]
- // mmc_hash_t
- mmc_hash_t mmc_standard_hash = {
- mmc_standard_create_state,
- mmc_standard_free_state,
- mmc_standard_find_server,
- mmc_standard_add_server
- };
- [/cc]
上から分かるようにpool->hash->create_stateの関数呼び出しは実際にはmmc_standard_create_stateの関数呼び出し、mmc_を見続けますstandard_create_state関数コードの実装:
- [cc lang="c"]
- // hash
- typedef struct mmc_standard_state {
- int num_servers; //
- mmc_t **buckets; // ,
- int num_buckets; //
- mmc_hash_function hash; // hash
- } mmc_standard_state_t;
- void *mmc_standard_create_state(mmc_hash_function hash) /* {{{ */
- {
- //
- mmc_standard_state_t *state = emalloc(sizeof(mmc_standard_state_t));
- memset(state, 0, sizeof(mmc_standard_state_t));
- // hash hash
- state->hash = hash;
- return state;
- }
- [/cc]
- crc :
- [cc lang="c"]
- static unsigned int mmc_hash_crc32(const char *key, int key_len) /* CRC32 hash {{{ */
- {
- unsigned int crc = ~0;
- int i;
- for (i=0; i CRC32(crc, key[i]);
- }
- return ~crc;
- }
- [/cc]
- CRC32 Cyclic redundancy check
- fnv :
- [cc lang="c"]
- /* 32 bit magic FNV-1a prime and init */
- #define FNV_32_PRIME 0×01000193
- #define FNV_32_INIT 0x811c9dc5
- static unsigned int mmc_hash_fnv1a(const char *key, int key_len) /* FNV-1a hash {{{ */
- {
- unsigned int hval = FNV_32_INIT;
- int i;
- for (i=0; i hval ^= (unsigned int)key[i];
- hval *= FNV_32_PRIME;
- }
- return hval;
- }
- [/cc]
具体的なfnvアルゴリズムの深い実現は参考にすることができる
Fowler–Noll–Vo hash function
最後にmmc_を見てみましょうconsistent_hash構造:
- [cc lang="c"]
- mmc_hash_t mmc_consistent_hash = {
- mmc_consistent_create_state,
- mmc_consistent_free_state,
- mmc_consistent_find_server,
- mmc_consistent_add_server
- };
- [/cc]
同じ4つの関数で、対応するcreateを見てみましょう.stateのmmc_consistent_create_stateの実装:
- [cc lang="c"]
- /* number of precomputed buckets, should be power of 2 */
- #define MMC_CONSISTENT_BUCKETS 1024
- typedef struct mmc_consistent_point {
- mmc_t *server; //
- unsigned int point; //
- } mmc_consistent_point_t;
- typedef struct mmc_consistent_state {
- int num_servers; //
- mmc_consistent_point_t *points; //
- int num_points; //
- mmc_t *buckets[MMC_CONSISTENT_BUCKETS]; //
- int buckets_populated; //
- mmc_hash_function hash; // hash
- } mmc_consistent_state_t;
- void *mmc_consistent_create_state(mmc_hash_function hash) /* {{{ */
- {
- // state
- mmc_consistent_state_t *state = emalloc(sizeof(mmc_consistent_state_t));
- memset(state, 0, sizeof(mmc_consistent_state_t));
- // hash hash
- state->hash = hash;
- return state;
- }
- [/cc]
これでmemcache_add_server中mmc_pool_new関数の流れが終わり、次にmmc_を見てみましょう.pool_add関数:
- [cc lang="c"]
- void mmc_pool_add(mmc_pool_t *pool, mmc_t *mmc, unsigned int weight) /* {{{ */
- {
- /* add server and a preallocated request pointer */
- if (pool->num_servers) {
- pool->servers = erealloc(pool->servers, sizeof(mmc_t *) * (pool->num_servers + 1));
- pool->requests = erealloc(pool->requests, sizeof(mmc_t *) * (pool->num_servers + 1));
- }
- else {
- pool->servers = emalloc(sizeof(mmc_t *));
- pool->requests = emalloc(sizeof(mmc_t *));
- }
- pool->servers[pool->num_servers] = mmc;
- pool->num_servers++;
- // pool , add_server
- pool->hash->add_server(pool->hash_state, mmc, weight);
- }
- [/cc]
- add_server hash mmc_standard_add_server :
- [cc lang="c"]
- void mmc_standard_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */
- {
- mmc_standard_state_t *state = s;
- int i;
- //
- if (state->num_buckets) {
- state->buckets = erealloc(state->buckets, sizeof(mmc_t *) * (state->num_buckets + weight));
- }
- else {
- state->buckets = emalloc(sizeof(mmc_t *) * (weight));
- }
- //
- for (i=0; i buckets[state->num_buckets + i] = mmc;
- }
- state->num_buckets += weight;
- state->num_servers++;
- }
- [/cc]
永続化hashモードではmmc_に対応するconsistent_add_server関数:
- [cc lang="c"]
- #define MMC_CONSISTENT_POINTS 160 /* points per server */
- void mmc_consistent_add_server(void *s, mmc_t *mmc, unsigned int weight) /* {{{ */
- {
- mmc_consistent_state_t *state = s;
- int i, key_len, points = weight * MMC_CONSISTENT_POINTS;
- /* buffer for "host:port-i\0" */
- char *key = emalloc(strlen(mmc->host) + MAX_LENGTH_OF_LONG * 2 + 3);
- /* add weight * MMC_CONSISTENT_POINTS number of points for this server */
- state->points = erealloc(state->points, sizeof(mmc_consistent_point_t) * (state->num_points + points));
- // server ,point hash
- for (i=0; i key_len = sprintf(key, "%s:%d-%d", mmc->host, mmc->port, i);
- state->points[state->num_points + i].server = mmc;
- state->points[state->num_points + i].point = state->hash(key, key_len);
- MMC_DEBUG(("mmc_consistent_add_server: key %s, point %lu", key, state->points[state->num_points + i].point));
- }
- state->num_points += points;
- state->num_servers++;
- // buckets
- state->buckets_populated = 0;
- efree(key);
- }
- [/cc]
以上のコードは永続化hashアルゴリズムの賦値実現があり、具体的にはConsistent hashingと国内の大侠charlee翻訳の小日本の文章memcached全面剖析–PDF総括編を参照してください.
Consistent hashingアルゴリズムの最大の特徴は、キャッシュ・サーバの数が変更された場合、既存のキャッシュを再分散する必要がなく、既存のキャッシュをそのまま保持できることです.
これでmemcache全体がadd_serverプロセスが終了しました.
3.キャッシュサーバへのデータ保存
PHP対応コード:
- [cc lang="php"]
- $mmc->set(‘key’, ‘value’);
- [/cc]
- ,set memcache_set :
- [cc lang="c"]
- /* {{{ proto bool memcache_set( object memcache, string key, mixed var [, int flag [, int expire ] ] )
- Sets the value of an item. Item may exist or not */
- PHP_FUNCTION(memcache_set)
- {
- // Memcache add,set replace
- php_mmc_store(INTERNAL_FUNCTION_PARAM_PASSTHRU, "set", sizeof("set") – 1);
- }
- [/cc]
php_を見てmmc_store関数:
- [cc lang="c"]
- static void php_mmc_store(INTERNAL_FUNCTION_PARAMETERS, char *command, int command_len) /* {{{ */
- {
- mmc_pool_t *pool;
- ……
- // pool
- if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {
- RETURN_FALSE;
- }
- //
- switch (Z_TYPE_P(value)) {
- //
- case IS_STRING:
- result = mmc_pool_store(
- pool, command, command_len, key_tmp, key_tmp_len, flags, expire,
- Z_STRVAL_P(value), Z_STRLEN_P(value) TSRMLS_CC);
- break;
- // , ,
- case IS_LONG:
- case IS_DOUBLE:
- case IS_BOOL: {
- ……
- result = mmc_pool_store(
- pool, command, command_len, key_tmp, key_tmp_len, flags, expire,
- Z_STRVAL(value_copy), Z_STRLEN(value_copy) TSRMLS_CC);
- zval_dtor(&value_copy);
- break;
- }
- //
- default: {
- ……
- result = mmc_pool_store(
- pool, command, command_len, key_tmp, key_tmp_len, flags, expire,
- buf.c, buf.len TSRMLS_CC);
- }
- }
- ……
- }
- [/cc]
上のコードから分かるように、格納データは主にmmc_に渡されている.pool_store処理:
- [cc lang="c"]
- 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) /* {{{ */
- {
- /* , */
- ……
- // key
- while (result < 0 && (mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC)) != NULL) {
- // ,
- if ((result = mmc_server_store(mmc, request, request_len TSRMLS_CC)) < 0) {
- mmc_server_failure(mmc TSRMLS_CC);
- }
- }
- if (key_copy != NULL) {
- efree(key_copy);
- }
- if (data != NULL) {
- efree(data);
- }
- efree(request);
- return result;
- }
- [/cc]
次にmmc_を見てみましょうpool_findは処理されています
- [cc lang="c"]
- #define mmc_pool_find(pool, key, key_len) \
- pool->hash->find_server(pool->hash_state, key, key_len)
- [/cc]
再びマルチステート呼び出しfind_server関数、前の分析からfind_がわかりますserverの標準hashモードでの関数はmmc_standard_find_server、持続化hashモードでの関数はmmc_consistent_find_server、同じようにmmc_を先に見ますstandard_find_server
- [cc lang="c"]
- mmc_t *mmc_standard_find_server(void *s, const char *key, int key_len TSRMLS_DC) /* {{{ */
- {
- mmc_standard_state_t *state = s;
- mmc_t *mmc;
- if (state->num_servers > 1) {
- // hash ,
- unsigned int hash = mmc_hash(state, key, key_len), i;
- mmc = state->buckets[hash % state->num_buckets];
- // , hash
- for (i=0; !mmc_open(mmc, 0, NULL, NULL TSRMLS_CC) && MEMCACHE_G(allow_failover) && i char *next_key = emalloc(key_len + MAX_LENGTH_OF_LONG + 1);
- int next_len = sprintf(next_key, "%d%s", i+1, key);
- MMC_DEBUG(("mmc_standard_find_server: failed to connect to server '%s:%d' status %d, trying next", mmc->host, mmc->port, mmc->status));
- hash += mmc_hash(state, next_key, next_len);
- mmc = state->buckets[hash % state->num_buckets];
- efree(next_key);
- }
- }
- else {
- mmc = state->buckets[0];
- mmc_open(mmc, 0, NULL, NULL TSRMLS_CC);
- }
- return mmc->status != MMC_STATUS_FAILED ? mmc : NULL;
- }
- [/cc]
mmc_を見てconsistent_find_server
- [cc lang="c"]
- mmc_t *mmc_consistent_find_server(void *s, const char *key, int key_len TSRMLS_DC) /* {{{ */
- {
- mmc_consistent_state_t *state = s;
- mmc_t *mmc;
- if (state->num_servers > 1) {
- unsigned int i, hash = state->hash(key, key_len);
- // ,
- if (!state->buckets_populated) {
- mmc_consistent_populate_buckets(state);
- }
- mmc = state->buckets[hash % MMC_CONSISTENT_BUCKETS];
- // , hash
- for (i=0; !mmc_open(mmc, 0, NULL, NULL TSRMLS_CC) && MEMCACHE_G(allow_failover) && i char *next_key = emalloc(key_len + MAX_LENGTH_OF_LONG + 1);
- int next_len = sprintf(next_key, "%s-%d", key, i);
- MMC_DEBUG(("mmc_consistent_find_server: failed to connect to server '%s:%d' status %d, trying next", mmc->host, mmc->port, mmc->status));
- hash = state->hash(next_key, next_len);
- mmc = state->buckets[hash % MMC_CONSISTENT_BUCKETS];
- efree(next_key);
- }
- }
- else {
- mmc = state->points[0].server;
- mmc_open(mmc, 0, NULL, NULL TSRMLS_CC);
- }
- return mmc->status != MMC_STATUS_FAILED ? mmc : NULL;
- }
- //
- static void mmc_consistent_populate_buckets(mmc_consistent_state_t *state) /* {{{ */
- {
- unsigned int i, step = 0xffffffff / MMC_CONSISTENT_BUCKETS;
- qsort((void *)state->points, state->num_points, sizeof(mmc_consistent_point_t), mmc_consistent_compare);
- for (i=0; i state->buckets[i] = mmc_consistent_find(state, step * i);
- }
- state->buckets_populated = 1;
- }
- static int mmc_consistent_compare(const void *a, const void *b) /* {{{ */
- {
- if (((mmc_consistent_point_t *)a)->point < ((mmc_consistent_point_t *)b)->point) {
- return -1;
- }
- if (((mmc_consistent_point_t *)a)->point > ((mmc_consistent_point_t *)b)->point) {
- return 1;
- }
- return 0;
- }
- static mmc_t *mmc_consistent_find(mmc_consistent_state_t *state, unsigned int point) /* {{{ */
- {
- int lo = 0, hi = state->num_points – 1, mid;
- while (1) {
- /* point is outside interval or lo >= hi, wrap-around */
- if (point <= state->points[lo].point || point > state->points[hi].point) {
- return state->points[lo].server;
- }
- /* test middle point */
- mid = lo + (hi – lo) / 2;
- MMC_DEBUG(("mmc_consistent_find: lo %d, hi %d, mid %d, point %u, midpoint %u", lo, hi, mid, point, state->points[mid].point));
- /* perfect match */
- if (point <= state->points[mid].point && point > (mid ? state->points[mid-1].point : 0)) {
- return state->points[mid].server;
- }
- /* too low, go up */
- if (state->points[mid].point < point) {
- lo = mid + 1;
- }
- else {
- hi = mid - 1;
- }
- }
- }
- [/cc]
これでmemcache_setプロセスが終了します.
4.保存したデータをキャッシュサーバに取得する
PHP対応コード:
- [cc lang="php"]
- echo $mmc->get(‘key’);
- [/cc]
以上の解析から,getメソッドはmemcache_に対応することが分かった.get関数:
- [cc lang="php"]
- PHP_FUNCTION(memcache_get)
- {
- ……
- // pool
- if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {
- RETURN_FALSE;
- }
- // key
- if (Z_TYPE_P(zkey) != IS_ARRAY) {
- // key
- if (mmc_prepare_key(zkey, key, &key_len TSRMLS_CC) == MMC_OK) {
- // key value
- if (mmc_exec_retrieval_cmd(pool, key, key_len, &return_value, flags TSRMLS_CC) < 0) {
- zval_dtor(return_value);
- RETVAL_FALSE;
- }
- }
- else {
- RETVAL_FALSE;
- }
- //
- } else if (zend_hash_num_elements(Z_ARRVAL_P(zkey))){
- // key
- if (mmc_exec_retrieval_cmd_multi(pool, zkey, &return_value, flags TSRMLS_CC) < 0) {
- zval_dtor(return_value);
- RETVAL_FALSE;
- }
- } else {
- RETVAL_FALSE;
- }
- }
- [/cc]
次にmmc_を見てexec_retrieval_cmdとmmc_exec_retrieval_cmd_Multi関数:
- [cc lang="c"]
- int mmc_exec_retrieval_cmd(mmc_pool_t *pool, const char *key, int key_len, zval **return_value, zval *return_flags TSRMLS_DC) /* {{{ */
- {
- mmc_t *mmc;
- char *command, *value;
- int result = -1, command_len, response_len, value_len, flags = 0;
- MMC_DEBUG(("mmc_exec_retrieval_cmd: key '%s'", key));
- command_len = spprintf(&command, 0, "get %s", key);
- // key value
- while (result < 0 && (mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC)) != NULL) {
- ......
- }
- if (return_flags != NULL) {
- zval_dtor(return_flags);
- ZVAL_LONG(return_flags, flags);
- }
- efree(command);
- return result;
- }
- static int mmc_exec_retrieval_cmd_multi(mmc_pool_t *pool, zval *keys, zval **return_value, zval *return_flags TSRMLS_DC) /* {{{ */
- {
- ......
- do {
- result_status = num_requests = 0;
- zend_hash_internal_pointer_reset_ex(Z_ARRVAL_P(keys), &pos);
- // key key pool->requests
- while (zend_hash_get_current_data_ex(Z_ARRVAL_P(keys), (void **)&zkey, &pos) == SUCCESS) {
- if (mmc_prepare_key(*zkey, key, &key_len TSRMLS_CC) == MMC_OK) {
- /* schedule key if first round or if missing from result */
- if ((!i || !zend_hash_exists(Z_ARRVAL_PP(return_value), key, key_len)) &&
- // key
- (mmc = mmc_pool_find(pool, key, key_len TSRMLS_CC)) != NULL) {
- if (!(mmc->outbuf.len)) {
- smart_str_appendl(&(mmc->outbuf), "get", sizeof("get")-1);
- pool->requests[num_requests++] = mmc;
- }
- smart_str_appendl(&(mmc->outbuf), " ", 1);
- smart_str_appendl(&(mmc->outbuf), key, key_len);
- MMC_DEBUG(("mmc_exec_retrieval_cmd_multi: scheduled key ‘%s’ for ‘%s:%d’ request length ‘%d’", key, mmc->host, mmc->port, mmc->outbuf.len));
- }
- }
- zend_hash_move_forward_ex(Z_ARRVAL_P(keys), &pos);
- }
- ……
- } while (result_status < 0 && MEMCACHE_G(allow_failover) && i++ < MEMCACHE_G(max_failover_attempts));
- ......
- return result_status;
- }
- [/cc]
上から見える分布式hashのコア関数はすべてmmc_pool_findは、まずkeyに対応するサーバリソースを見つけ、サーバリソースに基づいてデータを要求します.
これでmemcache_getのプロセスは終了します.
5.保存したデータをキャッシュサーバに削除する
対応するphpコード:
- [cc lang="php"]
- $mmc->delete(‘key’);
- [/cc]
以前の解析からdeleteはmemcache_に対応することが分かった.delete:
- [cc lang="c"]
- /* {{{ proto bool memcache_delete( object memcache, string key [, int expire ])
- Deletes existing item */
- PHP_FUNCTION(memcache_delete)
- {
- mmc_t *mmc;
- mmc_pool_t *pool;
- int result = -1, key_len;
- zval *mmc_object = getThis();
- char *key;
- long time = 0;
- char key_tmp[MMC_KEY_MAX_SIZE];
- unsigned int key_tmp_len;
- if (mmc_object == NULL) {
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "Os|l", &mmc_object, memcache_class_entry_ptr, &key, &key_len, &time) == FAILURE) {
- return;
- }
- }
- else {
- if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s|l", &key, &key_len, &time) == FAILURE) {
- return;
- }
- }
- if (!mmc_get_pool(mmc_object, &pool TSRMLS_CC) || !pool->num_servers) {
- RETURN_FALSE;
- }
- if (mmc_prepare_key_ex(key, key_len, key_tmp, &key_tmp_len TSRMLS_CC) != MMC_OK) {
- RETURN_FALSE;
- }
- //
- while (result < 0 && (mmc = mmc_pool_find(pool, key_tmp, key_tmp_len TSRMLS_CC)) != NULL) {
- //
- if ((result = mmc_delete(mmc, key_tmp, key_tmp_len, time TSRMLS_CC)) < 0) {
- mmc_server_failure(mmc TSRMLS_CC);
- }
- }
- if (result > 0) {
- RETURN_TRUE;
- }
- RETURN_FALSE;
- }
- /* }}} */
- [/cc]
これでmemcache_deleteプロセスが終了しました.
当サイトの文章はすべて転載して、出典:http://blog.liubijian.com/php_memcache_code_analysis.html
... other posts by lbj