Nginx 이벤트 순환

13753 단어 nginx
이 글 은 Nginx 가 어떻게 시간 순환 을 하 는 지 설명 합 니 다. 먼저 이벤트 순환 의 출발점 은 감청 포트 에서 연결 을 얻 는 것 입 니 다. 우 리 는 ngxevent_core_module 모듈 의 ngxevent_process_init 함수 에서 다음 코드 를 볼 수 있 습 니 다:
    /* for each listening socket */
	/*         connection         ,   slot*/	
    ls = cycle->listening.elts;   //       master         ,       
    for (i = 0; i < cycle->listening.nelts; i++) {
	//                  connection,     c           connection
        c = ngx_get_connection(ls[i].fd, cycle->log);   

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

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

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

        rev = c->read;   //rev    connection    

        rev->log = c->log;
        rev->accept = 1;    //              accept  ,    epoll                accept  

#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;
            }
        }



		/*               ngx_event_accept*/	

        rev->handler = ngx_event_accept;   //                 socket

이 부분 코드 는 워 커 프로 세 스에 서 모든 listening 에 하나의 connection 을 할당 하고 이 connection 의 읽 기 이벤트 처리 함 수 를 ngx 로 설정 합 니 다.event_accept 함수, 즉 이 함수 로 listening 의 accept 를 처리 합 니 다. 자, 이제 이 함수 부터 봅 시다. (이 함 수 는 Ngx event accept. c 로 정 의 됩 니 다.)
lc = ev->data;  //        connection
    ls = lc->listening;
    ev->ready = 0;
	
//     epoll     ,          ,            
    do {
        socklen = NGX_SOCKADDRLEN;

//  accept        socket
        s = accept(lc->fd, (struct sockaddr *) sa, &socklen);


위의 코드 는 먼저 이벤트 변수 에서 이 이벤트 에 대응 하 는 connection 을 가 져 오고, 이어서 accept 함 수 를 호출 하여 감청 하 는 socket 설명 부호 에서 연결 을 가 져 올 수 있 습 니 다.
		/*accept        ,     ngx_accept_disabled   
			   ngx_accept_disabled      ,            。 
				
			     ,             “         ,   
			         ”。                  ,     
			             。        ,    :    accept 
			         7/8 ,ngx_accept_disabled   0 ,     
			      。 
			  */  

        ngx_accept_disabled = ngx_cycle->connection_n / 8
                              - ngx_cycle->free_connection_n;
//      socket  connection
        c = ngx_get_connection(s, ev->log);

상기 코드 는 연결 을 가 져 온 후 ngx 를 계산 하 는 데 사 용 됩 니 다.accept_disabled 의 값 은 워 커 프로 세 스 간 부하 균형 을 이 루 는 데 사 용 됩 니 다. 워 커 프로 세 스 가 너무 많은 connection 을 가지 고 있 지 않도록 합 니 다. 구체 적 인 것 은 나중에 말씀 드 리 겠 습 니 다.그 다음 에 연 결 된 socket 설명자 에 connection 을 분배 하 는 것 입 니 다. 다음 코드 는 방금 분 배 된 connection 을 초기 화 하 는 것 입 니 다. 예 를 들 어 메모리 풀 을 분배 하고 socket 설명 자 를 비 차단 으로 설정 하 는 등 입 니 다.
//           
        if (ngx_add_conn && (ngx_event_flags & NGX_USE_EPOLL_EVENT) == 0) {
            if (ngx_add_conn(c) == NGX_ERROR) {
                ngx_close_accepted_connection(c);
                return;
            }
        }

        log->data = NULL;
        log->handler = NULL;

		/*   listen handler   ,                
			   accept       epoll ;    handler     
			  ngx_http_init_connection(  src/http/ngx_http_request.c ); 
			        http        。 
		  */  
        ls->handler(c);   

위의 이 부분 코드 는 비교적 중요 합 니 다. 그것 은 처음으로 ngx 를 호출 합 니 다.add_conn 함수 가 방금 연결 한 것 을 epoll 에 넣 었 습 니 다. 우 리 는 ngx 를 볼 수 있 습 니 다.add_conn 의 정의, Ngxevent. h 중:
#define ngx_add_conn         ngx_event_actions.add_conn

사실 여기 앞 글 을 보시 면 ngxadd_conn 은 솔직히 실제 이벤트 모듈 을 호출 하 는 addconn 함수, 실제 epoll 모듈 을 사용 하면 epoll 모듈 의 ngx 를 호출 합 니 다.epoll_add_connection 함수, 다음 코드 가 있 습 니 다:
        ls->handler(c);   

여 기 는 listening 의 handler 로 방금 분 배 된 connection 을 처리 하 는 것 입 니 다. http 부분 과 관련 된 것 이 니 나중에 다시 이야기 하 겠 습 니 다.
자, 여기까지 ngxevent_accept 함수 가 말 하 는 것 은 많 지 않다.이제 본 격 적 으로 Nginx 의 이벤트 순환 에 들 어 갈 수 있 습 니 다.이벤트 순환 의 입 구 를 먼저 봅 시다. 워 커 프로 세 스 의 실행 함수 ngxworker_process_cycle 에 서 는 순환 할 때마다 사용 되 는 코드 가 있 습 니 다.
//       ,                 
        ngx_process_events_and_timers(cycle);

응, ngxprocess_events_and_timers 함 수 는 이벤트 순환 의 입구 함수 로 Ngx 에 정의 되 어 있 습 니 다.이벤트. c 에서 이 함 수 를 분석 하 겠 습 니 다.
    /*ngx_use_accept_mutex        accept    
          ,accept_mutex off;    。 
     accept mutex         ,        。 
     */  
    if (ngx_use_accept_mutex) {
        if (ngx_accept_disabled > 0) {
            ngx_accept_disabled--;

        } else {
         /* ngx_accept_disabled  0,      */  
              
            /*   accept mutex,          ,   listen 
                   epoll 。  ,              
                   ,        epoll_wait ,        。 
            */  
            //   ngx_trylock_accept_mutex   ,         ,              worker   epoll  
            if (ngx_trylock_accept_mutex(cycle) == NGX_ERROR) {
                return;
            }
			  /*      ,     NGX_POST_EVENTS  , 
                                        , 
                       ,        。  ,       
                      ,            ,      
                        ,           ,  accept 
                        。 
                */  
            if (ngx_accept_mutex_held) {
                flags |= NGX_POST_EVENTS;  //    ,        

            } else {
             
                /*        ,     NGX_POST_EVENTS   。 
                             ,      。 
                */  
                if (timer == NGX_TIMER_INFINITE
                    || timer > ngx_accept_mutex_delay)
                {
                    timer = ngx_accept_mutex_delay;
                }
            }
        }
    }

우선 ngx 사용 여 부 를 판단 합 니 다.use_accept_mutex 신 호 량, 이 신 호 량 은 놀 라 움 을 피 하 는 데 사용 된다.현재 worker 프로 세 스 가 이 신 호 량 을 가 져 와 야 listening 을 자신의 epoll 에 진정 으로 추가 할 수 있 습 니 다. 해당 accept 이벤트 입 니 다.여기 도 위 에서 언급 한 ngx 를 보 았 습 니 다.accept_disabled 가 사용 할 수 있 는 역할 입 니 다. 그 는 listening 을 현재 worker 프로 세 스 의 epoll 에 추가 할 지 여 부 를 판단 하 는 데 도 사 용 됩 니 다. 이렇게 하면 부하 균형 을 이 룰 수 있 고 하나의 worker 프로 세 스 가 너무 많은 connection 을 가지 고 있 는 것 을 피 할 수 있 습 니 다.
	/*epoll  wait   ,ngx_process_events          
		 epoll    ngx_epoll_process_events  。    epoll 
		      ,     。 
	   */  
    (void) ngx_process_events(cycle, timer, flags);

이 코드 는 실제 이벤트 모듈 의 process 를 직접 호출 합 니 다.이벤트 함수, 이 벤트 를 처리 하 는 것 은 그 정 의 를 보 는 것 이 좋 습 니 다.
#define ngx_process_events   ngx_event_actions.process_events

응, 딱 봐 도 알 겠 어. epoll 모듈 을 사용 하면 그 ngx 를 호출 할 거 야.epoll_process_이벤트 함수.이 따 얘 기 하 자.
       if (ngx_posted_accept_events) {
			 /*ngx_posted_accept_events        
		        epoll      wait  accept  。 
		           NGX_POST_EVENTS      ,    
		         accept         。 
		       
		               accept     ,       
		      ngx_event_accept           ,     
		      epoll 。 
		    */  
        ngx_event_process_posted(cycle, &ngx_posted_accept_events);
    }


	/*  accept      ,       ,      。 
	            。 
	 */  
    if (ngx_accept_mutex_held) {
        ngx_shmtx_unlock(&ngx_accept_mutex);
    }

이 부분 코드 는 listening 감청 에서 accept 이 벤트 를 가 져 왔 는 지 판단 합 니 다. 있 으 면 빨리 처리 해 야 합 니 다. 현재 워 커 프로 세 스 가 ngx 를 차지 하고 있 음 을 설명 하기 때 문 입 니 다.accept_mutex 신 호 량, accept 사건 을 처리 한 후 이 신 호 량 을 빨리 방출 하여 다른 worker 프로 세 스 가 이 자 물 쇠 를 가 져 올 수 있 도록 한 다음 listening 에서 연결 을 가 져 옵 니 다.
	/*      (          )        , 
		           handler  ,         
		        handler 。 
	  */  
    if (ngx_posted_events) {
        if (ngx_threaded) {
            ngx_wakeup_worker_thread(cycle);

        } else {
            ngx_event_process_posted(cycle, &ngx_posted_events);
        }
    }

이 부분 은 일반적인 사건 을 처리 하 는 데 쓰 인 다.이렇게 ngxprocess_events_and_timers 함수 에서 사건 을 처리 하 는 부분 은 다 말 했 지만 이 함 수 는 시간 을 처리 하 는 부분 도 있 습 니 다. 이것 은 나중에 Nginx 의 시간 함수 처 리 를 말 할 때 다시 이야기 하 겠 습 니 다.
자, 이제 분석 감 이 방금 언급 한 epoll 모듈 의 ngxepoll_process_이벤트 함수.
//   epoll wait,        event_list  ,       nevents
    /*         ,       timer;nginx    
                  。     timer    。 
    */  
    events = epoll_wait(ep, event_list, (int) nevents, timer);    //                ,       ,      epoll wait           ,             

우선 epoll 호출wait 함수 가 epoll 에서 발생 한 사건 을 가 져 온 다음 이 사건 들 을 옮 겨 다 닐 수 있 습 니 다:
//           
    for (i = 0; i < events; i++) {
        c = event_list[i].data.ptr;  //          connection

//instance            
        instance = (uintptr_t) c & 1;
        c = (ngx_connection_t *) ((uintptr_t) c & (uintptr_t) ~1);

        rev = c->read;

        if (c->fd == -1 || rev->instance != instance) {

            /*
             * the stale event from a file descriptor
             * that was just closed in this iteration
             */

            ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll: stale event %p", c);
            continue;
        }
    //          
        revents = event_list[i].events;

        ngx_log_debug3(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                       "epoll: fd:%d ev:%04XD d:%p",
                       c->fd, revents, event_list[i].data.ptr);
//         
        if (revents & (EPOLLERR|EPOLLHUP)) {
            ngx_log_debug2(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                           "epoll_wait() error on fd:%d ev:%04XD",
                           c->fd, revents);
        }

#if 0
        if (revents & ~(EPOLLIN|EPOLLOUT|EPOLLERR|EPOLLHUP)) {
            ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
                          "strange epoll_wait() events fd:%d ev:%04XD",
                          c->fd, revents);
        }
#endif

        if ((revents & (EPOLLERR|EPOLLHUP))
             && (revents & (EPOLLIN|EPOLLOUT)) == 0)
        {
            /*
             * if the error events were returned without EPOLLIN or EPOLLOUT,
             * then add these flags to handle the events at least in one
             * active handler
             */

            revents |= EPOLLIN|EPOLLOUT;
        }
		/*         ,            active */  
        if ((revents & EPOLLIN) && rev->active) {

            if ((flags & NGX_POST_THREAD_EVENTS) && !rev->accept) {
                rev->posted_ready = 1;

            } else {
                rev->ready = 1;
            }

            if (flags & NGX_POST_EVENTS) {
//     NGX_POST_EVENTS,    worker        ,          ,          accept  ,          accept    ,   event accept    1 ,         worker          
//                    ,                   ,       worker          
                queue = (ngx_event_t **) (rev->accept ?
                               &ngx_posted_accept_events : &ngx_posted_events); 

                ngx_locked_post_event(rev, queue);

            } else {
                rev->handler(rev);
            }
        }

        wev = c->write;
//      ,    connection        
        if ((revents & EPOLLOUT) && wev->active) {

            if (c->fd == -1 || wev->instance != instance) {

                /*
                 * the stale event from a file descriptor
                 * that was just closed in this iteration
                 */

                ngx_log_debug1(NGX_LOG_DEBUG_EVENT, cycle->log, 0,
                               "epoll: stale event %p", c);
                continue;
            }

            if (flags & NGX_POST_THREAD_EVENTS) {
                wev->posted_ready = 1;

            } else {
                wev->ready = 1;
            }

            if (flags & NGX_POST_EVENTS) {
                ngx_locked_post_event(wev, &ngx_posted_events);

            } else {
                wev->handler(wev);
            }
        }
    }

위의 코드 는 사실 설명 이 명확 합 니 다. 모든 사건 을 반복 해서 옮 겨 다 니 며 이 사건 의 유형 을 판단 하고 이 사건 의 실제 소속 connection 을 가 져 옵 니 다. 이 벤트 를 읽 는 경우 이 connection 의 read 이 벤트 를 꺼 내 read 의 handler 로 처리 합 니 다. 이 벤트 를 쓰 는 경우 이 connection 의 write 를 가 져 옵 니 다.그리고 write 의 handler 로 처리 합 니 다.하지만 여기 서 주의해 야 할 것 은..
  if (flags & NGX_POST_EVENTS) {
//     NGX_POST_EVENTS,    worker        ,          ,          accept  ,          accept    ,   event accept    1 ,         worker          
//                    ,                   ,       worker          
                queue = (ngx_event_t **) (rev->accept ?
                               &ngx_posted_accept_events : &ngx_posted_events); 

                ngx_locked_post_event(rev, queue);

            } else {
                rev->handler(rev);
            }

이 세그먼트 코드 는 신 호 량 을 가지 고 있 는 지 여 부 를 판단 합 니 다. 앞에서 말 했 듯 이 가지 고 있다 면 이 사건 들 을 대기 열 에 넣 고 잠시 후에 처리 해 야 합 니 다. 여 기 는 신 호 량 을 빨리 방출 할 수 있 도록 이 사건 의 유형 을 판단 하기 위해 accept 사건 인지 일반적인 읽 기 사건 인지 구분 하여 서로 다른 대기 열 에 넣 어야 합 니 다. 음,이벤트 의 accept 도 메 인 은 이전에 이미 말 했 듯 이 이 판단 을 위 한 것 입 니 다.
자, ngxepoll_process_이벤트 함수 도 기본적으로 끝 났 으 니 이벤트 순환 도 많 지 않 습 니 다.

좋은 웹페이지 즐겨찾기