细说memcached_set

一、memcached_set

在设置好memcached_st之后,就调用memcached_set(memcached_st *ptr, const char *key, size_t key_length, const char *value, size_t value_length, time_t expiration, uint32_t flags),它内部只是调用memcached_send(ptr, key, key_length, key, key_length, value, value_length, expiration, flags, 0, SET_OP)。SET_OP表示这是set操作。


二、memcached_send

1、initialize_query(memcached_st *self, bool increment_query_id),此处increment_query_id为true,主要完成self->query_id++。

2、memcached_validate_key_length(key_length, memcached_is_binary(ptr))检查key的长度。

3、memcached_key_test(*ptr, (const char **)&key, &key_length, 1)检查key。

4、uint32_t server_key= memcached_generate_hash_with_redistribution(ptr, group_key, group_key_length),此处group_key就是key。该函数选择一个服务器id,应该是一致性哈希的核心函数,暂不分析。

5、memcached_server_write_instance_st instance= memcached_server_instance_fetch(ptr, server_key)获得服务器。

6、根据协议类型,决定调用memcached_send_binary(...),还是memcached_send_ascii(ptr, instance, key, key_length, value, value_length, expiration, flags, cas, flush, reply, verb)。此外verb为SET_OP。


三、memcached_send_ascii

将参数放入libmemcached_io_vector_st vector[ ],然后调用memcached_vdo(instance, vector, 12, flush)。如果成功,memcached_server_response_increment(instance)即(instance)->cursor_active++;并且reply和flush为true,则还要执行memcached_response(instance, buffer, sizeof(buffer), NULL)。


四、memcached_vdo

1、memcached_connect(memcached_server_write_instance_st server)

memcached_return_t memcached_connect(memcached_server_write_instance_st server)
{
  if (server->fd != INVALID_SOCKET)  //这说明已经连接了
  {
    return MEMCACHED_SUCCESS;
  }



  bool in_timeout= false;
  memcached_return_t rc;
  if (memcached_failed(rc= backoff_handling(server, in_timeout)))  //防止由于出错而一直处于某个状态持续等待。其中也有可能调用run_distribution
  {
    set_last_disconnected_host(server);
    return rc;
  }

  if (LIBMEMCACHED_WITH_SASL_SUPPORT and server->root->sasl.callbacks and memcached_is_udp(server->root))
  {
    return memcached_set_error(*server, MEMCACHED_INVALID_HOST_PROTOCOL, MEMCACHED_AT, memcached_literal_param("SASL is not supported for UDP connections"));
  }

  /* We need to clean up the multi startup piece */
  switch (server->type)
  {
  case MEMCACHED_CONNECTION_UDP:
  case MEMCACHED_CONNECTION_TCP:
    rc= network_connect(server);

    if (LIBMEMCACHED_WITH_SASL_SUPPORT)
    {
      if (server->fd != INVALID_SOCKET and server->root->sasl.callbacks)
      {
        rc= memcached_sasl_authenticate_connection(server);
        if (memcached_failed(rc) and server->fd != INVALID_SOCKET)
        {
          WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
          (void)closesocket(server->fd);
          server->fd= INVALID_SOCKET;
        }
      }
    }
    break;

  case MEMCACHED_CONNECTION_UNIX_SOCKET:
    rc= unix_socket_connect(server);
    break;
  }

  if (memcached_success(rc))
  {
    memcached_mark_server_as_clean(server);  //设置server->server_failure_counter= 0;server->next_retry= 0;
    return rc;
  }

  set_last_disconnected_host(server);
  if (memcached_has_current_error(*server))     //这里的出错处理,以后再看了
  {
    memcached_mark_server_for_timeout(server);
    assert(memcached_failed(memcached_server_error_return(server)));
  }
  else
  {
    memcached_set_error(*server, rc, MEMCACHED_AT);
    memcached_mark_server_for_timeout(server);
  }

  LIBMEMCACHED_MEMCACHED_CONNECT_END();

  if (in_timeout)
  {
    return memcached_set_error(*server, MEMCACHED_SERVER_TEMPORARILY_DISABLED, MEMCACHED_AT);
  }

  return rc;
}

其中,对于MEMCACHED_CONNECTION_TCP,调用network_connect(server):

static memcached_return_t network_connect(memcached_server_st *server)
{
  bool timeout_error_occured= false;

  WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);
  WATCHPOINT_ASSERT(server->cursor_active == 0);

  /*
    We want to check both of these because if address_info_next has been fully tried, we want to do a new lookup to make sure we have picked up on any new DNS information.
  */
  if (server->address_info == NULL or server->address_info_next == NULL)
  {
    WATCHPOINT_ASSERT(server->state == MEMCACHED_SERVER_STATE_NEW);
    server->address_info_next= NULL;
    memcached_return_t rc;
    uint32_t counter= 5;
    while (--counter)
    {
      if ((rc= set_hostinfo(server)) != MEMCACHED_TIMEOUT)
      {
        break;
      }
    }

    if (memcached_failed(rc))
    {
      return rc;
    }
  }

  if (server->address_info_next == NULL)
  {
    server->address_info_next= server->address_info;
    server->state= MEMCACHED_SERVER_STATE_ADDRINFO;
  }

  /* Create the socket */
  while (server->address_info_next and server->fd == INVALID_SOCKET)
  {
    /* Memcache server does not support IPV6 in udp mode, so skip if not ipv4 */
    if (memcached_is_udp(server->root) and server->address_info_next->ai_family != AF_INET)
    {
      server->address_info_next= server->address_info_next->ai_next;
      continue;
    }

    if ((server->fd= socket(server->address_info_next->ai_family,
                            server->address_info_next->ai_socktype,
                            server->address_info_next->ai_protocol)) < 0)
    {
      return memcached_set_errno(*server, get_socket_errno(), NULL);
    }

    set_socket_options(server);  //设置O_NONBLOCK标志(总是使用非阻塞IO以防写死锁);同时根据server->root->flags,进行相应标志设置。

    /* connect to server */
    if ((connect(server->fd, server->address_info_next->ai_addr, server->address_info_next->ai_addrlen) != SOCKET_ERROR))
    {
      server->state= MEMCACHED_SERVER_STATE_CONNECTED;
      return MEMCACHED_SUCCESS;
    }

    /* An error occurred */
    switch (get_socket_errno())
    {
    case ETIMEDOUT:
      timeout_error_occured= true;
      break;

    case EWOULDBLOCK:
    case EINPROGRESS: // nonblocking mode - first return
    case EALREADY: // nonblocking mode - subsequent returns
      {
        server->state= MEMCACHED_SERVER_STATE_IN_PROGRESS;
        memcached_return_t rc= connect_poll(server);        //其中调用poll()来处理memcached的回应

        if (memcached_success(rc))
        {
          server->state= MEMCACHED_SERVER_STATE_CONNECTED;
          return MEMCACHED_SUCCESS;
        }

        // A timeout here is treated as an error, we will not retry
        if (rc == MEMCACHED_TIMEOUT)
        {
          timeout_error_occured= true;
        }
      }
      break;

    case EISCONN: // we are connected :-)
      WATCHPOINT_ASSERT(0); // This is a programmer's error
      break;

    case EINTR: // Special case, we retry ai_addr
      WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
      (void)closesocket(server->fd);
      server->fd= INVALID_SOCKET;
      continue;

    default:
      break;
    }

    WATCHPOINT_ASSERT(server->fd != INVALID_SOCKET);
    (void)closesocket(server->fd);
    server->fd= INVALID_SOCKET;
    server->address_info_next= server->address_info_next->ai_next;
  }

  WATCHPOINT_ASSERT(server->fd == INVALID_SOCKET);

  if (timeout_error_occured)
  {
    if (server->fd != INVALID_SOCKET)
    {
      (void)closesocket(server->fd);
      server->fd= INVALID_SOCKET;
    }
  }

  WATCHPOINT_STRING("Never got a good file descriptor");

  if (memcached_has_current_error(*server))
  {
    return memcached_server_error_return(server);
  }

  if (timeout_error_occured and server->state < MEMCACHED_SERVER_STATE_IN_PROGRESS)
  {
    return memcached_set_error(*server, MEMCACHED_TIMEOUT, MEMCACHED_AT);
  }

  return memcached_set_error(*server, MEMCACHED_CONNECTION_FAILURE, MEMCACHED_AT); /* The last error should be from connect() */
}
由于此处为非阻塞模式,所以connect()立即返回,并get_socket_errno()为EINPROGRESS,通过connect_poll()使用poll()完成最终的连接。最后server->state = MEMCACHED_SERVER_STATE_CONNECTED

2、ssize_t memcached_io_writev(memcached_server_write_instance_st ptr, libmemcached_io_vector_st vector[], const size_t number_of, const bool with_flush)

其中vector为参数数组。

ssize_t memcached_io_writev(memcached_server_write_instance_st ptr,
                            libmemcached_io_vector_st vector[],
                            const size_t number_of, const bool with_flush)
{
  ssize_t total= 0;

  for (size_t x= 0; x < number_of; x++, vector++)
  {
    ssize_t returnable;

    if (vector->length)
    {
      if ((returnable= _io_write(ptr, vector->buffer, vector->length, false)) == -1)  //将数据写入ptr->write_buffer
      {
        return -1;
      }
      total+= returnable;
    }
  }

  if (with_flush)
  {
    if (memcached_io_write(ptr) == false)  //内部调用_io_write(),进一步调用io_flush(),内部调用系统全局函数::send();若::send()失败,则可能调用io_wait()(内部调用poll)
    {                                    //io_flush()和io_wait()(写等待时)中,都会调用memcached_purge(),它的作用?清理输入缓冲区中的内容,和以前的版本保持兼容。
      return -1;
    }
  }

  return total;
}

五、memcached_response

定义于libmemcached/responce.cc,它会调用同名函数memcached_response(memcached_server_write_instance_st ptr, char *buffer, size_t buffer_length, memcached_result_st *result, uint64_t& numeric_value)。进一步会调用_read_one_response(ptr, buffer, buffer_length, result, numeric_value)。该函数先memcached_server_response_decrement(ptr)即(ptr)->cursor_active--,可以看出cursor_acive表示未处理的response的个数;

之后对于memcached_result_st * result == NULL,设置result为(memcached_st *)ptr->root中的result;最后调用textual_read_one_response()或者binary_read_one_response()(二进制协议)。

static memcached_return_t textual_read_one_response(memcached_server_write_instance_st ptr,
                                                    char *buffer, const size_t buffer_length,
                                                    memcached_result_st *result,
                                                    uint64_t& numeric_value)
{
  numeric_value= UINT64_MAX;
  size_t total_read;
  memcached_return_t rc= memcached_io_readline(ptr, buffer, buffer_length, total_read);
  //内部调用memcached_io_read,进一步调用::recv()。先一次性读入buffer,再分析出一行。
  if (memcached_failed(rc))
  {
    return rc;
  }
  assert(total_read);

  switch(buffer[0])
  {
  case 'V':
    {
      // VALUE
      if (buffer[1] == 'A' and buffer[2] == 'L' and buffer[3] == 'U' and buffer[4] == 'E') /* VALUE */
      {
        /* We add back in one because we will need to search for END */
        memcached_server_response_increment(ptr);
        return textual_value_fetch(ptr, buffer, result);
      }
      // VERSION
      else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'S' and buffer[4] == 'I' and buffer[5] == 'O' and buffer[6] == 'N') /* VERSION */
      {
        return MEMCACHED_SUCCESS;
      }
    }
    break;

  case 'S':
    {
      // STAT
      if (buffer[1] == 'T' and buffer[2] == 'A' and buffer[3] == 'T') /* STORED STATS */
      {
        memcached_server_response_increment(ptr);
        return MEMCACHED_STAT;
      }
      // SERVER_ERROR
      else if (buffer[1] == 'E' and buffer[2] == 'R' and buffer[3] == 'V' and buffer[4] == 'E' and buffer[5] == 'R'
               and buffer[6] == '_' 
               and buffer[7] == 'E' and buffer[8] == 'R' and buffer[9] == 'R' and buffer[10] == 'O' and buffer[11] == 'R' )
      {
        if (total_read == memcached_literal_param_size("SERVER_ERROR"))
        {
          return MEMCACHED_SERVER_ERROR;
        }

        if (total_read > memcached_literal_param_size("SERVER_ERROR object too large for cache") and
            (memcmp(buffer, memcached_literal_param("SERVER_ERROR object too large for cache")) == 0))
        {
          return MEMCACHED_E2BIG;
        }

        // Move past the basic error message and whitespace
        char *startptr= buffer + memcached_literal_param_size("SERVER_ERROR");
        if (startptr[0] == ' ')
        {
          startptr++;
        }

        char *endptr= startptr;
        while (*endptr != '\r' && *endptr != '\n') endptr++;

        return memcached_set_error(*ptr, MEMCACHED_SERVER_ERROR, MEMCACHED_AT, startptr, size_t(endptr - startptr));
      }
      // STORED
      else if (buffer[1] == 'T' and buffer[2] == 'O' and buffer[3] == 'R') //  and buffer[4] == 'E' and buffer[5] == 'D')
      {
        return MEMCACHED_STORED;
      }
    }
    break;
  ...        //多个case

  default:
    break;
  }

  buffer[total_read]= 0;

  return memcached_set_error(*ptr, MEMCACHED_UNKNOWN_READ_FAILURE, MEMCACHED_AT,
                             buffer, total_read);
}
最终,返回MEMCACHED_STORED,至此memcached_set()完成。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值