nginx 소스 코드 분석의 이벤트 메커니즘

이벤트 메커니즘 은 특히 nginx 의 심장 처럼 끊임없이 작 동 하여 nginx 의 요청 응답 모드 가 정상적으로 작 동 하도록 보장 합 니 다.
본 고 는 사건 메커니즘 의 원리 와 실현 을 분석 할 것 이다.
nginx 자 체 는 poll, epoll, select, ao, kqueue 등 여러 가지 체 제 를 지원 합 니 다. 여기 서 epoll 을 분석 합 니 다. 이것 은 nginx 의 비장의 무기 이기 때 문 입 니 다.
처음 접 촉 했 을 때 우 리 는 감청, 요청, 수용, 응답 이라는 개념 만 알 고 있 었 다.우 리 는 이 사 고 를 따라 전개 하여 nginx 가 어떻게 이런 구조 체 를 설계 하 는 지 보 았 다.
1. 대체적으로 디자인
감청 이 든 요청 이 든 fd 가 생 길 수 있 는 것 은 연결 로 간주 되 며, fd 는 연결 (connection) 에 대응 합 니 다.
모든 연결 은 읽 기 (read) 와 쓰기 (write) 를 할 수 있 습 니 다. 이 두 가 지 는 이벤트 (event) 로 간주 되 고 구조 체 는 다음 과 같 습 니 다.
struct ngx_connection_s {
    void               *data;    //        ,listening, request, ...    
    ngx_event_t        *read;    //     
    ngx_event_t        *write;   //     

    ngx_socket_t        fd;        //    

    ngx_listening_t    *listening;   //      
};

struct ngx_event_s {
    void            *data;      //         ,connection, ...    

    unsigned         write:1;   //      
    
    unsigned         accept:1;  //      accept     

    unsigned         instance:1; //            

    unsigned         active:1;   //       ,   epoll_ctl    1

    unsigned         ready:1;     //    epoll_wait       1


    unsigned         timedout:1; //       
    unsigned         timer_set:1; //          ,               1


    ngx_event_handler_pt  handler; //         ,  


    ngx_rbtree_node_t   timer;        //                
};

2. 감청 listen
설정 파일 분석 (listen 명령 에 대한) 을 처리 할 때 nginx 는 이 listen 을 처리 하기 시작 합 니 다.그것들 을 ngx 에 놓 아 라cycle - > listening 에서.
struct ngx_cycle_s {
    ...
    ngx_array_t  listening;  //     ,    ngx_listening_s
    ...
}

struct ngx_listening_s {
    ngx_socket_t        fd;        //      

    struct sockaddr    *sockaddr;
    socklen_t           socklen;    
    ...
};
감청 은 읽 기 이벤트 가 있 고 사건 을 쓰 지 않 았 습 니 다. epoll 은 두 가지 모드 LT 와 ET 가 있 습 니 다. 감청 은 LT 를 사용 하고 감청 한 read 사건 의 처리 함 수 는 ngx 입 니 다.event_accept。
3. 수락
이 생 성 된 fd 는 읽 기와 쓰기 이벤트 가 있 고 읽 기 이벤트 에 대한 처리 함 수 는 ngx 입 니 다.http_init_request。따라서 연결 요청 이 완료 되면 이 함수 부터 실행 합 니 다.
이것 도 request 가 시작 하 는 생명 주기 입 니 다. 여기 의 구조 체 는:
struct ngx_http_request_s {
    uint32_t                          signature;         /* "HTTP" */

    ngx_connection_t                 *connection;   //      

    /*            ,    ,                     */
    void                            **ctx;
    void                            **main_conf;
    void                            **srv_conf;
    void                            **loc_conf;

    /*                     */
    u_char                           *uri_start;
    u_char                           *uri_end;
    u_char                           *uri_ext;
    u_char                           *args_start;
    u_char                           *request_start;
    u_char                           *request_end;
    u_char                           *method_end;
    u_char                           *schema_start;
    u_char                           *schema_end;
    u_char                           *host_start;
    u_char                           *host_end;
    u_char                           *port_start;
    u_char                           *port_end;

    unsigned                          http_minor:16;
    unsigned                          http_major:16;
};

4. 신기 한 시간 초과
시간 초과 처 리 를 했 기 때문에 전체 코드 의 복잡 도 는 적어도 한 단계 향상 되 었 습 니 다. libevent 와 같은 동쪽 은 신호 로 시간 을 초과 한 것 입 니 다.
nginx 작 가 는 이런 처리 방식 이 라인 이 안전 하지 않다 고 생각해 야 하기 때문에 스스로 하 나 를 실현 했다.이것 은 반복 적 으로 바퀴 를 만 드 는 것 이 아니 라 시간 초과 메커니즘 이다.
응용 프로그램의 일부 논리 로 응용 프로그램 코드 에서 크게 나 무 랄 데 가 없다.
시간 초과 기 제 는 빨 간 검 은 나 무 를 사 용 했 습 니 다. 잦 은 삽입, 검색 과 삭제 가 있 기 때문에 빨 간 검 은 나 무 를 사용 하 는 효율 이 매우 높 습 니 다.
초기 화: 전문 변수 ngxevent_timer_rbtree
ngx_event_timer_init(cycle->log);
시간 초과 검사: epoll 모든 이벤트 처리 전에 시간 초과 가 무엇 인지 확인 하고 이 벤트 를 timedout 으로 표시 하 며 이벤트 처 리 를 즉시 실행 합 니 다.
ngx_event_expire_timers(void)
{
    ...
    
    for ( ;; ) {      

        node = ngx_rbtree_min(root, sentinel);

        /* node->key <= ngx_current_time,        ,       */

        if ((ngx_msec_int_t) (node->key - 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; //   timer_set
            ev->timedout = 1;  //      

            ev->handler(ev);   //     ,                  ,   handler     
                               //            

            continue;
        }

        break; //          ,    
    }
}

5. epoll 의 응용
연결 (또는 이벤트) 을 epoll 에 추가 해 야 처리 할 수 있 습 니 다. 그렇지 않 으 면 읽 을 수 있 거나 쓸 수 있 더 라 도 무시 할 수 있 습 니 다.
for ( ;; ) {
    timer = ngx_event_find_timer();

    events = epoll_wait(ep, event_list, (int) nevents, timer);

    ;    

    ;    

    ;      
}

이벤트 동작:
ngx_epoll_add_event
ngx_epoll_del_event
빗질
만약 우리 가 스스로 업무 논 리 를 쓴다 면, 어떻게 사건 을 처리 합 니까?
fd 가 이미 있다 고 가정 하면 socket 함 수 를 통 해 생 긴 것 일 수 있 습 니 다.
연결 가 져 오기:
c = ngx_get_connection(fd);
read, write 처리:
c->read->handler = ngx_http_init_request;
c->write->handler = ngx_http_empty_handler;
타이머 처리: 타 이 머 는 이벤트 에 대한 것 입 니 다.
ngx_add_timer(c->read, c->listening->post_accept_timeout);
ngx_add_timer(c->write, ...);
등록 이벤트: 즉 epoll 가입 입 니 다. 여 기 는 보통 ET 모드 를 사용 합 니 다.
ngx_handle_read_event(c->read, 0);
ngx_handle_read_event(c->write, 0);
위의 예 는 read, write 를 모두 처 리 했 습 니 다. 실제 상황 이 꼭 그래 야 하 는 것 은 아 닙 니 다. 읽 기 나 쓰기 이 벤트 를 처리 할 지, 필요 한 것, 사용 할 것 을 보 세 요.
7. 잊 을 수 없 는 timedout.
만약 당신 이 신경 을 쓴다 면, 모든 이벤트 의 handler 함수 체 앞 에 이런 코드 가 있다 는 것 을 알 게 될 것 입 니 다.
ngx_http_init_request(ngx_event_t *rev)
{       
    ...

    if (rev->timedout) {  
        ngx_http_close_connection(c);
        return;
    }

    ...
}


ngx_http_process_request_line(ngx_event_t *rev)
{
    ...

    if (rev->timedout) {       
        c->timedout = 1;
        ngx_http_close_request(r, NGX_HTTP_REQUEST_TIME_OUT);
        return;
    }

    ...
}
그래서 앞에서 언급 한 바 와 같이 시간 초과 검사 시 nginx 는 이벤트 만 timedout 으로 표시 하고 연결 을 닫 지 않 았 습 니 다. 이것 은 nginx 가 처리 할 수 있 기 때 문 입 니 다.
http, mail 또는 서로 다른 연결, 모든 연결 은 자신 만 의 처리 방식 이 있 기 때문에 이 코드 는 없 는 곳 이 없습니다.
8. 이 코드 를 이해 할 수 있 습 니까?
ngx_url_t                u;
ngx_peer_connection_t   peer;

ngx_memzero(&u, sizeof(ngx_url_t));
ngx_memzero(&peer, sizeof(ngx_peer_connection_t));

ngx_str_set(&u.url, "127.0.0.1:8080");

ngx_parse_url(pool, &u);

peer.sockaddr = u.addrs->sockaddr;
peer.socklen = u.addrs->socklen;
peer.name = u.addrs->name;
peer.get = ngx_event_get_peer;

ngx_event_connect_peer(&peer);

peer.connection->read->handler = ngx_mail_auth_http_read_handler;
peer.connection->write->handler = ngx_mail_auth_http_write_handler;

ngx_add_timer(peer.connection->read, ahcf->timeout);
ngx_add_timer(peer.connection->write, ahcf->timeout);

메 일 부분 auth 캡 처 입 니 다.http 의 코드, nginx 가 사용 하 는 광범 위 한 하 나 는 자신 이 socket 을 만들어 다른 서버 에 연결 하 는 것 입 니 다.
fastcgi, proxy 처럼 모두 이 렇 습 니 다. 그 안의 핵심 은 바로 ngx 입 니 다.event_connect_peer, 이 주 제 는 비교적 깊 습 니 다. 남 긴 후에 전문 적 으로 분석 하고 벽돌 을 먼저 던 집 니 다.
ps: 이 글 을 쓸 때 일부 코드 는 경험 에 의 해 두 드 렸 습 니 다. 잘못된 점 이 있 으 면 지적 해 주 십시오.디자인 의 차원 에 대해 이야기 할 수 있다 면 충분 하 다.
원문 읽 기:http://nglua.com/reads/4.html

좋은 웹페이지 즐겨찾기