gearman 源码学习笔记3

本文详细解析了Gearman网络模块的实现原理,包括使用libevent高性能网络库进行网络功能的实现,从初始化套接字到绑定监听的过程,以及如何处理接受事件等关键步骤。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

这次说一把gearman的网络模块,gearman是利用libevent高性能网络库实现网络功能的。

首先看

 gearmand->ret= _listen_init(gearmand);
    if (gearmand->ret != GEARMAND_SUCCESS)
    {
      return gearmand->ret;
    }
gearman在_listen_init

<pre name="code" class="cpp">static gearmand_error_t _listen_init(gearmand_st *gearmand)
{
  for (uint32_t x= 0; x < gearmand->_port_list.size(); ++x)
  {
    struct addrinfo hints;
    struct addrinfo *addrinfo= NULL;

    gearmand_port_st *port= &gearmand->_port_list[x];

    memset(&hints, 0, sizeof(struct addrinfo));
    hints.ai_flags= AI_PASSIVE;
    hints.ai_socktype= SOCK_STREAM;

    {
      int ret= getaddrinfo(gearmand->host, port->port, &hints, &addrinfo);
      if (ret != 0)
      {
        char buffer[1024];

        int length= snprintf(buffer, sizeof(buffer), "%s:%s", gearmand->host ? gearmand->host : "<any>", port->port);
        if (length <= 0 or size_t(length) >= sizeof(buffer))
        {
          buffer[0]= 0;
        }

        if (addrinfo)
        {
          freeaddrinfo(addrinfo);
        }
        return gearmand_gai_error(buffer, ret);
      }
    }

    std::set<host_port_t> unique_hosts;
    for (struct addrinfo *addrinfo_next= addrinfo; addrinfo_next != NULL;
         addrinfo_next= addrinfo_next->ai_next)
    {
      char host[NI_MAXHOST];

      {
        int ret= getnameinfo(addrinfo_next->ai_addr, addrinfo_next->ai_addrlen, host,
                             NI_MAXHOST, port->port, NI_MAXSERV,
                             NI_NUMERICHOST | NI_NUMERICSERV);
        if (ret != 0)
        {
          gearmand_gai_error("getaddrinfo", ret);
          strncpy(host, "-", sizeof(host));
          strncpy(port->port, "-", sizeof(port->port));
        }
      }

      std::string host_string(host);
      std::string port_string(port->port);
      host_port_t check= std::make_pair(host_string, port_string);
      if (unique_hosts.find(check) != unique_hosts.end())
      {
        gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "Already listening on %s:%s", host, port->port);
        continue;
      }
      unique_hosts.insert(check);

      gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "Trying to listen on %s:%s", host, port->port);

      /*
        @note logic for this pulled from Drizzle.

        Sometimes the port is not released fast enough when stopping and
        restarting the server. This happens quite often with the test suite
        on busy Linux systems. Retry to bind the address at these intervals:
        Sleep intervals: 1, 2, 4,  6,  9, 13, 17, 22, ...
        Retry at second: 1, 3, 7, 13, 22, 35, 52, 74, ...
        Limit the sequence by drizzled_bind_timeout.
      */
      uint32_t waited;
      uint32_t this_wait;
      uint32_t retry;
      int ret= -1;
      int fd;
      for (waited= 0, retry= 1; ; retry++, waited+= this_wait)
      {
        { 
          gearmand_error_t socket_ret;
          if (gearmand_failed(socket_ret= set_socket(gearmand, fd, addrinfo_next)))
          {
            gearmand_sockfd_close(fd);
            freeaddrinfo(addrinfo);
            return socket_ret;
          }
        }

        errno= 0;
        if ((ret= bind(fd, addrinfo_next->ai_addr, addrinfo_next->ai_addrlen)) == 0)
        {
          // Success
          break;
        }
        // Protect our error
        ret= errno;
        gearmand_sockfd_close(fd);
        
        if (waited >= bind_timeout)
        {
          freeaddrinfo(addrinfo);
          return gearmand_log_error(GEARMAN_DEFAULT_LOG_PARAM, "Timeout occured when calling bind() for %s:%s", host, port->port);
        }

        if (ret != EADDRINUSE)
        {
          freeaddrinfo(addrinfo);
          return gearmand_perror(ret, "bind");
        }

        this_wait= retry * retry / 3 + 1;

        // We are in single user threads, so strerror() is fine.
        gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "Retrying bind(%s) on %s:%s %u + %u >= %u", 
                           strerror(ret), host, port->port,
                           waited, this_wait, bind_timeout);

        struct timespec requested;
        requested.tv_sec= this_wait;
        requested.tv_nsec= 0;

        nanosleep(&requested, NULL);
      }

      if (listen(fd, gearmand->backlog) == -1)
      {
        gearmand_perror(errno, "listen");

        gearmand_sockfd_close(fd);

        freeaddrinfo(addrinfo);
        return GEARMAND_ERRNO;
      }

      // Scoping note for eventual transformation
      {
        int* fd_list= (int *)realloc(port->listen_fd, sizeof(int) * (port->listen_count + 1));
        if (fd_list == NULL)
        {
          gearmand_perror(errno, "realloc");

          gearmand_sockfd_close(fd);

          freeaddrinfo(addrinfo);
          return GEARMAND_ERRNO;
        }

        port->listen_fd= fd_list;
      }

      port->listen_fd[port->listen_count]= fd;
      port->listen_count++;

      gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "Listening on %s:%s (%d)", host, port->port, fd);
    }

    freeaddrinfo(addrinfo);

    /* Report last socket() error if we couldn't find an address to bind. */
    if (port->listen_fd == NULL)
    {
      return gearmand_log_fatal(GEARMAN_DEFAULT_LOG_PARAM, "Could not bind/listen to any addresses");
    }

    assert(port->listen_event == NULL);
    port->listen_event= (struct event *)malloc(sizeof(struct event) * port->listen_count); // libevent POD
    if (port->listen_event == NULL)
    {
      return gearmand_merror("malloc(sizeof(struct event) * port->listen_count)", struct event, port->listen_count);
    }

    for (uint32_t y= 0; y < port->listen_count; ++y)
    {
      event_set(&(port->listen_event[y]), port->listen_fd[y], EV_READ | EV_PERSIST, _listen_event, port);

      if (event_base_set(gearmand->base, &(port->listen_event[y])) == -1)
      {
        return gearmand_perror(errno, "event_base_set()");
      }
    }
  }

  return GEARMAND_SUCCESS;
}



具体在socket_ret= set_socket(gearmand, fd, addrinfo_next) 初始化套接字

然后 bind listen(fd, gearmand->backlog) 

最终将 fd注册给libevent event_base_set(gearmand->base, &(port->listen_event[y])

当发生 accept事件时(其实就是tcp的三次握手)或触发_listen_event 处理事件

请看

static void _listen_event(int event_fd, short events __attribute__ ((unused)), void *arg)
{
  gearmand_port_st *port= (gearmand_port_st *)arg;
  struct sockaddr sa;

  socklen_t sa_len= sizeof(sa);
#if defined(HAVE_ACCEPT4) && HAVE_ACCEPT4
  int fd= accept4(event_fd, &sa, &sa_len, SOCK_NONBLOCK); //  SOCK_NONBLOCK);
#else
  int fd= accept(event_fd, &sa, &sa_len);
#endif

  if (fd == -1)
  {
    int local_error= errno;

    switch (local_error)
    {
    case EINTR:
      return;

    case ECONNABORTED:
    case EMFILE:
      gearmand_perror(local_error, "accept");
      return;

    default:
      break;
    }

    _clear_events(Gearmand());
    Gearmand()->ret= gearmand_perror(local_error, "accept");
    return;
  }
  gearmand_log_debug(GEARMAN_DEFAULT_LOG_PARAM, "accept() fd:%d", fd);

  /* 
    Since this is numeric, it should never fail. Even if it did we don't want to really error from it.
  */
  char host[NI_MAXHOST];
  char port_str[NI_MAXSERV];
  int error= getnameinfo(&sa, sa_len, host, NI_MAXHOST, port_str, NI_MAXSERV,
                         NI_NUMERICHOST | NI_NUMERICSERV);
  if (error != 0)
  {
    gearmand_gai_error("getnameinfo", error);
    strncpy(host, "-", sizeof(host));
    strncpy(port_str, "-", sizeof(port_str));
  }

  gearmand_log_info(GEARMAN_DEFAULT_LOG_PARAM, "Accepted connection from %s:%s", host, port_str);

  {
    int flags= 1;
    if (setsockopt(fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags)) == -1)
    {
      gearmand_log_perror(GEARMAN_DEFAULT_LOG_PARAM, errno, "%s:%s setsockopt(SO_KEEPALIVE)", host, port_str);
    }
  }

  gearmand_error_t ret= gearmand_con_create(Gearmand(), fd, host, port_str, port);
  if (ret == GEARMAND_MEMORY_ALLOCATION_FAILURE)
  {
    gearmand_sockfd_close(fd);
    return;
  }
  else if (ret != GEARMAND_SUCCESS)
  {
    Gearmand()->ret= ret;
    _clear_events(Gearmand());
  }
}

在gearmand_con_create里有比较复杂的逻辑包括 实现创建连接 roundrobbin 选择工作线程 已经pip通知工作线程。下班啦 下回再写啦。






评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值