memcached深度分析(7)

發表于:2015-07-10來源:uml.org.cn作者:火龍果軟件點擊數: 標簽:數據庫
這個擴展是使用php的stream直接連接memcached服務器并通過socket發送命令的。它不像libmemcache那樣完善,也不支持add_server這種分布操作,但是因為它不依賴其它

  這個擴展是使用php的stream直接連接memcached服務器并通過socket發送命令的。它不像libmemcache那樣完善,也不支持add_server這種分布操作,但是因為它不依賴其它的外界程序,兼容性要好一些,也比較穩定。至于效率,差別不是很大。

  另外,有很多的PHP class可以使用,比如MemcacheClient.inc.php,phpclasses.org上可以找到很多,一般都是對perl client API的再封裝,使用方式很像。

  BSM_Memcache

  從C client來說,APR_Memcache是一個很成熟很穩定的client程序,支持線程鎖和原子級操作,保證運行的穩定性。不過它是基于APR的(APR將在最后一節介紹),沒有libmemcache的應用范圍廣,目前也沒有很多基于它開發的程序,現有的多是一些Apache Module,因為它不能脫離APR環境運行。但是APR倒是可以脫離Apache單獨安裝的,在APR網站上可以下載APR和APR-util,不需要有Apache,可以直接安裝,而且它是跨平臺的。

  BSM_Memcache是我在BS.Magic項目中開發的一個基于APR_Memcache的PHP擴展,說起來有點拗口,至少它把APR扯進了PHP擴展中。這個程序很簡單,也沒做太多的功能,只是一種形式的嘗試,它支持服務器分組。

  和mcache擴展支持多服務器分布存儲不同,BSM_Memcache支持多組服務器,每一組內的服務器還是按照hash方式來分布保存數據,但是兩個組中保存的數據是一樣的,也就是實現了熱備,它不會因為一臺服務器發生單點故障導致數據無法獲取,除非所有的服務器組都損壞(例如機房停電)。當然實現這個功能的代價就是性能上的犧牲,在每次添加刪除數據的時候都要掃描所有的組,在get數據的時候會隨機選擇一組服務器開始輪詢,一直到找到數據為止,正常情況下一次就可以獲取得到。

  BSM_Memcache只支持這幾個函數:

zend_function_entry bsm_memcache_functions[] =
{
    PHP_FE(mc_get,          NULL)
    PHP_FE(mc_set,          NULL)
    PHP_FE(mc_del,          NULL)
    PHP_FE(mc_add_group,    NULL)
    PHP_FE(mc_add_server,   NULL)
    PHP_FE(mc_shutdown,     NULL)
    {NULL, NULL, NULL}
};
mc_add_group函數返回一個整形(其實應該是一個object,我偷懶了~_~)作為組ID,
mc_add_server的時候要提供兩個參數,一個是組ID,一個是服務器地址(ADDR : PORT)。 
/**
* Add a server group
*/
PHP_FUNCTION(mc_add_group)
{
    apr_int32_t group_id;
    apr_status_t rv;
 
    if (0 != ZEND_NUM_ARGS())
    {
        WRONG_PARAM_COUNT;
        RETURN_NULL();
    }
 
    group_id = free_group_id();
    if (-1 == group_id)
    {
        RETURN_FALSE;
    }
 
    apr_memcache_t *mc;
    rv = apr_memcache_create(p, MAX_G_SERVER, 0, &mc);
 
    add_group(group_id, mc);
 
    RETURN_DOUBLE(group_id);
}
/**
* Add a server into group
*/
PHP_FUNCTION(mc_add_server)
{
    apr_status_t rv;
    apr_int32_t group_id;
    double g;
    char *srv_str;
    int srv_str_l;
    if (2 != ZEND_NUM_ARGS())
    {
        WRONG_PARAM_COUNT;
    }
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ds", &g, &srv_str, &srv_str_l) == FAILURE)
    {
        RETURN_FALSE;
    }
    group_id = (apr_int32_t) g;
 
    if (-1 == is_validate_group(group_id))
    {
        RETURN_FALSE;
    }
    char *host, *scope;
    apr_port_t port;
    rv = apr_parse_addr_port(&host, &scope, &port, srv_str, p);
    if (APR_SUCCESS == rv)
    {
        // Create this server object
        apr_memcache_server_t *st;
        rv = apr_memcache_server_create(p, host, port, 0, 64, 1024, 600, &st);
        if (APR_SUCCESS == rv)
        {
            if (NULL == mc_groups[group_id])
            {
                RETURN_FALSE;
            }
 
            // Add server
            rv = apr_memcache_add_server(mc_groups[group_id], st);
 
            if (APR_SUCCESS == rv)
            {
                RETURN_TRUE;
            }
        }
    }
 
    RETURN_FALSE;
}
在set和del數據的時候,要循環所有的組: 
/**
* Store item into all groups
*/
PHP_FUNCTION(mc_set)
{
    char *key, *value;
    int key_l, value_l;
    double ttl = 0;
    double set_ct = 0;
    if (2 != ZEND_NUM_ARGS())
    {
        WRONG_PARAM_COUNT;
    }
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "ss|d", &key, &key_l, &value, &value_l, ttl) == FAILURE)
    {
        RETURN_FALSE;
    }
    // Write data into every object
    apr_int32_t i = 0;
    if (ttl < 0)
    {
        ttl = 0;
    }
    apr_status_t rv;
 
    for (i = 0; i < MAX_GROUP; i++)
    {
        if (0 == is_validate_group(i))
        {
            // Write it!
            rv = apr_memcache_add(mc_groups[i], key, value, value_l, (apr_uint32_t) ttl, 0);
            if (APR_SUCCESS == rv)
            {
                set_ct++;
            }
        }
    }
 
    RETURN_DOUBLE(set_ct);
}
在mc_get中,首先要隨機選擇一個組,然后從這個組開始輪詢:

/**
* Fetch a item from a random group
*/
PHP_FUNCTION(mc_get)
{               
    char *key, *value = NULL;
    int key_l;
    apr_size_t value_l;
 
    if (1 != ZEND_NUM_ARGS())
    {
        WRONG_PARAM_COUNT;
    }
    if (zend_parse_parameters(ZEND_NUM_ARGS() TSRMLS_CC, "s", &key, &key_l) == FAILURE)
    {
        RETURN_MULL();
    }
    // I will try ...
    // Random read
    apr_int32_t curr_group_id = random_group();
    apr_int32_t i = 0;
    apr_int32_t try = 0;
    apr_uint32_t flag;
    apr_memcache_t *oper;
    apr_status_t rv;
 
    for (i = 0; i < MAX_GROUP; i++)
    {
        try = i + curr_group_id;
        try = try % MAX_GROUP;
        if (0 == is_validate_group(try))
        {
            // Get a value
            oper = mc_groups[try];
            rv = apr_memcache_getp(mc_groups[try], p, (const char *) key, &value, &value_l, 0);
            if (APR_SUCCESS == rv)

            {
                RETURN_STRING(value, 1);
            }
        }
    }
 
    RETURN_FALSE;
}
/**
* Random group id
* For mc_get()
*/
apr_int32_t random_group()
{
    struct timeval tv;
    struct timezone tz;
    int usec;
 
    gettimeofday(&tv, &tz);
 
    usec = tv.tv_usec;
 
    int curr = usec % count_group();
 
    return (apr_int32_t) curr;
}

原文轉自:http://www.uml.org.cn/sjjm/201411134.asp

国产97人人超碰caoprom_尤物国产在线一区手机播放_精品国产一区二区三_色天使久久综合给合久久97