nginx 이벤트 모듈 의 클 라 이언 트 연결 및 시간 초과 관리

이전 글 은 nginx 가 감청 사건 을 어떻게 관리 하 는 지 분석 하고 감청 사건 을 epoll 사건 관리자 에 등록 했다.다음은 이 를 바탕 으로 클 라 이언 트 연결 요청 이 왔 을 때 nginx 가 클 라 이언 트 와 tcp 연결 을 어떻게 하 는 지, 그리고 연결 이 만들어 진 후에 시간 초과 사건 을 어떻게 관리 하 는 지 분석 합 니 다.
연결 이벤트 관리
        함수 ngxevent_process_init 에 서 는 읽 기 이벤트 의 반전 을 ngx 로 설정 합 니 다.event_accept。 이렇게 설정 하면 nginx 서버 에서 클 라 이언 트 의 연결 요청 을 감청 한 후 이 리 셋 이 실 행 됩 니 다. 클 라 이언 트 와 tcp 연결 을 만 드 는 데 사 용 됩 니 다.연결 이 구축 되면 클 라 이언 트 와 데이터 상호작용 을 정상적으로 할 수 있 습 니 다.
//ngx_event_core_module   init_process  。   ngx_worker_process_init    
static ngx_int_t ngx_event_process_init(ngx_cycle_t *cycle)
{
	//         ,             (      ,        ,
	//    , 、         ),            
    ls = cycle->listening.elts;
    for (i = 0; i < cycle->listening.nelts; i++) 
	{
        c = ngx_get_connection(ls[i].fd, cycle->log);
        //              
		c->listening = &ls[i];
		//              
        ls[i].connection = c;
        rev = c->read;

		//      ,        ,     
        rev->handler = ngx_event_accept;
		
		//  work          ,       epoll 
		//         NULL,   ngx_get_connection            0  
		if (ngx_add_event(rev, NGX_READ_EVENT, 0) == NGX_ERROR) 
		{
			return NGX_ERROR;
		}
}

        ngx_event_accept 는 클 라 이언 트 로부터 연결 요청 을 받 는 데 사 용 됩 니 다.tcp 가 만들어 진 후 연결 풀 에서 새 연결 대상 을 가 져 오고 읽 기, 쓰기 이 벤트 를 가 져 오 며 읽 기 이 벤트 를 epoll 에 추가 합 니 다.이 새로운 연결 대상 과 감청 대상 의 역할 은 다르다.감청 대상 은 클 라 이언 트 의 연결 을 감청 하 는 데 사 용 됩 니 다. 클 라 이언 트 와 연결 하기 전에 호출 되 지 않 았 습 니 다.이 새 연결 대상 은 tcp 연결 후 호출 되 어 클 라 이언 트 와 데이터 읽 기와 쓰기 에 사 용 됩 니 다.
//         
void ngx_event_accept(ngx_event_t *ev)
{
    do 
	{
		//       
        s = accept(lc->fd, (struct sockaddr *) sa, &socklen);

		//work        ,   work                7/8 ,
		//  work                。     tcp          ,        
        ngx_accept_disabled = ngx_cycle->connection_n / 8
                              - ngx_cycle->free_connection_n;

		//          (       ,   )
        c = ngx_get_connection(s, ev->log);

		//        
        c->pool = ngx_create_pool(ls->pool_size, ev->log);
        c->sockaddr = ngx_palloc(c->pool, socklen);
        ngx_memcpy(c->sockaddr, sa, socklen);

		//         ,          。         ngx_os_io       
		//                ,         。          ,        。
		//                    ,           ,      ,            
		//              
        c->recv = ngx_recv;
        c->send = ngx_send;
        c->recv_chain = ngx_recv_chain;
        c->send_chain = ngx_send_chain;
		
		//             ls, ls              accept     ,
		//          
        c->listening = ls;

		//         ,        epoll
		//                    ,        。
		//     5            socket,     5     。             
		//    ,  4      ,      。             ,              。
        ls->handler(c);		//ngx_http_init_connection

    } while (ev->available);
}

        함수 에서 ngx 호출listening_s 대상 의 handler 방법.이 방법 은 사실 ngxhttp_init_connection, ngxhttp_add_listening 함수 에 설정 되 어 있 습 니 다.
ngx_http_init_listening
   ---> ngx_http_add_listening
           --->
//    ngx_listening_t  ,         。        
ngx_listening_t * ngx_http_add_listening(ngx_conf_t *cf, ngx_http_conf_addr_t *addr)
{
	//    ngx_listening_t  
    ls = ngx_create_listening(cf, &addr->opt.u.sockaddr, addr->opt.socklen);
	
	//    
    ls->handler = ngx_http_init_connection;
}

        ngx_http_init_connection 은 tcp 가 연결 을 만 든 후 http 요청 초기 화 전 단계 입 니 다. 새로 만 든 클 라 이언 트 연결 등록 읽 기 이벤트 에 ngxhttp_init_request, 이벤트 리 셋 ngxhttp_empty_handler, 동시에 읽 기 이벤트 의 시간 초과 이 벤트 를 레 드 블랙 트 리 가 구현 하 는 타이머 에 등록 합 니 다.마지막 으로 읽 기 이 벤트 를 epoll 에 넣 습 니 다.이 작업 이 실 행 된 후에 클 라 이언 트 로부터 데 이 터 를 받 을 수 있 습 니 다.
//         ,ngx_event_accept      ngx_listening_t handler,      
//  :            
void ngx_http_init_connection(ngx_connection_t *c)
{
    rev = c->read;
	//     
    rev->handler = ngx_http_init_request;				

	//           ,                    
    c->write->handler = ngx_http_empty_handler;			

	//           ,        ,post_accept_timeout    
	// nginx.conf  client_header_timeout  
    ngx_add_timer(rev, c->listening->post_accept_timeout);

	//       epoll ,            epoll ,                  ,           
    ngx_handle_read_event(rev, 0);
}

        nginx 서버 는 클 라 이언 트 의 연결 요청 을 처리 한 후 워 크 프로 세 스 의 이벤트 순환 으로 돌 아 왔 습 니 다.새로 만 든 대상 을 감청 하고 클 라 이언 트 가 보 낸 데 이 터 를 기다 리 며 클 라 이언 트 와 데이터 상호작용 을 합 니 다.
//work       
static void ngx_worker_process_cycle(ngx_cycle_t *cycle, void *data)
{
    for ( ;; ) 
	{
        ngx_process_events_and_timers(cycle);
    }
}

2. 시간 초과 사건 관리
        nginx 서버 는 클 라 이언 트 의 연결 요청 을 감청 한 후 클 라 이언 트 와 tcp 연결 을 만 들 고 이 새로운 연결 을 위해 읽 기와 쓰기 이 벤트 를 등록 하 며 읽 기 이벤트 의 시간 초과 이 벤트 를 빨간색 과 검은색 트 리 가 실현 하 는 타이머 에 추가 합 니 다.위 에서 의 ngxhttp_init_connection 함수 에서 이 동작 을 볼 수 있 고 읽 기 이 벤트 를 빨간색 과 검은색 트 리 가 실 현 된 타이머 에 추가 할 수 있 습 니 다.
//         ,ngx_event_accept      ngx_listening_t handler,      
//  :            
void ngx_http_init_connection(ngx_connection_t *c)
{
    rev = c->read;
	//     
    rev->handler = ngx_http_init_request;				

	//           ,                    
    c->write->handler = ngx_http_empty_handler;			

	//           ,        ,post_accept_timeout    
	// nginx.conf  client_header_timeout  
    ngx_add_timer(rev, c->listening->post_accept_timeout);

	//       epoll 
    ngx_handle_read_event(rev, 0);
}

    ngx_event_add_timer 는 이 벤트 를 붉 은 검 은 나무 가 실현 하 는 타이머 에 등록 하 는 것 을 책임 집 니 다.붉 은 검 은 나무 에 있 는 모든 시간 초과 사건 노드 는 ngx 를 통 해event_s 대상 의 timer 멤버 가 연결 되 어 있 습 니 다.타이머 에 있 는 모든 시간 초과 이벤트 노드 의 key 는 시간 초과 로 이 사건 의 시간 초과 시간 을 기록 합 니 다.
//            ,timer     
static ngx_inline void ngx_event_add_timer(ngx_event_t *ev, ngx_msec_t timer)
{
    ngx_msec_t      key;
    ngx_msec_int_t  diff;

    key = ngx_current_msec + timer;

	//            ,         
    if (ev->timer_set) 
	{
        ngx_del_timer(ev);
    }

	//        id,     
    ev->timer.key = key;


	//       
    ngx_rbtree_insert(&ngx_event_timer_rbtree, &ev->timer);


	//             
    ev->timer_set = 1;
}

        읽 기 이 벤트 를 레 드 블랙 트 리 타이머 에 추가 한 후, 다음 work 프로 세 스 는 이벤트 순환 에 들 어가 epoll 에 차단 합 니 다.wait 호출.그럼 epollwait 언제 돌아 올 까요?클 라 이언 트 의 데 이 터 를 받 은 후, 또는 모든 이벤트 의 정시 시간 이 되면 epollwait 복귀.다음은 epoll 을 어떻게 설정 하 는 지 보 겠 습 니 다.wait 의 시간 초과, 시간 초과 후 즉시 epollwait 복귀.
//work      
void ngx_process_events_and_timers(ngx_cycle_t *cycle)
{
	//                  ,   timer             
	timer = ngx_event_find_timer();

	//  epoll_wait    
    (void) ngx_process_events(cycle, timer, flags);
	
	//epoll_wait   ,        
	ngx_event_expire_timers();
}

        붉 은 검 은 나 무 는 이 진 트 리 이기 때문에 최소 시간 초과 시간 은 사실상 왼쪽 나무의 최소 값 이다.그래서 ngx 를 볼 수 있 습 니 다.event_find_timer 함수 의 실현 은 왼쪽 하위 트 리 에서 최소 값 을 찾 는 것 입 니 다.붉 은 검 은 나무의 실현 을 잘 모 르 면 주리 대신 의 블 로 그 를 볼 수 있다http://www.cnblogs.com/v-July-v/archive/2010/12/29/1983707.html
//               ;
//   :>0           
//		 <=0         
ngx_msec_t ngx_event_find_timer(void)
{
    ngx_msec_int_t      timer;
    ngx_rbtree_node_t  *node, *root, *sentinel;

	//     ,   -1        
    if (ngx_event_timer_rbtree.root == &ngx_event_timer_sentinel) 
	{
        return NGX_TIMER_INFINITE;
    }
    root = ngx_event_timer_rbtree.root;
    sentinel = ngx_event_timer_rbtree.sentinel;
	//     
    node = ngx_rbtree_min(root, sentinel);

	//        
    timer = (ngx_msec_int_t) node->key - (ngx_msec_int_t) ngx_current_msec;

    return (ngx_msec_t) (timer > 0 ? timer : 0);
}


        그리고 epollwait 호출 후 이벤트 가 시간 을 초과 하면 시간 초과 사건 을 어떻게 처리 합 니까?ngx_event_expire_timers 내부 에 빨 간 검 은 나 무 를 옮 겨 다 니 며 시간 이 초 과 된 모든 사건 을 찾 고 시간 이 초 과 된 사건 의 처리 반전 을 호출 합 니 다.주의해 야 할 것 은 함수 도 빨 간 검 은 나무 에서 이 시간 초과 사건 을 삭제 하기 때문에 이 시간 초과 사건 을 관리 해 야 한다 면 이 사건 을 빨 간 검 은 나무 가 실현 하 는 타이머 에 다시 추가 해 야 합 니 다.
//                 ,                
void ngx_event_expire_timers(void)
{
    ngx_event_t        *ev;
    ngx_rbtree_node_t  *node, *root, *sentinel;

    sentinel = ngx_event_timer_rbtree.sentinel;
	//     ,      
    for ( ;; ) 
	{
        root = ngx_event_timer_rbtree.root;
		//        
        if (root == sentinel)
		{
            return;
        }

		//             
        node = ngx_rbtree_min(root, sentinel);

        /* node->key <= ngx_current_time */
		//    
        if ((ngx_msec_int_t) node->key - (ngx_msec_int_t) ngx_current_msec <= 0)
        {
            ev = (ngx_event_t *) ((char *) node - offsetof(ngx_event_t, timer));

			//                   
            ngx_rbtree_delete(&ngx_event_timer_rbtree, &ev->timer);

			//              
            ev->timer_set = 0;

			//        
            ev->timedout = 1;

			//      
            ev->handler(ev);

			//           ,                 
            continue;
        }

		//           ,           ,                
        break;
    }
}

        이로써 시간 초과 사건 의 관리 도 분석 이 끝났다. 

좋은 웹페이지 즐겨찾기