nginx 소스 코드 분석 (6) - 연결 구축

9582 단어
웹 서버 에 있어 클 라 이언 트 의 연결 을 감청 할 수 있어 야 통신 할 수 있 습 니 다. 이 글 은 nginx 가 어떻게 연결 을 실현 하 는 지 보 겠 습 니 다.새로운 연결 을 감청 하 는 것 은 실제 적 으로 socket 의 읽 기 이 벤트 를 감청 하 는 것 입 니 다. 이 때 socket 의 연결 대기 열 이 비어 있 지 않 습 니 다. 차단 되 지 않 은 호출 accpet 로 새로운 연결 을 가 져 올 수 있 습 니 다.nginx 에 서 는 모든 socket 이 하나의 연결 구조 로 봉 인 됩 니 다. 바로 ngx 입 니 다.connection_t 타 입.매개 ngxconnection_t 구 조 는 읽 기와 쓰기 사건 read 와 write 를 가지 고 있 으 며, 그것들 은 ngx 이다.event_t 형식의 handler 반전 함수 포인터 가 있 습 니 다. 읽 기와 쓰기 이벤트 가 발생 했 을 때 호출 됩 니 다.감청 socket 초기 화 에 서 는 주로 감청 주소, 포트 등 정 보 를 가 져 오 는 socket 초기 화 를 소개 합 니 다.이벤트 모델 에서 nginx 이벤트 모델 의 초기 화 를 소 개 했 습 니 다. ngxevent_process_init 함수 에서 모든 감청 socket 에 연결 을 할당 하고 이 연 결 된 read 의 handler 를 ngx 로 초기 화 합 니 다.event_accept, 즉 연결 을 감청 할 때 호출 되 고 연결 을 초기 화 하 는 데 사용 되 며 마지막 으로 이벤트 순환 에 추가 합 니 다.
        이벤트 순환 에 서 는 놀 라 움 을 방지 하기 위해 서 (새로 도착 한 연결 이 모든 막 힌 worker 프로 세 스 를 깨 울 수 있 습 니 다) accept 잠 금 을 가 져 온 worker 프로 세 스 만 accept 새로운 연결 을 받 을 수 있 습 니 다. 그 다음 에 ngx 를 호출 할 수 있 습 니 다.event_accept 함수 처리.구체 적 인 과정 을 살 펴 보 겠 습 니 다.
1. ngx_event_accept
    ecf = ngx_event_get_conf(ngx_cycle->conf_ctx, ngx_event_core_module);

    if (ngx_event_flags & NGX_USE_RTSIG_EVENT) {
        ev->available = 1;

    } else if (!(ngx_event_flags & NGX_USE_KQUEUE_EVENT)) {
        ev->available = ecf->multi_accept;
    }
        이벤트 의 available 속성 을 초기 화하 여 이벤트 발생 을 표시 합 니 다.
    lc = ev->data;
    ls = lc->listening;
    ev->ready = 0;

    ngx_log_debug2(NGX_LOG_DEBUG_EVENT, ev->log, 0,
                   "accept on %V, ready: %d", &ls->addr_text, ev->available);
        ngx_event_t 의 data 속성 은 이 이벤트 가 있 는 연결 입 니 다. socket 의 연결 을 감청 할 때 listening 속성 을 통 해 해당 하 는 감청 socket (ngx listening t) 을 얻 을 수 있 습 니 다.다음 while 순환 은 ev - > available 횟수 를 교체 하여 해당 하 는 연결 을 가 져 오 는 데 사 용 됩 니 다.while 순환 의 시작 부분 은 accept 를 호출 하여 연결 socket 을 가 져 오 는 것 입 니 다.
    ngx_accept_disabled = ngx_cycle->connection_n / 8
                              - ngx_cycle->free_connection_n;
        이벤트 모델 을 소개 할 때 ngxaccept_disabled 는 주로 워 커 프로 세 스 의 간단 한 부하 균형 을 실현 하 는 데 사 용 됩 니 다. 워 커 프로 세 스 의 남 은 연결 개수 가 연결 탱크 크기 의 1 / 8 보다 적 을 때 이 프로 세 스 는 경쟁 accept 잠 금, ngx 를 포기 합 니 다.accept_disabled 는 ngxprocess_events_and_timers 함수 에서 사용 합 니 다.
        c = ngx_get_connection(s, ev->log);

        if (c == NULL) {
            if (ngx_close_socket(s) == -1) {
                ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                              ngx_close_socket_n " failed");
            }

            return;
        }
        s 는 socket 을 연결 하 는 파일 설명자 입 니 다. 연결 풀 에서 연결 을 가 져 와 이 socket 에 할당 합 니 다.
        c->pool = ngx_create_pool(ls->pool_size, ev->log);
        if (c->pool == NULL) {
            ngx_close_accepted_connection(c);
            return;
        }
        이 연결 을 위해 메모리 풀 을 만 듭 니 다.
        c->sockaddr = ngx_palloc(c->pool, socklen);
        if (c->sockaddr == NULL) {
            ngx_close_accepted_connection(c);
            return;
        }

        ngx_memcpy(c->sockaddr, sa, socklen);

        log = ngx_palloc(c->pool, sizeof(ngx_log_t));
        if (log == NULL) {
            ngx_close_accepted_connection(c);
            return;
        }
        연 결 된 sockaddr 는 클 라 이언 트 socket 주 소 를 저장 합 니 다. 공간 을 할당 합 니 다.그리고 연 결 된 log 구 조 를 위해 공간 을 분배 합 니 다.
        if (ngx_inherited_nonblocking) {
            if (ngx_event_flags & NGX_USE_AIO_EVENT) {
                if (ngx_blocking(s) == -1) {
                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                                  ngx_blocking_n " failed");
                    ngx_close_accepted_connection(c);
                    return;
                }
            }

        } else {
        	//       
            if (!(ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT))) {
                if (ngx_nonblocking(s) == -1) {
                    ngx_log_error(NGX_LOG_ALERT, ev->log, ngx_socket_errno,
                                  ngx_nonblocking_n " failed");
                    ngx_close_accepted_connection(c);
                    return;
                }
            }
        }
        aio 를 사용 할 때 차단 방식 을 사용 하고 다른 모드 (epoll, select 등) 는 비 차단 방식 을 사용 합 니 다.
        c->recv = ngx_recv;
        c->send = ngx_send;
        c->recv_chain = ngx_recv_chain;
        c->send_chain = ngx_send_chain;

        c->log = log;
        c->pool->log = log;

        c->socklen = socklen;
        c->listening = ls;
        c->local_sockaddr = ls->sockaddr;

        c->unexpected_eof = 1;
        새로운 연결 의 일부 속성 을 초기 화 합 니 다. 수신, 발송 함수, 로그, 감청 socket 등 을 포함 합 니 다.다음은 연 결 된 이벤트 부분 을 초기 화 합 니 다.
        rev = c->read;
        wev = c->write;

        wev->ready = 1;

        if (ngx_event_flags & (NGX_USE_AIO_EVENT|NGX_USE_RTSIG_EVENT)) {
            /* rtsig, aio, iocp */
            rev->ready = 1;
        }

        if (ev->deferred_accept) {
            rev->ready = 1;
#if (NGX_HAVE_KQUEUE)
            rev->available = 1;
#endif
        }

        rev->log = log;
        wev->log = log;
        읽 기와 쓰기 이벤트 의 ready 속성 을 설정 합 니 다. 이 속성 은 이 이벤트 가 준비 되 었 음 을 나타 내 며 촉발 할 수 있 습 니 다.
        c->number = ngx_atomic_fetch_add(ngx_connection_counter, 1);
        ngx_connection_counter 는 nginx 의 연결 카운터 로 공유 메모리 에서 초기 화 되 는 것 을 방지 합 니 다. 이벤트 module 의 module init 리 셋 함수 에서 호출 됩 니 다. 구체 적 으로 는 ngx 입 니 다.event_module_init 함수.연 결 된 number 필드 는 이 연결 의 번 호 를 분명히 나타 낸다.
        if (ls->addr_ntop) {
            c->addr_text.data = ngx_pnalloc(c->pool, ls->addr_text_max_len);
            if (c->addr_text.data == NULL) {
                ngx_close_accepted_connection(c);
                return;
            }

            c->addr_text.len = ngx_sock_ntop(c->sockaddr, c->addr_text.data,
                                             ls->addr_text_max_len, 0);
            if (c->addr_text.len == 0) {
                ngx_close_accepted_connection(c);
                return;
            }
        }
        이 코드 는 바 이 너 리 주 소 를 텍스트 형식 으로 바 꾸 고 연 결 된 addr 에 값 을 부여 합 니 다.text 속성.
        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;
            }
        }
        호출 ngxadd_conn 은 새 연결 을 nginx 의 이벤트 순환 에 추가 합 니 다.epoll 을 사용 할 때 실제로 ngx 를 호출 합 니 다.epoll_add_connection 함수, 최종 호출 epollctl 에 이 벤트 를 추가 하면 이 socket 에서 온 데 이 터 를 나중에 들 을 수 있 습 니 다.
        log->data = NULL;
        log->handler = NULL;

        /**
         *     socket      ngx_http_init_connection(http/ngx_http_request.c),  handler
         *   ngx_http_add_listening      
         */
        ls->handler(c);
        ls 는 socket (ngx listening t) 을 감청 합 니 다. handler 는 accept 에서 새 연결 에 호출 되 었 습 니 다. 설명 은 이미 분명하게 말 했 습 니 다. 바로 ngx 를 호출 하 는 것 입 니 다.http_init_connection 연결 초기 화 를 마 쳤 습 니 다. 그 중에서 가장 중요 한 것 은 읽 기 이벤트 에 handler 를 설정 하 는 것 입 니 다. 다음은 이 함 수 를 보 겠 습 니 다.
2. ngx_http_init_connection
    ctx = ngx_palloc(c->pool, sizeof(ngx_http_log_ctx_t));
    if (ctx == NULL) {
        ngx_http_close_connection(c);
        return;
    }

    ctx->connection = c;
    ctx->request = NULL;
    ctx->current_request = NULL;

    c->log->connection = c->number;
    c->log->handler = ngx_http_log_error;
    c->log->data = ctx;
    c->log->action = "reading client request line";

    c->log_error = NGX_ERROR_INFO;
        로그 관련 속성 을 초기 화 합 니 다.
    rev = c->read;
    rev->handler = ngx_http_init_request;
    c->write->handler = ngx_http_empty_handler;
        이 코드 는 가장 핵심 적 인 것 입 니 다. 이 벤트 를 읽 는 handler 를 ngx 로 설정 합 니 다.http_init_request, 클 라 이언 트 가 서버 에 데 이 터 를 보 낼 때 호출 되 어 클 라 이언 트 요청 을 초기 화하 고 처리 합 니 다.뒤에서 ngxin 요청 처 리 를 소개 하 겠 습 니 다. 상세 하 게 소개 하 겠 습 니 다.
    if (rev->ready) {
        /* the deferred accept(), rtsig, aio, iocp */

        if (ngx_use_accept_mutex) {
            ngx_post_event(rev, &ngx_posted_events);
            return;
        }

        ngx_http_init_request(rev);
        return;
    }
        설명 에 따 르 면 이 코드 는 deferred accept, rtsig, ao 와 iocp 를 사용 할 때 호출 되 며, ngxevent_accept 에 서 는 이 몇 가지 상황 에서 만 읽 기 이벤트 의 ready 를 1 로 설정 합 니 다.
    ngx_add_timer(rev, c->listening->post_accept_timeout);

    /*
     *                  
     */
    if (ngx_handle_read_event(rev, 0) != NGX_OK) {
#if (NGX_STAT_STUB)
        (void) ngx_atomic_fetch_add(ngx_stat_reading, -1);
#endif
        ngx_http_close_connection(c);
        return;
    }
        timer 를 설정 하고 마지막 으로 새 socket 설명 자 를 이벤트 순환 에 추가 합 니 다. 그러나 epoll 을 사용 할 때 이벤트 순환 에 추가 하 는 작업 은 이미 ngxadd_conn 에서 완성 되 었 기 때문에 이 함 수 는 어떠한 조작 도 하지 않 습 니 다.
        위 는 nginx 가 연결 을 만 드 는 처리 과정 입 니 다. 주로 연결 의 속성 을 초기 화하 고 이 벤트 를 읽 는 handler 를 설정 하여 요청 을 처리 합 니 다. 다음 편 은 nginx 의 요청 처 리 를 소개 합 니 다.

좋은 웹페이지 즐겨찾기