nginx中怎么實(shí)現(xiàn)一個事件模塊

nginx中怎么實(shí)現(xiàn)一個事件模塊,相信很多沒有經(jīng)驗(yàn)的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。

夏縣ssl適用于網(wǎng)站、小程序/APP、API接口等需要進(jìn)行數(shù)據(jù)傳輸應(yīng)用場景,ssl證書未來市場廣闊!成為成都創(chuàng)新互聯(lián)公司的ssl證書銷售渠道,可以享受市場價格4-6折優(yōu)惠!如果有意向歡迎電話聯(lián)系或者加微信:18982081108(備注:SSL證書合作)期待與您的合作!

1. ngx_events_block()----events配置塊解析

        nginx在解析nginx.conf配置文件時,如果當(dāng)前解析的配置項名稱為events,并且是一個配置塊,則會調(diào)用ngx_events_block()方法解析該配置塊,如下是該方法的源碼:

static char * ngx_events_block(ngx_conf_t *cf, ngx_command_t *cmd, void *conf) {
  char *rv;
  void ***ctx;
  ngx_uint_t i;
  ngx_conf_t pcf;
  ngx_event_module_t *m;

  // 如果存儲事件模塊配置數(shù)據(jù)的配置項不為空,說明已經(jīng)解析過配置項了,因而直接返回
  if (*(void **) conf) {
    return "is duplicate";
  }

  // 這里主要是計算event模塊的個數(shù),并且將各個event模塊的相對順序標(biāo)記在了該模塊的ctx_index屬性中
  ngx_event_max_module = ngx_count_modules(cf->cycle, NGX_EVENT_MODULE);

  // 創(chuàng)建一個存儲配置項數(shù)組的指針
  ctx = ngx_pcalloc(cf->pool, sizeof(void *));
  if (ctx == NULL) {
    return NGX_CONF_ERROR;
  }

  // 為配置項指針申請數(shù)組內(nèi)存
  *ctx = ngx_pcalloc(cf->pool, ngx_event_max_module * sizeof(void *));
  if (*ctx == NULL) {
    return NGX_CONF_ERROR;
  }

  // 將數(shù)組值賦值到conf中,也即關(guān)聯(lián)到核心配置對象ngx_cycle_t中
  *(void **) conf = ctx;

  for (i = 0; cf->cycle->modules[i]; i++) {
    if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
      continue;
    }

    m = cf->cycle->modules[i]->ctx;

    // 如果當(dāng)前模塊的create_conf()方法不為空,則調(diào)用該方法創(chuàng)建存儲配置項的結(jié)構(gòu)體
    if (m->create_conf) {
      (*ctx)[cf->cycle->modules[i]->ctx_index] = m->create_conf(cf->cycle);
      if ((*ctx)[cf->cycle->modules[i]->ctx_index] == NULL) {
        return NGX_CONF_ERROR;
      }
    }
  }

  // 這里將*cf結(jié)構(gòu)體進(jìn)行了復(fù)制,臨時存儲在pcf中,然后初始化當(dāng)前的*cf結(jié)構(gòu)體的模塊相關(guān)的參數(shù),
  // 以進(jìn)行下一步的解析
  pcf = *cf;
  cf->ctx = ctx;
  cf->module_type = NGX_EVENT_MODULE;
  cf->cmd_type = NGX_EVENT_CONF;

  // 解析events{}配置塊中的子配置項
  rv = ngx_conf_parse(cf, NULL);

  // 重新將pcf復(fù)制給*cf,以供后面返回使用
  *cf = pcf;

  if (rv != NGX_CONF_OK) {
    return rv;
  }

  // 到這里,說明events{}配置塊的配置項都解析完成了,因而這里調(diào)用各個模塊的init_conf()方法,
  // 進(jìn)行配置項的初始化和合并工作
  for (i = 0; cf->cycle->modules[i]; i++) {
    if (cf->cycle->modules[i]->type != NGX_EVENT_MODULE) {
      continue;
    }

    m = cf->cycle->modules[i]->ctx;

    // 如果當(dāng)前模塊的init_conf()不為空,則調(diào)用其init_conf()方法初始化配置項
    if (m->init_conf) {
      rv = m->init_conf(cf->cycle, (*ctx)[cf->cycle->modules[i]->ctx_index]);
      if (rv != NGX_CONF_OK) {
        return rv;
      }
    }
  }

  return NGX_CONF_OK;
}

        ngx_events_block()方法主要完成的工作有如下幾個:

  • 調(diào)用ngx_count_modules()方法對事件模塊序號進(jìn)行標(biāo)記,需要注意的是,這里的排序是針對當(dāng)前模塊在所有事件類型模塊中的順序進(jìn)行標(biāo)記,并且將序號保存在各模塊的ctx_index屬性中,比如這里的事件類型核心模塊ngx_event_core_modulectx_index就為0;

  • 為指針ctx申請內(nèi)存空間,并且申請一個數(shù)組,將其地址賦值給ctx指針,這里的數(shù)組長度就為事件模塊的數(shù)目。其實(shí)這里的數(shù)組就是用來保存每個事件模塊的配置對象的,當(dāng)前事件模塊在所有事件模塊中的相對位置就對應(yīng)于該數(shù)組中的相對位置,這里的相對位置也即前一步中計算得到的ctx_index

  • 調(diào)用各個事件模塊的create_conf()方法創(chuàng)建各自的配置結(jié)構(gòu)體,并且將其保存在ctx指針指向的數(shù)組中;

  • 調(diào)用ngx_conf_parse()方法對配置文件繼續(xù)解析,前面我們已經(jīng)講到,ngx_events_block()方法就是解析到events配置項的時候才調(diào)用的,因而這里的ngx_conf_parse()方法的調(diào)用就是繼續(xù)解析events配置塊的子配置項,而該方法調(diào)用完成則說明events配置塊里的配置項都已經(jīng)解析完成;

  • 調(diào)用各個模塊的init_conf()方法對配置項進(jìn)行初始化,簡單的說,就是,由于在nginx.conf中只配置了部分配置項的值,而剩余的配置項就由init_conf()方法來設(shè)置默認(rèn)值;

2. ngx_event_init_conf()----檢查事件模塊配置結(jié)構(gòu)體是否正常創(chuàng)建

        在nginx解析完nginx.conf配置文件的所有配置項后(包括前一步中講解的對events配置項的解析),其就會調(diào)用所有核心模塊的init_conf()方法對核心模塊的配置項進(jìn)行初始化。這里的核心模塊就包括ngx_events_module,該模塊的init_conf()方法指向的就是這里的ngx_event_init_conf()方法,該方法本質(zhì)上并沒有做什么工作,只是檢查了是否創(chuàng)建了存儲事件模塊配置項的結(jié)構(gòu)體數(shù)組。如下是ngx_event_init_conf()方法的源碼:

static char *ngx_event_init_conf(ngx_cycle_t *cycle, void *conf) {
  if (ngx_get_conf(cycle->conf_ctx, ngx_events_module) == NULL) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0,
                  "no \"events\" section in configuration");
    return NGX_CONF_ERROR;
  }

  return NGX_CONF_OK;
}

        上面兩個方法就是ngx_events_module核心模塊的兩個主要的配置方法,可以看到,這個核心模塊的主要作用就是創(chuàng)建了一個數(shù)組,用于存儲各個事件模塊的配置結(jié)構(gòu)體的。下面我們來看一下事件核心模塊的主要方法。

3. ngx_event_core_create_conf()----創(chuàng)建事件核心模塊配置結(jié)構(gòu)體

        在第1點(diǎn)中我們講到,解析events配置塊的子配置項之前,會調(diào)用各個事件模塊的create_conf()方法來創(chuàng)建其使用的存儲配置數(shù)據(jù)的結(jié)構(gòu)體,而后調(diào)用ngx_conf_parse()方法來解析子配置項,接著調(diào)用各個事件模塊的init_conf()方法初始化各個模塊配置數(shù)據(jù)的結(jié)構(gòu)體。這里ngx_event_core_module_ctx就是一個事件類型的模塊,其create_conf屬性指向的就是ngx_event_core_create_conf()方法,而init_conf屬性指向的就是ngx_event_core_init_conf()方法。這一節(jié)我們首先講解ngx_event_core_create_conf()方法的實(shí)現(xiàn)原理:

static void *ngx_event_core_create_conf(ngx_cycle_t *cycle) {
  ngx_event_conf_t *ecf;

  ecf = ngx_palloc(cycle->pool, sizeof(ngx_event_conf_t));
  if (ecf == NULL) {
    return NULL;
  }

  ecf->connections = NGX_CONF_UNSET_UINT;
  ecf->use = NGX_CONF_UNSET_UINT;
  ecf->multi_accept = NGX_CONF_UNSET;
  ecf->accept_mutex = NGX_CONF_UNSET;
  ecf->accept_mutex_delay = NGX_CONF_UNSET_MSEC;
  ecf->name = (void *) NGX_CONF_UNSET;

  return ecf;
}

        可以看到,這里的ngx_event_core_create_conf()方法本質(zhì)上就是創(chuàng)建了一個ngx_event_conf_t結(jié)構(gòu)體,并且將各個屬性都設(shè)置為未設(shè)置狀態(tài)。

4. ngx_event_core_init_conf()----初始化配置結(jié)構(gòu)體

        前面我們講到,在解析完各個子配置項之后,nginx會調(diào)用各個事件模塊的init_conf()方法,這里的核心事件模塊就是這個ngx_event_core_init_conf()方法,如下是該方法的源碼:

static char * ngx_event_core_init_conf(ngx_cycle_t *cycle, void *conf) {
  ngx_event_conf_t *ecf = conf;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)
  int                  fd;
#endif
  ngx_int_t i;
  ngx_module_t *module;
  ngx_event_module_t *event_module;

  module = NULL;

#if (NGX_HAVE_EPOLL) && !(NGX_TEST_BUILD_EPOLL)

  // 測試是否具有創(chuàng)建epoll句柄的權(quán)限
  fd = epoll_create(100);

  if (fd != -1) {
    // 關(guān)閉創(chuàng)建的epoll句柄,并且將module指向epoll模塊
      (void) close(fd);
      module = &ngx_epoll_module;

  } else if (ngx_errno != NGX_ENOSYS) {
      module = &ngx_epoll_module;
  }
#endif

  // 這里,如果沒有前面判斷的模塊類型,則默認(rèn)使用事件模塊中的第一個模塊作為事件處理模型
  if (module == NULL) {
    for (i = 0; cycle->modules[i]; i++) {

      if (cycle->modules[i]->type != NGX_EVENT_MODULE) {
        continue;
      }

      event_module = cycle->modules[i]->ctx;

      if (ngx_strcmp(event_module->name->data, event_core_name.data) == 0) {
        continue;
      }

      module = cycle->modules[i];
      break;
    }
  }

  // 如果此時module還是為NULL,則返回異常
  if (module == NULL) {
    ngx_log_error(NGX_LOG_EMERG, cycle->log, 0, "no events module found");
    return NGX_CONF_ERROR;
  }

  // 下面的操作主要是判斷各個屬性是否為初始設(shè)置的無效值,如果是,則說明nginx.conf中沒有配置
  // 關(guān)于該屬性的配置項,那么這里就會為該屬性設(shè)置默認(rèn)值
  ngx_conf_init_uint_value(ecf->connections, DEFAULT_CONNECTIONS);
  cycle->connection_n = ecf->connections;

  ngx_conf_init_uint_value(ecf->use, module->ctx_index);

  event_module = module->ctx;
  ngx_conf_init_ptr_value(ecf->name, event_module->name->data);

  ngx_conf_init_value(ecf->multi_accept, 0);
  ngx_conf_init_value(ecf->accept_mutex, 0);
  ngx_conf_init_msec_value(ecf->accept_mutex_delay, 500);

  return NGX_CONF_OK;
}

        ngx_event_core_init_conf()方法的主要做了兩件事:

  • 選擇當(dāng)前所使用的模塊,如果沒指定,則默認(rèn)使用第一個事件模塊;

  • 初始化事件核心模塊的配置結(jié)構(gòu)體的各個屬性值為默認(rèn)值。

5. ngx_event_module_init()----核心模塊的配置項初始化

        對于ngx_event_core_module模塊而言,其還指定了兩個方法,一個是用于初始化模塊的ngx_event_module_init()方法,另一個是用于worker進(jìn)程執(zhí)行主循環(huán)邏輯之前進(jìn)行調(diào)用的ngx_event_process_init()方法。ngx_event_module_init()方法是在master進(jìn)程中調(diào)用的,其會在解析完nginx.conf文件中的所有配置項之后調(diào)用,本質(zhì)上,該方法的作用就是對當(dāng)前配置的核心模塊(事件模塊)進(jìn)行初始化。如下是ngx_event_module_init()方法的源碼:

/**
 * 當(dāng)前方法的主要作用是申請一塊用于存儲統(tǒng)計數(shù)據(jù)的共享內(nèi)存,然后設(shè)置ngx_accept_mutex_ptr、
 * ngx_connection_counter、ngx_temp_number等變量的地址,如果開啟了slab stat,
 * 那么還會設(shè)置ngx_stat_accepted、ngx_stat_handled、ngx_stat_requests等的地址,以統(tǒng)計更多的數(shù)據(jù)
 */
static ngx_int_t ngx_event_module_init(ngx_cycle_t *cycle) {
  void ***cf;
  u_char *shared;
  size_t size, cl;
  ngx_shm_t shm;
  ngx_time_t *tp;
  ngx_core_conf_t *ccf;
  ngx_event_conf_t *ecf;

  // 獲取core event module的配置結(jié)構(gòu)體
  cf = ngx_get_conf(cycle->conf_ctx, ngx_events_module);
  ecf = (*cf)[ngx_event_core_module.ctx_index];

  if (!ngx_test_config && ngx_process <= NGX_PROCESS_MASTER) {
    ngx_log_error(NGX_LOG_NOTICE, cycle->log, 0,
                  "using the \"%s\" event method", ecf->name);
  }

  // 獲取core module的配置對象
  ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);

  ngx_timer_resolution = ccf->timer_resolution;

#if !(NGX_WIN32)
  {
    ngx_int_t limit;
    struct rlimit rlmt;

    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "getrlimit(RLIMIT_NOFILE) failed, ignored");

    } else {
      // 這里主要是檢查當(dāng)前事件模塊配置的connections數(shù)目是否超過了操作系統(tǒng)限制的最大文件句柄數(shù),
      // 或者超過了配置文件中指定的最大文件句柄數(shù)
      if (ecf->connections > (ngx_uint_t) rlmt.rlim_cur
          && (ccf->rlimit_nofile == NGX_CONF_UNSET
              || ecf->connections > (ngx_uint_t) ccf->rlimit_nofile)) {
        limit = (ccf->rlimit_nofile == NGX_CONF_UNSET) ?
                (ngx_int_t) rlmt.rlim_cur : ccf->rlimit_nofile;

        ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                      "%ui worker_connections exceed "
                      "open file resource limit: %i",
                      ecf->connections, limit);
      }
    }
  }
#endif /* !(NGX_WIN32) */


  if (ccf->master == 0) {
    return NGX_OK;
  }

  if (ngx_accept_mutex_ptr) {
    return NGX_OK;
  }


  /* cl should be equal to or greater than cache line size */

  cl = 128;

  size = cl            /* ngx_accept_mutex */
         + cl          /* ngx_connection_counter */
         + cl;         /* ngx_temp_number */

#if (NGX_STAT_STUB)

  size += cl           /* ngx_stat_accepted */
         + cl          /* ngx_stat_handled */
         + cl          /* ngx_stat_requests */
         + cl          /* ngx_stat_active */
         + cl          /* ngx_stat_reading */
         + cl          /* ngx_stat_writing */
         + cl;         /* ngx_stat_waiting */

#endif

  // 設(shè)置共享內(nèi)存的大小
  shm.size = size;
  ngx_str_set(&shm.name, "nginx_shared_zone");
  shm.log = cycle->log;

  // 為共享內(nèi)存結(jié)構(gòu)體申請內(nèi)存塊
  if (ngx_shm_alloc(&shm) != NGX_OK) {
    return NGX_ERROR;
  }

  // addr就是申請的共享內(nèi)存塊的地址
  shared = shm.addr;

  ngx_accept_mutex_ptr = (ngx_atomic_t *) shared;
  ngx_accept_mutex.spin = (ngx_uint_t) -1;

  if (ngx_shmtx_create(&ngx_accept_mutex, (ngx_shmtx_sh_t *) shared, cycle->lock_file.data) != NGX_OK) {
    return NGX_ERROR;
  }

  // 獲取ngx_connection_counter的地址
  ngx_connection_counter = (ngx_atomic_t *) (shared + 1 * cl);

  // 將ngx_connection_counter的值設(shè)置為1
  (void) ngx_atomic_cmp_set(ngx_connection_counter, 0, 1);

  ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                 "counter: %p, %uA",
                 ngx_connection_counter, *ngx_connection_counter);

  // 獲取ngx_temp_number的地址
  ngx_temp_number = (ngx_atomic_t *) (shared + 2 * cl);

  tp = ngx_timeofday();

  // 生成一個隨機(jī)數(shù)
  ngx_random_number = (tp->msec << 16) + ngx_pid;

#if (NGX_STAT_STUB)

  ngx_stat_accepted = (ngx_atomic_t *) (shared + 3 * cl);
  ngx_stat_handled = (ngx_atomic_t *) (shared + 4 * cl);
  ngx_stat_requests = (ngx_atomic_t *) (shared + 5 * cl);
  ngx_stat_active = (ngx_atomic_t *) (shared + 6 * cl);
  ngx_stat_reading = (ngx_atomic_t *) (shared + 7 * cl);
  ngx_stat_writing = (ngx_atomic_t *) (shared + 8 * cl);
  ngx_stat_waiting = (ngx_atomic_t *) (shared + 9 * cl);

#endif

  return NGX_OK;
}

        ngx_event_module_init()方法主要完成的工作有如下幾個:

  • 獲取配置的timer_resolution屬性值,并將其賦值給ngx_timer_resolution屬性,這個屬性的作用主要是指定更新nginx緩存的時間的定時任務(wù)的執(zhí)行時間間隔;

  • 獲取nginx配置的文件描述符和當(dāng)前操作系統(tǒng)的文件描述符的配置,對比兩個值,從而更新當(dāng)前進(jìn)程所能開啟的文件描述符的個數(shù);

  • 聲明一塊共享內(nèi)存,用于存儲nginx進(jìn)行統(tǒng)計用的各個屬性的數(shù)據(jù)。

6. ngx_event_process_init()----初始化worker進(jìn)程

        ngx_event_process_init()方法主要是在worker進(jìn)程執(zhí)行主循環(huán)之前進(jìn)行初始化調(diào)用的,如下是該方法的源碼:

static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle) {
  ngx_uint_t m, i;
  ngx_event_t *rev, *wev;
  ngx_listening_t *ls;
  ngx_connection_t *c, *next, *old;
  ngx_core_conf_t *ccf;
  ngx_event_conf_t *ecf;
  ngx_event_module_t *module;

  // 獲取核心模塊的配置對象
  ccf = (ngx_core_conf_t *) ngx_get_conf(cycle->conf_ctx, ngx_core_module);
  // 獲取事件核心模塊的配置對象
  ecf = ngx_event_get_conf(cycle->conf_ctx, ngx_event_core_module);

  // 判斷當(dāng)前如果滿足三個條件,則標(biāo)記當(dāng)前為使用共享鎖的方式:
  // 1. 當(dāng)前為master-worker模式;
  // 2. 當(dāng)前worker進(jìn)程的數(shù)量大于1;
  // 3. 當(dāng)前打開了使用共享鎖的開關(guān);
  if (ccf->master && ccf->worker_processes > 1 && ecf->accept_mutex) {
    ngx_use_accept_mutex = 1;
    ngx_accept_mutex_held = 0;
    ngx_accept_mutex_delay = ecf->accept_mutex_delay;

  } else {
    // 如果不滿足上述條件,則指定不使用共享鎖
    ngx_use_accept_mutex = 0;
  }

#if (NGX_WIN32)

  /*
   * disable accept mutex on win32 as it may cause deadlock if
   * grabbed by a process which can't accept connections
   */

  ngx_use_accept_mutex = 0;

#endif

  // 這里這兩個隊列的主要作用在于,每個worker進(jìn)程在獲取到共享鎖之后,就會接收客戶端accept事件,
  // 然后將其放入到ngx_posted_accept_events隊列中,接著處理該隊列中的事件,并且將客戶端連接添加到
  // ngx_posted_events隊列中,然后再釋放鎖,也就是說獲取鎖的worker進(jìn)程只需要進(jìn)行accept客戶端連接,
  // 然后將鎖的權(quán)限交給其他的進(jìn)程,并且再自行處理接收到的連接的讀寫事件

  // 創(chuàng)建ngx_posted_accept_events隊列,該隊列用于接收客戶端的連接事件
  ngx_queue_init(&ngx_posted_accept_events);
  // 創(chuàng)建ngx_posted_events隊列,該隊列用于處理客戶端連接的讀寫事件
  ngx_queue_init(&ngx_posted_events);

  // 初始化一個用于存儲事件的紅黑樹
  if (ngx_event_timer_init(cycle->log) == NGX_ERROR) {
    return NGX_ERROR;
  }

  for (m = 0; cycle->modules[m]; m++) {
    if (cycle->modules[m]->type != NGX_EVENT_MODULE) {
      continue;
    }

    // ecf->use存儲了所選用的事件模型的模塊序號,這里是找到該模塊
    if (cycle->modules[m]->ctx_index != ecf->use) {
      continue;
    }

    // module即為所選用的事件模型對應(yīng)的模塊
    module = cycle->modules[m]->ctx;

    // 調(diào)用指定事件模型的初始化方法
    if (module->actions.init(cycle, ngx_timer_resolution) != NGX_OK) {
      /* fatal */
      exit(2);
    }

    break;
  }

#if !(NGX_WIN32)

  // ngx_timer_resolution表示發(fā)送更新時間事件的時間間隔
  // 這里表示如果設(shè)置了ngx_timer_resolution,并且沒有設(shè)置定時事件。
  // ngx_event_flags是在事件模塊的初始化中設(shè)置的,而且只有eventport和kqueue模型才會將
  // NGX_USE_TIMER_EVENT設(shè)置到ngx_event_flags中
  if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
    struct sigaction sa;
    struct itimerval itv;

    ngx_memzero(&sa, sizeof(struct sigaction));
    // 這里的sa主要是添加下面的SIGALRM的信號監(jiān)聽事件,該信號的作用是每隔一段時間就會向當(dāng)前進(jìn)程發(fā)出
    // 當(dāng)前進(jìn)程收到信號之后就會調(diào)用下面的ngx_timer_signal_handler()方法,該方法中會將
    // ngx_event_timer_alarm設(shè)置為1,而后當(dāng)前進(jìn)程在進(jìn)行事件循環(huán)的時候,判斷如果
    // ngx_event_timer_alarm為1,則會更新當(dāng)前進(jìn)程所緩存的時間數(shù)據(jù)
    sa.sa_handler = ngx_timer_signal_handler;
    sigemptyset(&sa.sa_mask);

    // 添加SIGALRM監(jiān)聽信號
    if (sigaction(SIGALRM, &sa, NULL) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "sigaction(SIGALRM) failed");
      return NGX_ERROR;
    }

    // 設(shè)置時間間隔相關(guān)參數(shù)
    itv.it_interval.tv_sec = ngx_timer_resolution / 1000;
    itv.it_interval.tv_usec = (ngx_timer_resolution % 1000) * 1000;
    itv.it_value.tv_sec = ngx_timer_resolution / 1000;
    itv.it_value.tv_usec = (ngx_timer_resolution % 1000) * 1000;

    // 按照指定的時間間隔設(shè)置定時器
    if (setitimer(ITIMER_REAL, &itv, NULL) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "setitimer() failed");
    }
  }

  // NGX_USE_FD_EVENT表示event filter沒有透明數(shù)據(jù),并需要一個文件描述符表,其主要用于poll、/dev/poll
  if (ngx_event_flags & NGX_USE_FD_EVENT) {
    struct rlimit rlmt;

    if (getrlimit(RLIMIT_NOFILE, &rlmt) == -1) {
      ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
                    "getrlimit(RLIMIT_NOFILE) failed");
      return NGX_ERROR;
    }

    // 這里主要是初始化最大個數(shù)的ngx_connection_t結(jié)構(gòu)體,將其保存在files數(shù)組中
    cycle->files_n = (ngx_uint_t) rlmt.rlim_cur;

    cycle->files = ngx_calloc(sizeof(ngx_connection_t *) * cycle->files_n, cycle->log);
    if (cycle->files == NULL) {
      return NGX_ERROR;
    }
  }

#else

  if (ngx_timer_resolution && !(ngx_event_flags & NGX_USE_TIMER_EVENT)) {
      ngx_log_error(NGX_LOG_WARN, cycle->log, 0,
                    "the \"timer_resolution\" directive is not supported "
                    "with the configured event method, ignored");
      ngx_timer_resolution = 0;
  }

#endif

  // 申請指定個數(shù)的ngx_connection_t數(shù)組,這里的connection_n對應(yīng)的是配置
  // 文件中的worker_connections所指定的大小
  cycle->connections = ngx_alloc(sizeof(ngx_connection_t) * cycle->connection_n, cycle->log);
  if (cycle->connections == NULL) {
    return NGX_ERROR;
  }

  c = cycle->connections;

  // 申請指定個數(shù)的ngx_event_t數(shù)組,其長度與connections數(shù)組一致,
  // 這樣便可以將connections數(shù)組與read_events數(shù)組進(jìn)行對應(yīng)
  cycle->read_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
  if (cycle->read_events == NULL) {
    return NGX_ERROR;
  }

  rev = cycle->read_events;
  for (i = 0; i < cycle->connection_n; i++) {
    rev[i].closed = 1;  // 初始狀態(tài)默認(rèn)讀事件都是closed狀態(tài)
    rev[i].instance = 1;  // 初始時初始化instance為1
  }

  // 申請指定個數(shù)的ngx_event_t數(shù)組,其長度與connections數(shù)組一致,
  // 這樣便可以將connections數(shù)組與write_events數(shù)組進(jìn)行對應(yīng)
  cycle->write_events = ngx_alloc(sizeof(ngx_event_t) * cycle->connection_n, cycle->log);
  if (cycle->write_events == NULL) {
    return NGX_ERROR;
  }

  wev = cycle->write_events;
  for (i = 0; i < cycle->connection_n; i++) {
    wev[i].closed = 1;  // 初始時寫事件默認(rèn)也都是closed狀態(tài)
  }

  i = cycle->connection_n;
  next = NULL;

  do {
    i--;

    // 將read_events和write_events數(shù)組的元素依次賦值到connections數(shù)組元素的read和write屬性中,
    // 并且將connections數(shù)組組裝成一個單鏈表
    c[i].data = next;
    c[i].read = &cycle->read_events[i];
    c[i].write = &cycle->write_events[i];
    c[i].fd = (ngx_socket_t) -1;

    next = &c[i];
  } while (i);

  // 初始狀態(tài)時,所有的connections都未被使用,因而需要存儲在free_connections鏈表中
  cycle->free_connections = next;
  cycle->free_connection_n = cycle->connection_n;

  /* for each listening socket */

  ls = cycle->listening.elts;
  for (i = 0; i < cycle->listening.nelts; i++) {

#if (NGX_HAVE_REUSEPORT)
    if (ls[i].reuseport && ls[i].worker != ngx_worker) {
      continue;
    }
#endif

    // 這里是為當(dāng)前所監(jiān)聽的每一個端口都綁定一個ngx_connection_t結(jié)構(gòu)體
    c = ngx_get_connection(ls[i].fd, cycle->log);

    if (c == NULL) {
      return NGX_ERROR;
    }

    c->type = ls[i].type;
    c->log = &ls[i].log;

    c->listening = &ls[i];
    ls[i].connection = c;

    rev = c->read;

    rev->log = c->log;
    // 標(biāo)記accept為1,表示當(dāng)前可以接收客戶端的連接事件
    rev->accept = 1;

#if (NGX_HAVE_DEFERRED_ACCEPT)
    rev->deferred_accept = ls[i].deferred_accept;
#endif

    if (!(ngx_event_flags & NGX_USE_IOCP_EVENT)) {
      if (ls[i].previous) {

        /*
         * delete the old accept events that were bound to
         * the old cycle read events array
         */

        // 刪除舊的事件
        old = ls[i].previous->connection;

        if (ngx_del_event(old->read, NGX_READ_EVENT, NGX_CLOSE_EVENT) == NGX_ERROR) {
          return NGX_ERROR;
        }

        old->fd = (ngx_socket_t) -1;
      }
    }

#if (NGX_WIN32)

    if (ngx_event_flags & NGX_USE_IOCP_EVENT) {
        ngx_iocp_conf_t  *iocpcf;

        rev->handler = ngx_event_acceptex;

        if (ngx_use_accept_mutex) {
            continue;
        }

        if (ngx_add_event(rev, 0, NGX_IOCP_ACCEPT) == NGX_ERROR) {
            return NGX_ERROR;
        }

        ls[i].log.handler = ngx_acceptex_log_error;

        iocpcf = ngx_event_get_conf(cycle->conf_ctx, ngx_iocp_module);
        if (ngx_event_post_acceptex(&ls[i], iocpcf->post_acceptex)
            == NGX_ERROR)
        {
            return NGX_ERROR;
        }

    } else {
        rev->handler = ngx_event_accept;

        if (ngx_use_accept_mutex) {
            continue;
        }

        if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }

#else

    // SOCK_STREAM表示TCP,一般都是TCP,也就是說在接收到客戶端的accept事件之后,
    // 就會調(diào)用ngx_event_accept()方法處理該事件
    rev->handler = (c->type == SOCK_STREAM) ? ngx_event_accept
                                            : ngx_event_recvmsg;

#if (NGX_HAVE_REUSEPORT)

    // 添加當(dāng)前事件到事件監(jiān)聽隊列中
    if (ls[i].reuseport) {
      if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
        return NGX_ERROR;
      }

      continue;
    }

#endif

    if (ngx_use_accept_mutex) {
      continue;
    }

#if (NGX_HAVE_EPOLLEXCLUSIVE)

    if ((ngx_event_flags & NGX_USE_EPOLL_EVENT)
        && ccf->worker_processes > 1)
    {
        if (ngx_add_event(rev, NGX_READ_EVENT, NGX_EXCLUSIVE_EVENT)
            == NGX_ERROR)
        {
            return NGX_ERROR;
        }

        continue;
    }

#endif

    // 添加當(dāng)前事件到事件監(jiān)聽隊列中
    if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) {
      return NGX_ERROR;
    }

#endif

  }

  return NGX_OK;
}

        這里ngx_event_process_init()方法主要完成了如下幾個工作:

  • 根據(jù)所使用的進(jìn)程模式和worker進(jìn)程數(shù)量,配置是否使用共享鎖的字段ngx_use_accept_mutex;

  • 初始化用于處理事件的ngx_posted_accept_events隊列和ngx_posted_events隊列;

  • 調(diào)用當(dāng)前所使用的事件模型模塊的init()方法,比如epoll模型在該init()方法中就會構(gòu)建一個epoll句柄,以便后續(xù)往其中添加需要監(jiān)聽的事件;

  • 判斷是否配置了ngx_timer_resolution屬性,也即上一節(jié)中獲取的更新nginx緩存時間的定時任務(wù)的執(zhí)行頻率字段,如果配置了,則創(chuàng)建一個定時任務(wù)以定時設(shè)置ngx_event_timer_alarm屬性值;

  • 創(chuàng)建長度相同的connectionsread_eventswrite_events數(shù)組,并且將connections數(shù)組中每個ngx_connection_t結(jié)構(gòu)體的read屬性指向read_events數(shù)組中對應(yīng)位置的讀事件結(jié)構(gòu)體,將write屬性指向write_events數(shù)組中對應(yīng)位置的寫事件結(jié)構(gòu)體,并且將所有的connections組織成單鏈表存儲到ngx_cycle_t的free_connections屬性中;

  • 為當(dāng)前nginx監(jiān)聽的各個端口配置一個ngx_connection_t結(jié)構(gòu)體,并且為其添加對應(yīng)的事件監(jiān)聽器,以等待客戶端連接的到來。

看完上述內(nèi)容,你們掌握nginx中怎么實(shí)現(xiàn)一個事件模塊的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!

當(dāng)前名稱:nginx中怎么實(shí)現(xiàn)一個事件模塊
URL地址:http://bm7419.com/article0/iihcoo.html

成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供建站公司、Google、微信公眾號、App設(shè)計、營銷型網(wǎng)站建設(shè)、網(wǎng)站改版

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

外貿(mào)網(wǎng)站建設(shè)