nginx 소스 코드 분석 스 레 드 탱크 상세 설명

11324 단어
nginx 소스 코드 분석 스 레 드 탱크 상세 설명
머리말
     nginx 는 다 중 프로 세 스 모델 을 사용 합 니 다. master 와 worker 는 주로 pipe 파 이 프 를 통 해 통신 을 합 니 다. 다 중 프로 세 스 의 장점 은 각 프로 세 스 가 서로 영향 을 주지 않 는 다 는 것 입 니 다.하지만 nginx 는 왜 다 중 스 레 드 모델 을 사용 하지 않 느 냐 는 질문 이 자주 나온다.사실 nginx 코드 에 thread 를 제공 합 니 다.pool (라인 풀) 의 핵심 모듈 로 다 중 작업 을 처리 합 니 다.다음은 본인 이 이 threadpool 이 모듈 의 이 해 를 통 해 여러분 과 공유 하 겠 습 니 다. 
2. threadpool 스 레 드 탱크 모듈 소개
     nginx 의 주요 기능 은 하나의 모듈 로 구성 되 어 있 습 니 다. threadpool 도 예 외 는 아니다.스 레 드 탱크 는 주로 파일 을 읽 고 보 내 는 등 IO 작업 에 사용 되 며 느 린 속도 의 IO 가 worker 의 정상 적 인 운행 에 영향 을 주지 않도록 합 니 다.먼저 공식 설정 예 시 를 참조 하 십시오.

Syntax: thread_pool name threads=number [max_queue=number];
Default: thread_pool default threads=32 max_queue=65536;
Context: main

     위 설정 설명 에 따 르 면 threadpool 은 이름 이 있 습 니 다. 위의 스 레 드 수 와 대기 열 크기 는 모든 워 커 프로 세 스 의 스 레 드 를 말 합 니 다. 모든 워 커 의 스 레 드 총수 가 아 닙 니 다.하나의 스 레 드 탱크 에 있 는 모든 스 레 드 가 하나의 대기 열 을 공유 합 니 다. 대기 열의 최대 인원 수 는 위 에서 정의 한 max 입 니 다.quue, 대기 열 이 가득 차 면 대기 열 에 작업 을 추가 하면 오류 가 발생 합 니 다. 
     앞서 말 한 모듈 에 따라 프로 세 스 초기 화 (master 가 worker 를 시작 하기 전에) createconf--> command_set 함수 -- > initconf, 다음은 이 절차 에 따라 threadpool 모듈 초기 화

/******************* nginx/src/core/ngx_thread_pool.c ************************/
//            
static void * ngx_thread_pool_create_conf(ngx_cycle_t *cycle)
{
  ngx_thread_pool_conf_t *tcf;
   // cycle->pool             
  tcf = ngx_pcalloc(cycle->pool, sizeof(ngx_thread_pool_conf_t));
  if (tcf == NULL) {
    return NULL;
  }
   
   //     4 ngx_thread_pool_t         
   //ngx_thread_pool_t                 
  if (ngx_array_init(&tcf->pools, cycle->pool, 4,
            sizeof(ngx_thread_pool_t *))
    != NGX_OK)
  {
    return NULL;
  }
 
  return tcf;
}
 
//         thread_pool   ,         ngx_thread_pool_t 
static char * ngx_thread_pool(ngx_conf_t *cf, ngx_command_t *cmd, void *conf)
{
  ngx_str_t     *value;
  ngx_uint_t     i;
  ngx_thread_pool_t *tp;
 
  value = cf->args->elts;
 
  //  thread_pool    name          (    ,       )
  //  ngx_thread_pool_t            
  //    ,nginx      name      
  tp = ngx_thread_pool_add(cf, &value[1]);
  .......
  //  thread_pool        
  for (i = 2; i < cf->args->nelts; i++) {
    //        
    if (ngx_strncmp(value[i].data, "threads=", 8) == 0) {
     .......
    }
     
    //           
    if (ngx_strncmp(value[i].data, "max_queue=", 10) == 0) {
     .......
    }
  }
  ......
}
 
//                          
static char * ngx_thread_pool_init_conf(ngx_cycle_t *cycle, void *conf)
{
  ....
  ngx_thread_pool_t **tpp;
 
  tpp = tcf->pools.elts;
  //             ,       
  for (i = 0; i < tcf->pools.nelts; i++) {
    .....
  }
 
  return NGX_CONF_OK;
}

     상기 프로 세 스 가 끝 난 후에 nginx 의 master 는 모든 스 레 드 탱크 의 설정 (tcf - > pools) 을 저장 합 니 다. 이 설정 은 worker 를 만 들 때 도 계 승 됩 니 다.그리고 모든 worker 에서 각 핵심 모듈 의 init 를 호출 합 니 다.process 함수.

/******************* nginx/src/core/ngx_thread_pool.c ************************/
//            
static ngx_int_t
ngx_thread_pool_init_worker(ngx_cycle_t *cycle)
{
  ngx_uint_t        i;
  ngx_thread_pool_t    **tpp;
  ngx_thread_pool_conf_t  *tcf;
  //    worker      worker       
  if (ngx_process != NGX_PROCESS_WORKER
    && ngx_process != NGX_PROCESS_SINGLE)
  {
    return NGX_OK;
  }
   
  //       
  ngx_thread_pool_queue_init(&ngx_thread_pool_done);
 
  tpp = tcf->pools.elts;
  for (i = 0; i < tcf->pools.nelts; i++) {
    //        
    if (ngx_thread_pool_init(tpp[i], cycle->log, cycle->pool) != NGX_OK) {
      return NGX_ERROR;
    }
  }
 
  return NGX_OK;
}
 
//      
static ngx_int_t ngx_thread_pool_init(ngx_thread_pool_t *tp, ngx_log_t *log, ngx_pool_t *pool)
{
  .....
  //       
  ngx_thread_pool_queue_init(&tp->queue);
 
  //     
  if (ngx_thread_mutex_create(&tp->mtx, log) != NGX_OK) {
    return NGX_ERROR;
  }
 
  //        
  if (ngx_thread_cond_create(&tp->cond, log) != NGX_OK) {
    (void) ngx_thread_mutex_destroy(&tp->mtx, log);
    return NGX_ERROR;
  }
  ......
  for (n = 0; n < tp->threads; n++) {
    //           
    err = pthread_create(&tid, &attr, ngx_thread_pool_cycle, tp);
    if (err) {
      ngx_log_error(NGX_LOG_ALERT, log, err,
             "pthread_create() failed");
      return NGX_ERROR;
    }
  }
  ......
}
 
//           
static void *ngx_thread_pool_cycle(void *data)
{
   ......
   for ( ;; ) {
    //          
    if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
      return NULL;
    }
 
    /* the number may become negative */
    tp->waiting--;
 
    //        , cond_wait           cond_signal/broadcast  
    while (tp->queue.first == NULL) {
      if (ngx_thread_cond_wait(&tp->cond, &tp->mtx, tp->log)
        != NGX_OK)
      {
        (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
        return NULL;
      }
    }
    //        task,         
    task = tp->queue.first;
    tp->queue.first = task->next;
 
    if (tp->queue.first == NULL) {
      tp->queue.last = &tp->queue.first;
    }
 
    if (ngx_thread_mutex_unlock(&tp->mtx, tp->log) != NGX_OK) {
      return NULL;
    }
    ......
    //task     
    task->handler(task->ctx, tp->log);
    .....
 
    ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);
 
    //            done       event         
    *ngx_thread_pool_done.last = task;
    ngx_thread_pool_done.last = &task->next;
     
    //       ,                      
    ngx_memory_barrier();
 
    ngx_unlock(&ngx_thread_pool_done_lock);
     
    (void) ngx_notify(ngx_thread_pool_handler);
  }
}
 
//  pool_done   task      event  
static void ngx_thread_pool_handler(ngx_event_t *ev)
{
  .....
  ngx_spinlock(&ngx_thread_pool_done_lock, 1, 2048);
 
  //         
  task = ngx_thread_pool_done.first;
  ngx_thread_pool_done.first = NULL;
  ngx_thread_pool_done.last = &ngx_thread_pool_done.first;
 
  ngx_memory_barrier();
 
  ngx_unlock(&ngx_thread_pool_done_lock);
 
  while (task) {
    ngx_log_debug1(NGX_LOG_DEBUG_CORE, ev->log, 0,
            "run completion handler for task #%ui", task->id);
    //            
    event = &task->event;
    task = task->next;
 
    event->complete = 1;
    event->active = 0;
 
    //  event                
    event->handler(event);
  }
}
 

3. threadpool 스 레 드 탱크 사용 예시
     앞에서 말 한 바 와 같이 nginx 의 스 레 드 탱크 는 주로 파일 을 조작 하 는 IO 작업 에 사 용 됩 니 다.그래서 nginx 에서 자체 적 으로 가지 고 있 는 모듈 ngxhttp_file_cache. c 파일 에서 스 레 드 탱크 의 사용 을 보 았 습 니 다.

/*********************** nginx/src/os/unix/ngx_files.c **********************/
//file_cache       (       )
static ssize_t ngx_http_file_cache_aio_read(ngx_http_request_t *r, ngx_http_cache_t *c)
{
  .......
#if (NGX_THREADS)
 
  if (clcf->aio == NGX_HTTP_AIO_THREADS) {
    c->file.thread_task = c->thread_task;
    //              ngx_thread_read      
    c->file.thread_handler = ngx_http_cache_thread_handler;
    c->file.thread_ctx = r;
    //       ,        ,    task             
    n = ngx_thread_read(&c->file, c->buf->pos, c->body_start, 0, r->pool);
 
    c->thread_task = c->file.thread_task;
    c->reading = (n == NGX_AGAIN);
 
    return n;
  }
#endif
 
  return ngx_read_file(&c->file, c->buf->pos, c->body_start, 0);
}
 
 
//task       
static ngx_int_t ngx_http_cache_thread_handler(ngx_thread_task_t *task, ngx_file_t *file)
{
  .......
  tp = clcf->thread_pool;
  .......
   
  task->event.data = r;
  //  thread_event_handler  ,      pool_done   event      
  task->event.handler = ngx_http_cache_thread_event_handler;
 
  //              
  if (ngx_thread_task_post(tp, task) != NGX_OK) {
    return NGX_ERROR;
  }
  ......
}
 
/*********************** nginx/src/core/ngx_thread_pool.c **********************/
//        
ngx_int_t ngx_thread_task_post(ngx_thread_pool_t *tp, ngx_thread_task_t *task)
{
  //              
  if (task->event.active) {
    ngx_log_error(NGX_LOG_ALERT, tp->log, 0,
           "task #%ui already active", task->id);
    return NGX_ERROR;
  }
 
  if (ngx_thread_mutex_lock(&tp->mtx, tp->log) != NGX_OK) {
    return NGX_ERROR;
  }
   
  //                        
  if (tp->waiting >= tp->max_queue) {
    (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
 
    ngx_log_error(NGX_LOG_ERR, tp->log, 0,
           "thread pool \"%V\" queue overflow: %i tasks waiting",
           &tp->name, tp->waiting);
    return NGX_ERROR;
  }
  //    
  task->event.active = 1;
 
  task->id = ngx_thread_pool_task_id++;
  task->next = NULL;
   
  //             ,      
  if (ngx_thread_cond_signal(&tp->cond, tp->log) != NGX_OK) {
    (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
    return NGX_ERROR;
  }
 
  *tp->queue.last = task;
  tp->queue.last = &task->next;
 
  tp->waiting++;
 
  (void) ngx_thread_mutex_unlock(&tp->mtx, tp->log);
 
  ngx_log_debug2(NGX_LOG_DEBUG_CORE, tp->log, 0,
          "task #%ui added to thread pool \"%V\"",
          task->id, &tp->name);
 
  return NGX_OK;
}

    위의 예 는 nginx 가 현재 스 레 드 탱크 에 대한 사용 방법 을 기본적으로 보 여 주 었 고 스 레 드 탱크 로 IO 와 같은 느 린 속도 조작 을 처리 하면 worker 의 메 인 스 레 드 의 집행 효율 을 향상 시 킬 수 있다.물론 사용자 스스로 모듈 을 개발 할 때 file 를 참조 할 수 있 습 니 다.cache 모듈 에 서 는 스 레 드 탱크 의 방법 을 사용 하여 다 중 스 레 드 를 호출 하여 프로그램의 성능 을 향상 시 킵 니 다.(많은 비판 과 지적 을 환영 합 니 다)
읽 어 주 셔 서 감사합니다. 여러분 에 게 도움 이 되 기 를 바 랍 니 다. 본 사이트 에 대한 여러분 의 지지 에 감 사 드 립 니 다!

좋은 웹페이지 즐겨찾기