一、memcached_get
内部只是调用memcached_get_by_key(ptr, NULL, 0, key, key_length, value_length, flags, error)。memcached_get_by_key()内部主要调用memcached_mget_by_key_real()和memcached_fetch()。之后,它还会调用一次针对dummy的memcached_fetch()。
由于memcached_get()先使用memcached_mget_by_key_real()向某个服务器发送命令,之后调用的memcached_fetch()仅仅是获得一个key/value记录,至于从哪个机器获得以及获得的key是什么都是不做限制的,因此memcached_get()并不适合于多线程中。这种说法对不对??
哈哈,确实如此。因此需要为每个线程使用独立的memcached_st,从而每个线程使用不同的套接口,但这也导致连接数的增多。
参考链接:http://lists.libmemcached.org/pipermail/libmemcached-discuss/2008-August/000420.html
二、memcached_mget_by_key_real
static memcached_return_t memcached_mget_by_key_real(memcached_st *ptr,
const char *group_key,
size_t group_key_length,
const char * const *keys,
const size_t *key_length,
size_t number_of_keys,
bool mget_mode)
{
bool failures_occured_in_sending= false;
const char *get_command= "get ";
uint8_t get_command_length= 4;
unsigned int master_server_key= (unsigned int)-1; /* 0 is a valid server id! */
memcached_return_t rc;
if (memcached_failed(rc= initialize_query(ptr, true))) //主要作用ptr->query_id++
{
return rc;
}
...
if (number_of_keys == 0)
{
return memcached_set_error(*ptr, MEMCACHED_NOTFOUND, MEMCACHED_AT, memcached_literal_param("number_of_keys was zero"));
}
if (memcached_failed(memcached_key_test(*ptr, keys, key_length, number_of_keys)))
{
return memcached_set_error(*ptr, MEMCACHED_BAD_KEY_PROVIDED, MEMCACHED_AT, memcached_literal_param("A bad key value was provided"));
}
bool is_group_key_set= false;
if (group_key and group_key_length)
{
if (memcached_failed(memcached_key_test(*ptr, (const char * const *)&group_key, &group_key_length, 1)))
{
return memcached_set_error(*ptr, MEMCACHED_BAD_KEY_PROVIDED, MEMCACHED_AT, memcached_literal_param("A bad group key was provided."));
}
master_server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length);
is_group_key_set= true;
}
/*
Here is where we pay for the non-block API. We need to remove any data sitting
in the queue before we start our get.
It might be optimum to bounce the connection if count > some number.
*/
for (uint32_t x= 0; x < memcached_server_count(ptr); x++) //将输入缓冲区中或即将进入输入缓冲区的response处理掉。
{
memcached_server_write_instance_st instance=
memcached_server_instance_fetch(ptr, x);
if (memcached_server_response_count(instance))
{
char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
if (ptr->flags.no_block)
{
memcached_io_write(instance);
}
while(memcached_server_response_count(instance))
{
(void)memcached_response(instance, buffer, MEMCACHED_DEFAULT_COMMAND_SIZE, &ptr->result);
}
}
}
if (ptr->flags.binary_protocol)
{
return binary_mget_by_key(ptr, master_server_key, is_group_key_set, keys,
key_length, number_of_keys, mget_mode);
}
if (ptr->flags.support_cas)
{
get_command= "gets ";
get_command_length= 5;
}
/*
If a server fails we warn about errors and start all over with sending keys
to the server.
*/
WATCHPOINT_ASSERT(rc == MEMCACHED_SUCCESS);
size_t hosts_connected= 0;
for (uint32_t x= 0; x < number_of_keys; x++)
{
memcached_server_write_instance_st instance;
uint32_t server_key;
if (is_group_key_set)
{
server_key= master_server_key;
}
else
{
server_key= memcached_generate_hash_with_redistribution(ptr, keys[x], key_length[x]); //这个暂不讨论,只是产生服务器id
}
instance= memcached_server_instance_fetch(ptr, server_key);
libmemcached_io_vector_st vector[]=
{
{ get_command, get_command_length },
{ memcached_array_string(ptr->_namespace), memcached_array_size(ptr->_namespace) },
{ keys[x], key_length[x] },
{ memcached_literal_param(" ") }
};
if (memcached_server_response_count(instance) == 0)
{
rc= memcached_connect(instance); //连接尚未断开,所以直接返回成功
if (memcached_failed(rc))
{
memcached_set_error(*instance, rc, MEMCACHED_AT);
continue;
}
hosts_connected++;
if ((memcached_io_writev(instance, vector, 4, false)) == -1) //将命令写入instance的write_buffer
{
failures_occured_in_sending= true;
continue;
}
WATCHPOINT_ASSERT(instance->cursor_active == 0);
memcached_server_response_increment(instance); //执行(instance)->cursor_active++
WATCHPOINT_ASSERT(instance->cursor_active == 1);
}
else
{
if ((memcached_io_writev(instance, (vector + 1), 3, false)) == -1)
{
memcached_server_response_reset(instance);
failures_occured_in_sending= true;
continue;
}
}
}
if (hosts_connected == 0)
{
LIBMEMCACHED_MEMCACHED_MGET_END();
if (memcached_failed(rc))
{
return rc;
}
return memcached_set_error(*ptr, MEMCACHED_NO_SERVERS, MEMCACHED_AT);
}
/*
Should we muddle on if some servers are dead?
*/
bool success_happened= false;
for (uint32_t x= 0; x < memcached_server_count(ptr); x++)
{
memcached_server_write_instance_st instance=
memcached_server_instance_fetch(ptr, x);
if (memcached_server_response_count(instance))
{
/* We need to do something about non-connnected hosts in the future */
if ((memcached_io_write(instance, "\r\n", 2, true)) == -1) //此时才将instance->write_buffer中的命令发给服务器
{
failures_occured_in_sending= true;
}
else
{
success_happened= true;
}
}
}
LIBMEMCACHED_MEMCACHED_MGET_END();
if (failures_occured_in_sending && success_happened)
{
return MEMCACHED_SOME_ERRORS;
}
if (success_happened)
return MEMCACHED_SUCCESS;
return MEMCACHED_FAILURE; // Complete failure occurred
}
三、memcached_fetch
当memcached_mget_by_key_real()执行完后,即我们已经向服务器发送了指令,服务器会给我们发送如"VALUE key 0 5\r\n"value\r\nEND\r\n"这样的答复。
第一次memcached_fetch()处理了"VALUE key 0 5\r\n"value\r\n"部分的数据,获得value值;之后,memcached_get_by_key()又一次调用memcached_fetch(),这一次是个dummy,当value最终textual_read_one_response()解析"END\r\n",并返回MEMCACHED_END。至此,一次完整的memcached_get()就算完成了。
经分析,memcached_fetch(memcached_st *ptr, char *key, size_t *key_length, size_t *value_length, uint32_t *flags, memcached_return_t *error)的作用是获得一条key/value,放入ptr->result(struct memcached_result_st)中,至于从哪个服务器获得、获得哪个key并不确定。
memcached_fetch()主要调用memcached_fetch_result()。
1、memcached_fetch_result(memcached_st *ptr, memcached_result_st *result, memcached_return_t *error)
其中,result传入&ptr->result。
memcached_result_st *memcached_fetch_result(memcached_st *ptr,
memcached_result_st *result,
memcached_return_t *error)
{
...
*error= MEMCACHED_MAXIMUM_RETURN; // We use this to see if we ever go into the loop
memcached_server_st *server;
while ((server= memcached_io_get_readable_server(ptr))) //获得一个可读的服务器
{
char buffer[MEMCACHED_DEFAULT_COMMAND_SIZE];
*error= memcached_response(server, buffer, sizeof(buffer), result);
if (*error == MEMCACHED_IN_PROGRESS)
{
continue;
}
else if (*error == MEMCACHED_SUCCESS)
{
result->count++; //成功则result->count++,表示引用计数。
return result; //成功则直接返回
}
else if (*error == MEMCACHED_END)
{
memcached_server_response_reset(server); //执行(server)->cursor_active=0,这就是解析"END\r\n"后的效果。
} //可以看到解析到"END\r\n"并不退出循环,而是看是不是还有可读的服务器?
else if (*error != MEMCACHED_NOTFOUND)
{
break;
}
}
if (*error == MEMCACHED_NOTFOUND and result->count)
{
*error= MEMCACHED_END;
}
else if (*error == MEMCACHED_MAXIMUM_RETURN and result->count)
{
*error= MEMCACHED_END;
}
else if (*error == MEMCACHED_MAXIMUM_RETURN) // while() loop was never entered
{
*error= MEMCACHED_NOTFOUND;
}
else if (*error == MEMCACHED_SUCCESS)
{
*error= MEMCACHED_END;
}
else if (result->count == 0)
{
*error= MEMCACHED_NOTFOUND;
}
/* We have completed reading data */ //对于MEMCACHED_END,往下继续执行
if (memcached_is_allocated(result))
{
memcached_result_free(result);
}
else
{
result->count= 0; //引用计数设为0
memcached_string_reset(&result->value);
}
return NULL;
}
根据上一节,memcached_response()会调用_read_one_response(),进而调用textual_read_one_response(),对于"VALUE",会调用textual_value_fetch(ptr, buffer, result)。该函数在解析完"VALUE key 0 5\r\n"(填写memcached_result_st* result)之后会调用memcached_io_read()将随后的value值(即"value\r\n")读入result->value(即struct memcached_string_st)。不过这里还有一行"END\r\n"没有解析,留给第二次memcached_fetch()(dummy效果)处理。
2、memcached_io_get_readable_server(memcached_st *memc)
memcached_server_write_instance_st memcached_io_get_readable_server(memcached_st *memc)
{
#define MAX_SERVERS_TO_POLL 100
struct pollfd fds[MAX_SERVERS_TO_POLL];
unsigned int host_index= 0;
for (uint32_t x= 0; x < memcached_server_count(memc) && host_index < MAX_SERVERS_TO_POLL; ++x)
{
memcached_server_write_instance_st instance= memcached_server_instance_fetch(memc, x);
if (instance->read_buffer_length > 0) /* I have data in the buffer */
{
return instance;
}
if (memcached_server_response_count(instance) > 0)
{
fds[host_index].events = POLLIN;
fds[host_index].revents = 0;
fds[host_index].fd = instance->fd;
++host_index;
}
}
if (host_index < 2)
{
/* We have 0 or 1 server with pending events.. */
for (uint32_t x= 0; x< memcached_server_count(memc); ++x)
{
memcached_server_write_instance_st instance=
memcached_server_instance_fetch(memc, x);
if (memcached_server_response_count(instance) > 0)
{
return instance;
}
}
return NULL;
}
int error= poll(fds, host_index, memc->poll_timeout);
switch (error)
{
case -1:
memcached_set_errno(*memc, get_socket_errno(), MEMCACHED_AT);
/* FALLTHROUGH */
case 0:
break;
default:
for (size_t x= 0; x < host_index; ++x)
{
if (fds[x].revents & POLLIN)
{
for (uint32_t y= 0; y < memcached_server_count(memc); ++y)
{
memcached_server_write_instance_st instance=
memcached_server_instance_fetch(memc, y);
if (instance->fd == fds[x].fd)
return instance;
}
}
}
}
return NULL;
}