소스 코드 측면 에서 nginx 와 uwsgi 의 통신 과정 을 이해 하 다.

13013 단어 webserver
문제 의 근원
상품 사진 을 올 리 는 문제 와 관련 된 항목 이 있 었 는데 저 는 그림 의 크기 를 제한 할 때 전체 그림 을 메모리 에 읽 은 다음 에 크기 를 판단 합 니 다.이런 방법 은 악의 적 인 공격 이나 그림 이 많 을 때 웹 애플 리 케 이 션 의 성능 에 심각 한 영향 을 줄 수 있다.원래 첫 번 째 콘 텐 츠 - length 를 먼저 판단 함으로써 크기 를 제한 하려 고 했 습 니 다.그러나 나중에 그림 이 전단 의 nginx 에서 완전히 읽 은 후에 uwsgi 에 전달 된다 면 이러한 판단 은 nginx 의 성능 에 영향 을 줄 것 이 라 고 생각 했다.이 를 위해 저 는 nginx 의 소스 코드 를 살 펴 보고 nginx 와 uwsgi 의 통신 과정 을 찾 았 습 니 다. 다음은 전체 통신 의 구체 적 인 절차 입 니 다.
리 셋 함수 설정
nginx 가 브 라 우 저 등 을 통 해 보 내 온 요청 을 proxypass 혹은 uwsgi패스 는 상위 웹 애플 리 케 이 션 에 전송 하여 처리 한 결 과 를 브 라 우 저 에 보 냅 니 다.uwsgi_pass 명령 의 처리 함 수 는 ngxhttp_uwsgi_handler, 즉 요청 이 있 으 면 설정 uwsgipass 의 location 시 ngx 호출http_uwsgi_handler 방법, 이 방법 은 전체 uwsgi 사건 처리 의 입구 방법 입 니 다.다음은 이 방법 을 살 펴 보 겠 습 니 다.
static ngx_int_t ngx_http_uwsgi_handler(ngx_http_request_t *r)
{
    ngx_http_upstream_t *u;
    ngx_http_uwsgi_loc_conf_t   *uwcf;
    uwcf = ngx_http_get_module_loc_conf(r, ngx_http_uwsgi_module);
    u = r->upstream;
    ……
    u->create_request = ngx_http_uwsgi_create_request;//  wsgi        
    u->process_header = ngx_http_uwsgi_process_status_line;//  wsgi    uwsgi      
    ……
    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);//      body  
    ……
}

이 방법 은 우선 ngx 를 만 듭 니 다.http_upstream_t 구조 체, 그리고 이 구조 체 의 몇 가지 반전 함 수 를 설정 합 니 다.nginx 는 고도 로 모듈 화 된 웹 서버 로 모든 기능 이 하나의 모듈 을 통 해 이 루어 지 며 uwsgi 와 의 통신 도 예외 가 아니다.전체 통신 과정 은 ngxhttp_uwsgi_module. c 이 모듈 파일 이 구현 되 었 습 니 다.물론 모든 모듈 을 nginx 에 추가 하려 면 갈고리 가 있어 야 합 니 다. nginx 에서 일부 링크 입 니 다. 그리고 모든 모듈 의 처리 방법 을 이 링크 에 추가 하면 nginx 프레임 코드 에 의 해 하나씩 호출 됩 니 다.웹 애플 리 케 이 션 과 통신 하 는 갈고리 파일 은 ngx 입 니 다.http_upstream. c 파일 은 nginx 가 언제 이 갈고리 함 수 를 촉발 하 는 지 에 대해 쉽게 말 하면 이 갈고리 함수 들 을 epoll 의 읽 기와 쓰기 이벤트 에 할당 하 는 것 입 니 다. 읽 기와 쓰기 요청 이 있 을 때 epoll 의 이벤트 응답 체 제 는 이 갈고리 함 수 를 호출 합 니 다.보시 다시 피 위의 갈고리 함 수 는 구조 요청 체 만 있 고 응답 머리 만 해석 할 수 있 으 며 패키지 에 대한 처리 가 없습니다. 이것 은 nginx 가 패키지 에 대해 처리 할 필요 가 없고 간단 한 저장 전송 만 할 수 있 기 때 문 입 니 다.
브 라 우 저 에서 데이터 읽 기
먼저 nginx 가 브 라 우 저 에서 데 이 터 를 얼마나 읽 어야 할 지 결정 하 는 것 을 보 겠 습 니 다. 이것 은 content - length 의 첫 번 째 부분 에서 결정 되 는 것 이 분명 하지만 client 를 받 을 수 있 습 니 다.max_body_size 명령 의 영향.우 리 는 nginx. conf 에 client 를 추가 할 수 있 습 니 다.max_body_size 설정 명령, 이 명령 은 http, server, location 세 곳 에 놓 을 수 있 습 니 다.nginx 는 머리 를 읽 은 후 src / http / ngx 를 호출 합 니 다.http_core_module. c 파일 중 ngxhttp_core_find_config_phase 방법 은 재 작성 한 URI 에 따라 일치 하 는 location 블록 을 검색 합 니 다. 이 때 설정 파일 은 합 쳐 졌 고 간단 한 검색 일 뿐 입 니 다.클 라 이언 트 가 설 치 된 것 을 발견 하면max_body_size 프로필 은 요청 체 가 주어진 크기 를 초과 하 는 지 아래 비 교 를 통 해 판단 합 니 다.
ngx_int_t ngx_http_core_find_config_phase(ngx_http_request_t *r, ngx_http_phase_handler_t *ph)
{
    if (clcf->client_max_body_size < r->headers_in.content_length_n)
    {
      ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,"client intended to send too large body: %O bytes",
            r->headers_in.content_length_n);
    }
}

설정 한 크기 를 초과 하지 않 았 다 면, ngxhttp_request_body_t 구조 체 에서 rest 는 content - length 의 크기 로 설정 되 어 읽 어야 할 바이트 수 를 표시 합 니 다.진정한 읽 기 방법 ngxhttp_read_client_request_body, 이 방법 은 반전 함수 ngxhttp_upstream_init, 이 반전 함 수 는 nginx 와 상류 통신 의 입 구 를 여 는 것 입 니 다.주의, 비록 ngxhttp_read_client_request_body 방법 은 막 히 지 않 습 니 다. 즉, 데이터 수신 이 없 을 때 바로 돌아 갑 니 다. 그러나 데이터 가 모두 수신 되 어야 ngx 를 호출 할 수 있 습 니 다.http_upstream_init 방법, 구체 적 인 실현 코드 보기:
ngx_int_t  ngx_http_read_client_request_body(ngx_http_request_t *r, ngx_http_client_body_handler_pt post_handler) 
{
    if (rb->rest == 0) { /*        body */
        if (r->request_body_in_file_only) {
            ngx_http_write_request_body(r); /*             */
        }
        post_handler(r); /*       , ngx_http_upstream_init */
    }
    r->read_event_handler = ngx_http_read_client_request_body_handler;//     
    rc = ngx_http_do_read_client_request_body(r);
}

이 를 통 해 알 수 있 듯 이 읽 기 가 끝나 면 ngx 를 호출 합 니 다.http_upstream_init 는 uwsgi 와 통신 을 시작 합 니 다. 없 으 면 먼저 읽 기 이 벤트 를 ngx 로 설정 합 니 다.http_read_client_request_body_handler 방법, 이 방법 내부 에서 오류 검 사 를 하 는 것 외 에 도 ngx 를 호출 합 니 다.http_read_client_request_body 방법, 즉 모든 body 를 읽 지 않 으 면 nginx 는 epoll 에 이 방법 을 계속 호출 하여 body 의 읽 기 작업 을 하 라 고 알려 줍 니 다.이 방법의 마지막 에 ngx 호출http_do_read_client_request_body 방법 으로 진정한 읽 기 동작 을 진행 합 니 다.
static ngx_int_t  ngx_http_do_read_client_request_body(ngx_http_request_t *r) 
{
    for ( ;; ) {
        for ( ;; ) {
            if (rb->buf->last == rb->buf->end) { /*    request_body    buf       */
                rc = ngx_http_request_body_filter(r, &out);/*                    ,      ,        */
            }

            n = c->recv(c, rb->buf->last, size); //                
            if (rb->rest == 0) { //           
                break;
            }
        }
    }
    if (rb->temp_file || r->request_body_in_file_only) {
        ngx_http_write_request_body(r); //           ,      ,       
    }
    if (!r->request_body_no_buffering) { //nginx    proxy_request_buffering    
        r->read_event_handler = ngx_http_block_reading;
        rb->post_handler(r);//  ngx_http_upstream_init  
    }
    return NGX_OK;
}

위 에서 볼 수 있 듯 이 proxy 가 열 리 면request_buffering (기본 오픈) 은 nginx 가 모든 데 이 터 를 읽 은 후에 uwsgi 에 보 냅 니 다.
uwsgi 에 데이터 보 내기
다음은 전체 통신 의 입구 함수 ngxhttp_upstream_init:
void  ngx_http_upstream_init(ngx_http_request_t *r) 
{
    ngx_http_upstream_init_request(r);
}

static void ngx_http_upstream_init_request(ngx_http_request_t *r)
{
    u->create_request(r);//  ngx_http_uwsgi_create_request      
    ngx_http_upstream_connect(r, u);//   uwsgi   
}

재 ngxhttp_uwsgi_create_request 방법 에서 주요 업 무 는 wsgi 프로 토 콜 에 따라 요청 메 시 지 를 구축 하 는 것 이다. 주로 첫 번 째 정보 이 고 구 축 된 연결 을 통 해 uwsgi 에 메 시 지 를 보 내 는 것 이다.방법 ngxhttp_upstream_connect 에 서 는 uwsgi 와 통신 하 는 방법 을 설정 하 였 습 니 다.
static void  ngx_http_upstream_connect(ngx_http_request_t *r, ngx_http_upstream_t *u)  
{
    ……
    rc = ngx_event_connect_peer(&u->peer); //            
    u->write_event_handler = ngx_http_upstream_send_request_handler; //   uwsgi       
    u->read_event_handler = ngx_http_upstream_process_header; //    uwsgi     
    ……
    ngx_http_upstream_send_request(r, u, 1); //               
}

uwsgi 에서 머리 정보 읽 기
이로써 알 수 있 듯 이 uwsgi 에서 데 이 터 를 nginx 로 보 낼 때 읽 기 이벤트, 즉 ngx 를 호출 합 니 다http_upstream_process_header 방법:
static void  ngx_http_upstream_process_header(ngx_http_request_t *r, ngx_http_upstream_t *u) 
{
    for ( ;; ) {
        n = c->recv(c, u->buffer.last, u->buffer.end - u->buffer.last); // uwsgi      
        if (n == NGX_AGAIN) { //      ,     epoll 
            ngx_handle_read_event(c->read, 0);
        }
        rc = u->process_header(r); //  uwsgi       ngx_http_uwsgi_process_status_line        
        if (rc == NGX_AGAIN) { //       ,    
         continue;
         }
        break;
    }
    /* rc == NGX_OK:         */
    if (!r->subrequest_in_memory) {
        ngx_http_upstream_send_response(r, u);//            
        return;
    }
}

머리 정 보 를 분석 한 후, ngx 를 호출 합 니 다.http_upstream_process_header 의 마지막 ngxhttp_upstream_send_response 방법 은 클 라 이언 트 에 응답 헤드 를 보 냅 니 다.또한 이 방법 에 서 는 사용자 가 uwsgi 를 설정 할 때 uwsgi 를 받 을 지 여부 와 관련 이 있 습 니 다.buffering 로고 (기본 오픈).열 리 면 nginx 는 가능 한 한 많은 데 이 터 를 캐 시 해서 브 라 우 저 에 보 냅 니 다. 메모리 캐 시가 부족 하면 임시 파일 에 저장 합 니 다.사용자 가 닫 히 면 nginx 는 최대 uwsgi 만 캐 시 합 니 다.buffer_size 크기 의 body 는 캐 시 가 가득 찼 을 때 백 엔 드 가 차단 모델 을 기반 으로 한 uwsgi 가 send 할 수 없어 서 막 힐 수 있 습 니 다.다음은 이 두 가지 상황 에서 의 데이터 전송 에 대해 토론 한다.
캐 시 를 열지 않 은 상태 에서 uwsgi 에서 패 키 지 를 읽 고 전송 합 니 다.
uwsgi 에서buffering 이 off 로 설 정 된 경우 uwsgi 에서 body 를 읽 고 body 를 브 라 우 저 에 보 내 는 방법 은 각각 ngx 입 니 다.http_upstream_process_non_buffered_upstream 과 ngxhttp_upstream_process_non_buffered_downstream。이 두 가지 방법 은 내부 에서 모두 ngx 를 호출 했다.http_upstream_process_non_buffered_request 방법, 다음은 이 방법 을 구체 적 으로 살 펴 보 겠 습 니 다.
static void ngx_http_upstream_process_non_buffered_request(ngx_http_request_t *r,ngx_uint_t do_write)
{
    /*             ,    ,              ,     ,     */
    for ( ;; ) 
    {
        if (do_write)//       
        {
            if (u->out_bufs || u->busy_bufs) 
            {
                /*      out_bufs     */
                rc = ngx_http_output_filter(r, u->out_bufs);

            }
            //                           
            if (u->busy_bufs == NULL) 
            {
                b->pos = b->start;
                b->last = b->start;
            }
        }
        /* do_write 0,            */
        size = b->end - b->last;//  buffer            
        if (size && upstream->read->ready) 
        {
            n = upstream->recv(upstream, b->last, size);//         buffer    
            if (n == NGX_AGAIN) 
            {//  epoll            
                break;
            }
            if (n > 0) 
            {
                u->state->response_length += n;
                /*      */
                u->input_filter(u->input_filter_ctx, n);
            }
            /*           ,    do_write 1,              */
            do_write = 1;
            continue;
        }
        break;
    }
}

이 방법 은 데 이 터 를 읽 고 do 를 설정 하 는 것 을 볼 수 있 습 니 다.write = 1, 전송 이 벤트 를 촉발 하기 때문에 비 캐 시 입 니 다.
캐 시 를 열 었 을 때 uwsgi 에서 패 키 지 를 읽 고 전송 합 니 다.
uwsgi 에서buffering 이 on 으로 설 정 된 경우 uwsgi 에서 body 를 읽 고 body 를 브 라 우 저 에 보 내 는 방법 은 각각 ngx 입 니 다.http_upstream_process_upstream 과 ngxhttp_upstream_process_downstream。이 두 가지 방법 은 마지막 으로 ngx 를 호출 합 니 다.event_pipe 방법 은 이 방법 에서 표지 위 치 를 통 해 각각 uwsgi 에서 데 이 터 를 읽 고 브 라 우 저 에 데 이 터 를 보 냅 니 다. 구체 적 으로 다음 과 같 습 니 다.
ngx_int_t ngx_event_pipe(ngx_event_pipe_t *p, ngx_int_t do_write)
{
    for ( ;; ) 
    {
        if (do_write) 
        {
            rc = ngx_event_pipe_write_to_downstream(p);//            
        }
        p->read = 0;
        p->upstream_blocked = 0;   
        /*            */
        ngx_event_pipe_read_upstream(p);
        if (!p->read && !p->upstream_blocked) {
            break;
        }
        /*           ,  p->read   1,    do_write 1,            */
        do_write = 1;
    }
}

uwsgi 에서 데 이 터 를 읽 습 니 다. 이 과정 에서 다음 과 같은 몇 가지 상황 과 관련 될 수 있 습 니 다. 1) 응답 머리 를 받 을 때 일부 패 킷 을 받 을 수 있 습 니 다.2) bufs. num 상한 선 에 도달 하지 않 으 면 bufs. size 크기 의 메모리 블록 을 수신 버퍼 로 할당 할 수 있 습 니 다.4) 버퍼 가 가득 차 면 임시 파일 을 써 야 합 니 다.
static ngx_int_t ngx_event_pipe_read_upstream(ngx_event_pipe_t *p)
{

    for ( ;; ) 
    {
            /*            ngx_buf_t    */ 
            if (p->free_raw_bufs) 
            {
                chain = p->free_raw_bufs;
            } 
            //     ,         
            else if (p->allocated < p->bufs.num) 
            {
                b = ngx_create_temp_buf(p->pool, p->bufs.size);//                 
                ……
            }
            //     
            else if (p->cacheable|| p->temp_file->offset < p->max_temp_file_size)
            {
                rc = ngx_event_pipe_write_chain_to_temp_file(p);//            

            } else {

                break;
            }
            //       
            n = p->upstream->recv_chain(p->upstream, chain);
    }
    if (p->length == 0) {//      ,          
        p->upstream_done = 1;
        p->read = 1;
    }
}

이 방법 은 가능 한 한 많은 데 이 터 를 캐 시 해서 브 라 우 저 에 보 내 는 것 임 을 알 수 있다.
총결산
  • nginx 는 uwsgi 에 데 이 터 를 보 냅 니 다. 먼저 nginx 는 사용자 가 client 를 설정 하 는 지 여 부 를 판단 합 니 다.max_body_size 명령 을 설정 하면 이 값 으로 content - length 와 비교 합 니 다. 보 낸 패키지 가 설정 한 값 을 초과 하면 nginx 는 413 패키지 의 너무 큰 오 류 를 되 돌려 줍 니 다.패키지 가 주어진 범위 내 에 있 으 면 nginx 는 proxyrequest_buffering 이 열 릴 지 여 부 는 클 라 이언 트 가 보 낸 요청 을 먼저 캐 시 할 지 여 부 를 결정 합 니 다.프 록 시 를 닫 으 면request_buffering (기본 오픈) 은 nginx 가 일부 데 이 터 를 받 으 면 uwsgi 에 직접 보 냅 니 다.프 록 시 가 열 리 면request_buffering, nginx 는 모든 데 이 터 를 읽 은 후에 uwsgi 에 보 내 는 것 입 니 다. body 가 너무 크 면 body 를 임시 파일 에 먼저 저장 해 야 할 수도 있 습 니 다.
  • uwsgi 는 nginx 로 데 이 터 를 되 돌려 줍 니 다. 만약 uwsgibuffering 오픈 (기본 오픈), nginx 는 가능 한 한 uwsgi 가 보 낸 body 를 캐 시 합 니 다. 버퍼 크기 는 uwsgi 입 니 다.buffers 와 uwsgibuffer_size 두 명령 이 설정 한 버퍼 의 합;저장 할 수 없 으 면 임시 파일 을 써 야 합 니 다. 임시 파일 의 크기 는 uwsgi 입 니 다.max_temp_size 와 uwsgitemp_file_write_size 결정;닫 으 면 데이터 가 브 라 우 저 에 동기 화 되 며, 매번 최대 캐 시 uwsgibuffer_size 의 데 이 터 는 upstream - > recv () 방법 으로 볼 수 있 습 니 다. 꽉 차 면 uwsgi 가 데 이 터 를 보 내지 못 해 막 힐 수 있 습 니 다.
  • 일 할 때 보통 proxyrequest_buffering 과 uwsgibuffering 기본 으로 열 린 설정 입 니 다. 이렇게 해야만 nginx 의 높 은 병발 특성 을 충분히 이용 할 수 있 고 연결 이 백 엔 드 의 웹 애플 리 케 이 션 을 오래 사용 하지 않 습 니 다.또한 요청 body 의 크기 를 제한 하려 면 그림 을 올 리 는 것 과 같이 nginx 가 최대 합 리 적 인 크기 로 켜 고 python 은 구체 적 인 인터페이스 업무 에 따라 일부분 을 읽 고 크기 를 초과 하면 버 립 니 다.nginx 는 여러 요청 body 의 크기 를 제한 해 야 할 수 있 기 때문에 보통 최대 값 을 설정 한 다음 웹 응용 에서 수요 에 따라 제한 합 니 다.
  • 좋은 웹페이지 즐겨찾기