Nginx 의 하위 요청 (subrequest) 원리 분석

9827 단어 nginx
Nginx 의 하위 요청 (subrequest) 원리 분석
Nginx 의 하위 요청 디자인 은 자신의 다단 계 처리 절 차 를 바탕 으로 지정 한 url 에 대해 측로 요청 을 하 는 기능 을 실현 하고 보통 감 권, 미 러 등 기능 을 사용 합 니 다.물론 다른 용법 도 있 습 니 다. 여기 서 일일이 설명 하지 않 습 니 다. 보통 사용자 가 사용 하 는 인 터 페 이 스 는 auth 와 같 습 니 다.request 인터페이스 나 lua 의 capture 인터페이스.이 두 대외 인 터 페 이 스 는 모두 Nginx 의 ngx_http_subrequest 함 수 를 사용 했다.본문 은 그 하위 요청 절 차 를 약간 정리 하 였 다.
배경 요구: Nginx 가 생명 주 기 를 요청 하 는 각 데이터 구 조 를 설명 하 는 데 사용 되 는 인식, 예 를 들 어 ngx_http_request_tngx_connection_t.
단순 업무
다음 설정 파일 은 외부 요청 이 / login 일 때 옆길 요청 을 하고 url 을 / auth 로 요청 합 니 다. 이 url 이 200 으로 돌아 간 후에 야 업 무 를 계속 할 수 있 습 니 다.물론 / auth 논 리 를 간소화 하기 위해 여 기 는 직접 / auth 아래 에서 2xx 를 되 돌려 주 었 습 니 다. 일반적인 상황 에서 복잡 한 업무 논리 가 있어 야 인증 을 완성 할 수 있 습 니 다.(auth request 상세 기능 은 여러분 이 직접 조회 하 세 요. 여 기 는 군말 하지 않 습 니 다).
    location /login {
        auth_request /auth/;
        
        #    
        proxy_pass http://xxxx;
    }

    location  /auth {
        return 200 ok;
    #   proxy_pass http://ups;
    }


Nginx 는 어떻게 측로 기능 을 실현 합 니까?검토 해 보 겠 습 니 다.
외부 요청 이 왔 을 때 먼저 Nginx 의 HTTP 일반적인 처 리 를 거 쳤 습 니 다. 즉, 프로 토 콜 을 분석 한 다음 에 location 에 일치 합 니 다. 이 단계 에서 처리 한 후에 access 단계 에 들 어가 면 access 단 계 는 content 단계 (즉 proxy pass) 앞 에 있 기 때문에 access 단계 의 흐름 은 proxy 보다 앞 설 수 있 습 니 다.패스 실행.
이 사례, 즉 위 에 파일 예 시 를 설정 한 authrequest 는 이 access 단계 함 수 를 실행 합 니 다.
호출 스 택 은 다음 과 같 습 니 다.
ngx_http_auth_request_handler
ngx_http_core_access_phase
ngx_http_core_run_phases
ngx_http_handler
ngx_http_process_request
xxxxx

Nginx 처리 절 차 를 잘 알 고 있다 면 이런 호출 스 택 은 매우 익숙 합 니 다. ngx_http_auth_request_handler 각 단 계 를 순환 처리 하 는 데 사 용 됩 니 다. ngx_http_core_run_phases 는 하나의 패키지 이 고 실제 호출 은 구체 적 으로 등 록 된 ngx_http_core_access_phase 함수 입 니 다.ngx_http_auth_request_handler 함 수 는 무엇 을 합 니까?
static ngx_int_t
ngx_http_auth_request_handler(ngx_http_request_t *r)
{
    ngx_http_request_t            *sr;
    ngx_http_post_subrequest_t    *ps;
    ngx_http_auth_request_ctx_t   *ctx;
    
    ctx = ngx_http_get_module_ctx(r, ngx_http_auth_request_module);

    if (ctx != NULL) {
        #            ,          ,       ctx    。        。
    }
    
    ctx = ngx_pcalloc(r->pool, sizeof(ngx_http_auth_request_ctx_t));
    if (ctx == NULL) {
        return NGX_ERROR;
    }

    ps = ngx_palloc(r->pool, sizeof(ngx_http_post_subrequest_t));
    ps->handler = ngx_http_auth_request_done;
    ps->data = ctx;
    
    ngx_http_subrequest(r, &arcf->uri, NULL, &sr, ps, NGX_HTTP_SUBREQUEST_WAITED);
   
   
   return NGX_AGAIN;

}

위의 함수 에는 두 가지 관건 이 있 는데 첫 번 째 관건 은 호출 ngx_http_auth_request_handler 이 고 두 번 째 관건 은 ngx_http_subrequest 입 니 다. 후 자 를 먼저 말 하고 NGX 로 돌아 갑 니 다.AGAIN 때 는 외부 return NGX_AGAIN; 가 뒤의 단계 (content 단계) 를 실행 하지 않 는 순환 을 종료 하 는데, 이러한 목적 은 인증 이 완 료 된 후 뒤의 단 계 를 실행 하기 위 한 것 으로 'access' 논리 에 도 부합 한다.
void
ngx_http_core_run_phases(ngx_http_request_t *r)
{
    ngx_int_t                   rc;
    ngx_http_phase_handler_t   *ph;
    ngx_http_core_main_conf_t  *cmcf;

    cmcf = ngx_http_get_module_main_conf(r, ngx_http_core_module);

    ph = cmcf->phase_engine.handlers;

    while (ph[r->phase_handler].checker) {

        # ngx_http_core_access_phase     ngx_http_auth_request_handler   NGX_AGAIN ,    NGX_OK
        rc = ph[r->phase_handler].checker(r, &ph[r->phase_handler]);

        if (rc == NGX_OK) {
            return;
        }
    }
}


두 번 째 관건 점 ngx_http_core_run_phases 함수 다시 보 겠 습 니 다.
ngx_int_t
ngx_http_subrequest(ngx_http_request_t *r,
    ngx_str_t *uri, ngx_str_t *args, ngx_http_request_t **psr,
    ngx_http_post_subrequest_t *ps, ngx_uint_t flags)
{

    ngx_http_request_t            *sr;
    
    #   :          1,          ,         r,            
    r->main->count++;
    
    #     ngx_http_request_t ,              ,     
    sr = ngx_pcalloc(r->pool, sizeof(ngx_http_request_t));
    
    #  access     url,     location 
    ngx_http_update_location_config(sr);

    #   sr    r posted_requests  。                  
    return ngx_http_post_request(sr, NULL);

}


이 함 수 는 키 요청 데이터 구 조 를 만 들 고 아버지 가 요청 한 링크 에 걸 렸 을 뿐 처리 하지 않 은 것 같 습 니 다. 그 렇 죠?
그럼 이 요청 은 어디서 처 리 됩 니까?우 리 는 위의 호출 스 택 을 돌 이 켜 보고 마지막 으로 xxxx 를 사 용 했 습 니 다. 이 부분 은 협의 와 관련 이 있 기 때문에 HTTP 요청 에 대해 ngx_http_subrequest 입 니 다. HTTP 2 요청 에 대해 ngx_http_process_request_headers 입 니 다. 이 두 함 수 는 모두 하나 ngx_http_v2_run_request 함수 가 있 습 니 다.이 함 수 는 순환 처리 전에 생 성 된 하위 요청, 즉 처 리 된 sr 입 니 다.
ngx_http_run_posted_requests(ngx_connection_t *c)
{
    ngx_http_request_t         *r;
    ngx_http_posted_request_t  *pr;

    for ( ;; ) {

        if (c->destroyed) {
            return;
        }

        r = c->data;
        pr = r->main->posted_requests;

        if (pr == NULL) {
            return;
        }

        r->main->posted_requests = pr->next;

        #  r  ngx_http_subrequest     sr
        r = pr->request;

        ngx_http_set_log_request(c->log, r);

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http posted request: \"%V?%V\"", &r->uri, &r->args);

        #write_event_handler    ngx_http_handler,       ,     ,                  Nginx     。
        r->write_event_handler(r);
    }
}

그래서 여기까지, 우 리 는 하위 요청 이 처리 되 는 것 을 보 았 습 니 다. 처리 입 구 는 ngx 입 니 다.http_handler, 즉 하위 요청 은 처음부터 처 리 됩 니 다.그렇다면 자식 이 처 리 를 요청 한 후, 어떻게 아버 지 를 깨 워 자신의 남 은 단 계 를 계속 처리 해 달라 고 요청 합 니까?이것 은 HTTP 요청 방출 단계 와 관련 되 어 있 습 니 다. 모든 HTTP 요청 은 처리 가 끝 난 후에 (즉 응답 전송 완료) 함수 로 실 행 됩 니 다.
ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{

    ......
    
    #       ,  auth_request  ,   `ngx_http_auth_request_handler`      `ngx_http_auth_request_done`,     。
    if (r != r->main && r->post_subrequest) {
        rc = r->post_subrequest->handler(r, r->post_subrequest->data, rc);
    }

    ......
    
    #        ,    r r->main    ,     r   ,r->main    
    if (r != r->main) {
        clcf = ngx_http_get_module_loc_conf(r, ngx_http_core_module);

        if (r->background) {

        }

        #      
        pr = r->parent;

        if (r == c->data) {
            #          
            r->main->count--;

            if (!r->logged) {
                if (clcf->log_subrequest) {
                    ngx_http_log_request(r);
                }

                r->logged = 1;

            } else {
                ngx_log_error(NGX_LOG_ALERT, c->log, 0,
                              "subrequest: \"%V?%V\" logged again",
                              &r->uri, &r->args);
            }

            r->done = 1;

            if (pr->postponed && pr->postponed->request == r) {
                pr->postponed = pr->postponed->next;
            }

            c->data = pr;

        } else {

        }

        #  pr    ,        post    ,           
        if (ngx_http_post_request(pr, NULL) != NGX_OK) {
            r->main->count++;
            ngx_http_terminate_request(r, 0);
            return;
        }

        ngx_log_debug2(NGX_LOG_DEBUG_HTTP, c->log, 0,
                       "http wake parent request: \"%V?%V\"",
                       &pr->uri, &pr->args);

        return;
    }

}

주의 하 세 요. 이때 ngx_http_run_posted_requests 함수 가 우리 ngx_http_post_request 에 있 기 때문에 위 에서 아버 지 를 자신 에 게 호출 ngx_http_finalize_request 하 라 고 요청 합 니 다. 그러면 바깥쪽 이 순환 할 때 아버지 가 요청 한 handler 를 실행 할 수 있 습 니 다.이 논 리 는 하위 요청 과 매우 비슷 하 며, 하위 요청 도 자신 을 post 대기 열 에 넣 습 니 다.
이렇게 되면 아버지의 요청 이 회 복 됩 니 다.access 단계 에서 이 어 집 행 됩 니 다.
static ngx_int_t
ngx_http_auth_request_handler(ngx_http_request_t *r)
{

    #            ,         
    
    if (ctx != NULL) {
        if (!ctx->done) {
            return NGX_AGAIN;
        }

        /*
         * as soon as we are done - explicitly set variables to make
         * sure they will be available after internal redirects
         */

        if (ngx_http_auth_request_set_variables(r, arcf, ctx) != NGX_OK) {
            return NGX_ERROR;
        }

        #      , 200 ,      ,  2xx     ,      NGX_OK,        ,        
        /* return appropriate status */

        if (ctx->status == NGX_HTTP_FORBIDDEN) {
            return ctx->status;
        }

        if (ctx->status == NGX_HTTP_UNAUTHORIZED) {
            sr = ctx->subrequest;

            h = sr->headers_out.www_authenticate;

            if (!h && sr->upstream) {
                h = sr->upstream->headers_in.www_authenticate;
            }

            if (h) {
                ho = ngx_list_push(&r->headers_out.headers);
                if (ho == NULL) {
                    return NGX_ERROR;
                }

                *ho = *h;

                r->headers_out.www_authenticate = ho;
            }

            return ctx->status;
        }

        if (ctx->status >= NGX_HTTP_OK
            && ctx->status < NGX_HTTP_SPECIAL_RESPONSE)
        {
            return NGX_OK;
        }

        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "auth request unexpected status: %ui", ctx->status);

        return NGX_HTTP_INTERNAL_SERVER_ERROR;
    }
}

물론 실제 구 덩이 는 많이 좋아 졌 습 니 다. nginx 코드 를 찾 아 보 세 요. 여기저기 r - > main 의 여러 가지 판단 이 여기저기 서 하위 요청 여 부 를 판단 해 야 합 니 다.

좋은 웹페이지 즐겨찾기