nginx 의 output chain 처리 (2)

18175 단어 nginxBlog
더 읽 기
이 어 지난번 분석 이 계속 되 었 습 니 다. 이번 에는 filter 체인 에서 가장 관건 적 인 모듈 을 살 펴 보 겠 습 니 다. 그것 이 바로 ngx 입 니 다.http_copy_filter_module 모듈, 이 filter 는 복사 해 야 할 buf (파일 이나 메모리) 를 다시 복사 해서 나머지 body filter 에 보 내 는 데 사 용 됩 니 다. 여기 서 중요 한 부분 이 있 습 니 다. 바로 여기 서 nginx 의 나머지 body filter 가 여러 번 호출 될 수 있 습 니 다. 이 다음 에 제 가 일일이 설명 하 겠 습 니 다.초기 화 함수 먼저 보기:
static ngx_int_t
ngx_http_copy_filter_init(ngx_conf_t *cf)
{
    ngx_http_next_filter = ngx_http_top_body_filter;
    ngx_http_top_body_filter = ngx_http_copy_filter;

    return NGX_OK;
}

이 필 터 는 body filter 만 있 고 header filter 가 없습니다. 즉, body filter 만 이 필 터 를 사용 할 수 있 습 니 다.
그리고 이 모듈 에 대응 하 는 명령 도 있 습 니 다. 바로 output 입 니 다.buffers, 이 명령 은 conf 의 bufs 에 저 장 됩 니 다:
typedef struct {
    ngx_bufs_t  bufs;
} ngx_http_copy_filter_conf_t;

nginx 설정 파일 에 있 는 모든 bufs 의 형식 이 같 고 개수 + 크기 임 을 알 아야 합 니 다.이 값 은 다음 에 filter 코드 를 분석 할 때 다시 볼 수 있 습 니 다.
그리고 대응 하 는 merge 방법 을 살 펴 보고 이 bufs 의 기본 값 이 얼마 인지 보 세 요.
static char *
ngx_http_copy_filter_merge_conf(ngx_conf_t *cf, void *parent, void *child)
{
    ngx_http_copy_filter_conf_t *prev = parent;
    ngx_http_copy_filter_conf_t *conf = child;

//   1 buf,   32768  
    ngx_conf_merge_bufs_value(conf->bufs, prev->bufs, 1, 32768);

    return NULL;
}

copy filter 에 header filter 가 없 기 때문에 context 초기 화 도 body filter 에 놓 여 있 습 니 다. ctx 는 ngx 입 니 다.output_chain_ctx_t, 왜 이름 이 outputchain 은 copy filter 의 주요 논리 적 처리 가 모두 ngx 에 놓 여 있 기 때 문 입 니 다.output_chain 의 이 모듈 은 http 디 렉 터 리 가 아 닌 core 디 렉 터 리 에 저 장 된 것 을 볼 수 있 습 니 다.
다음은 이 context 의 구 조 를 살 펴 보 겠 습 니 다.
typedef struct {
//     buf
    ngx_buf_t                   *buf;
//        chain
    ngx_chain_t                 *in;
//          chain,       
    ngx_chain_t                 *free;
//        chain
    ngx_chain_t                 *busy;

//sendfile  
    unsigned                     sendfile:1;
//directio  
    unsigned                     directio:1;
#if (NGX_HAVE_ALIGNED_DIRECTIO)
    unsigned                     unaligned:1;
#endif
//            (  sendfile  ,           ,           ,           )
    unsigned                     need_in_memory:1;
//     buf    ,              ,             。
    unsigned                     need_in_temp:1;

    ngx_pool_t                  *pool;
//  allocated   
    ngx_int_t                    allocated;
//   bufs   ,       loc conf    bufs
    ngx_bufs_t                   bufs;
//          (  upstream    output_chain)
    ngx_buf_tag_t                tag;

//      ngx_http_next_filter,       filter 
    ngx_output_chain_filter_pt   output_filter;
//  filter    ,      upstream    output_chain
    void                        *filter_ctx;
} ngx_output_chain_ctx_t;

다음은 구체 적 인 함수 의 실현 을 보면 context 의 이 도 메 인 들 의 뜻 을 잘 이해 할 수 있 습 니 다.
보 러 copyfilter 의 body filter.
static ngx_int_t
ngx_http_copy_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ngx_int_t                     rc;
    ngx_connection_t             *c;
    ngx_output_chain_ctx_t       *ctx;
    ngx_http_copy_filter_conf_t  *conf;

    c = r->connection;

//  ctx
    ctx = ngx_http_get_module_ctx(r, ngx_http_copy_filter_module);

//    ,        ctx
    if (ctx == NULL) {
        conf = ngx_http_get_module_loc_conf(r, ngx_http_copy_filter_module);

        ctx = ngx_pcalloc(r->pool, sizeof(ngx_output_chain_ctx_t));
        if (ctx == NULL) {
            return NGX_ERROR;
        }

        ngx_http_set_ctx(r, ctx, ngx_http_copy_filter_module);

//      
        ctx->sendfile = c->sendfile;
//         request  filter_need_in_memory  ,ctx         
        ctx->need_in_memory = r->main_filter_need_in_memory
                              || r->filter_need_in_memory;
//     
        ctx->need_in_temp = r->filter_need_temporary;

        ctx->pool = r->pool;
        ctx->bufs = conf->bufs;
        ctx->tag = (ngx_buf_tag_t) &ngx_http_copy_filter_module;
//    output_filter  body filter next
        ctx->output_filter = (ngx_output_chain_filter_pt) ngx_http_next_filter;
//  filter ctx      
        ctx->filter_ctx = r;

        r->request_output = 1;
    }
//      ,       。
    rc = ngx_output_chain(ctx, in);
......................................................

    return rc;
}

그리고 ngxoutput_chain 이 함수 입 니 다. 여기 nginx filter 의 주요 논 리 는 모두 이 함수 안에 있 습 니 다.다음은 이 함수 의 원형 이다.
ngx_int_t
ngx_output_chain(ngx_output_chain_ctx_t *ctx, ngx_chain_t *in)

그 다음 에 우 리 는 그 코드 를 단계별 로 보 겠 습 니 다. 아래 코드 는 short path 라 고 할 수 있 습 니 다. 즉, 우 리 는 모든 in chain 이 복사 할 필요 가 없 을 때 우 리 는 output 를 직접 호출 할 수 있 습 니 다.filter 는 남 은 filter 에 맡 겨 처리 합 니 다.
  
 if (ctx->in == NULL && ctx->busy == NULL) {

//           
        /*
         * the short path for the case when the ctx->in and ctx->busy chains
         * are empty, the incoming chain is empty too or has the single buf
         * that does not require the copy
         */

        if (in == NULL) {
            return ctx->output_filter(ctx->filter_ctx, in);
        }

//        chain,    buf     
        if (in->next == NULL
#if (NGX_SENDFILE_LIMIT)
            && !(in->buf->in_file && in->buf->file_last > NGX_SENDFILE_LIMIT)
#endif
            && ngx_output_chain_as_is(ctx, in->buf))
        {
            return ctx->output_filter(ctx->filter_ctx, in);
        }
    }

위 에서 우 리 는 함수 ngx 를 보 았 다.output_chain_as_is, 이 함 수 는 매우 관건 적 입 니 다. 다음은 다시 호출 될 것 입 니 다. 이 함 수 는 주로 buf 를 복사 해 야 하 는 지 여 부 를 판단 하 는 데 사 용 됩 니 다.1 을 되 돌려 복사 할 필요 가 없다 는 뜻 입 니 다. 그렇지 않 으 면 복사 가 필요 합 니 다.
static ngx_inline ngx_int_t
ngx_output_chain_as_is(ngx_output_chain_ctx_t *ctx, ngx_buf_t *buf)
{
    ngx_uint_t  sendfile;

//   specialbuf,     1,       
    if (ngx_buf_special(buf)) {
        return 1;
    }

//  buf    ,     directio  ,    buf
    if (buf->in_file && buf->file->directio) {
        return 0;
    }

//sendfile  
    sendfile = ctx->sendfile;

#if (NGX_SENDFILE_LIMIT)
//  pos  sendfile   ,     0
    if (buf->in_file && buf->file_pos >= NGX_SENDFILE_LIMIT) {
        sendfile = 0;
    }

#endif

    if (!sendfile) {
//    buf     ,             。
        if (!ngx_buf_in_memory(buf)) {
            return 0;
        }
//    in_file 0.
        buf->in_file = 0;
    }

//            ,       ,    0,      
    if (ctx->need_in_memory && !ngx_buf_in_memory(buf)) {
        return 0;
    }

//          ,          mmap ,   0.
    if (ctx->need_in_temp && (buf->memory || buf->mmap)) {
        return 0;
    }

    return 1;
}

위 에 두 개의 표시 가 있 습 니 다. 하 나 는 need 입 니 다.in_memory, 이것 은 주로 우리 가 sendfile 을 사용 할 때 nginx 는 요청 파일 을 메모리 에 복사 하지 않 습 니 다. 가끔 은 파일 의 내용 을 조작 해 야 합 니 다. 이때 우 리 는 이 표 시 를 설정 해 야 합 니 다. (설정 방법 앞에서 소개 가 초기 화 되 었 습 니 다) 그리고 우 리 는 body filter 에서 내용 을 조작 할 수 있 습 니 다.
두 번 째 는 needin_temp, 이것 은 메모리 에 존재 하 는 buf 를 복사 하 는 데 사 용 됩 니 다. 여기 서 유용 한 모듈 은 charset, 즉 디 코딩 filter 가 있 습 니 다.
그리고 이 부분 은 in chain 을 ctx - > in 으로 복사 하 는 끝 입 니 다. ngx 를 호출 합 니 다.output_chain_add_copy 는 add copy 를 진행 합 니 다. 이 함 수 는 비교적 간단 합 니 다. 여 기 는 분석 하지 않 습 니 다. 그러나 주의해 야 할 것 은 buf 가 파일 에 존재 하고 filepos 가 sendfile limit 을 초과 하면 buf 를 두 개의 buf 로 자 른 다음 두 개의 chain 에 저장 하고 최종 적 으로 연결 합 니 다.
if (in) {
//   ctx->in .
        if (ngx_output_chain_add_copy(ctx->pool, &ctx->in, in) == NGX_ERROR) {
            return NGX_ERROR;
        }
    }


그 다음은 주요 논리 처리 단계 다.여기 서 nginx 는 매우 교묘 하고 복잡 하 게 만 들 었 습 니 다. 먼저 chain 의 재 활용, 그 다음 에 buf 의 재 활용 입 니 다.
체인 의 중용 부터 살 펴 보 자.관건 적 인 몇 가지 구조 와 도 메 인, ctx 의 free, busy 및 ctx - > pool 의 chain 도 메 인.
그 중에서 발송 이 끝나 지 않 은 chain 은 busy 에 넣 고 발송 이 끝 난 것 은 free 에 넣 고 마지막 에 호출 합 니 다.  ngx_free_chain 은 free chain 을 pool - > chain 에 넣 고 ngxalloc_chain_링크 에 pool - > chain 에 chain 이 존재 한다 면 malloc 를 사용 하지 않 고 pool - > chain 으로 돌아 갑 니 다. 관련 코드 를 보 겠 습 니 다.

//  cl pool->chain 
#define ngx_free_chain(pool, cl)                                             \
    cl->next = pool->chain;                                                  \
    pool->chain = cl

ngx_chain_t *
ngx_alloc_chain_link(ngx_pool_t *pool)
{
    ngx_chain_t  *cl;

    cl = pool->chain;
//  cl  ,     cl
    if (cl) {
        pool->chain = cl->next;
        return cl;
    }
//    malloc chain
    cl = ngx_palloc(pool, sizeof(ngx_chain_t));
    if (cl == NULL) {
        return NULL;
    }

    return cl;
}

그 다음 에 buf 의 재 활용 입 니 다. 엄격 한 의미 에서 buf 의 재 활용 은 free 의 chain 에서 얻 은 것 입 니 다. free 의 buf 가 재 활용 되면 이 buf 에 대응 하 는 chain 은 ctx - > pool 에 연결 되 고 이 chain 은 재 활용 됩 니 다.
즉, buf 의 재 활용 은 첫 번 째 로 고려 되 었 습 니 다. 이 chain 의 buf 가 재 활용 되 지 않 아 도 된다 는 것 이 확실 할 때 만 chain 은 ctx - > pool 에 연결 되 어 재 활용 되 었 습 니 다.
또 하 나 는 ctx 의 allocated 도 메 인 입 니 다. 이 도 메 인 은 현재 컨 텍스트 에 몇 개의 buf 가 할당 되 었 는 지 표시 합 니 다. blog 는 처음에 output 라 고 언급 했 습 니 다.buffer 명령 은 output 의 buf 크기 와 buf 의 개 수 를 설정 하 는 데 사 용 됩 니 다.allocated 는 output 보다buffer 가 크 면 존재 하 는 buf 를 먼저 보 낸 다음 에 buf 를 다시 분배 할 수 있 습 니 다.
코드 를 보면 위 에서 말 한 중용 과 buf 의 제 어 를 보면 코드 안에서 비교적 뚜렷하게 볼 수 있다.이 코드 는 우리 가 단락 을 나 누 어 볼 때 다음 단락 은 주로 buf 를 복사 하기 전에 하 는 작업 입 니 다. 예 를 들 어 복사 여 부 를 판단 하고 buf 에 게 메모 리 를 나 누 어 주 는 등 입 니 다.

//out          chain,        filter   chain
 out = NULL;
//last_out out     chain
    last_out = &out;
    last = NGX_NONE;

for ( ;; ) {

//    chain
        while (ctx->in) {

//    chain buf  
            bsize = ngx_buf_size(ctx->in->buf);

//  bsize 0 buf
            if (bsize == 0 && !ngx_buf_special(ctx->in->buf)) {
                ngx_debug_point();

                ctx->in = ctx->in->next;

                continue;
            }

//        buf
            if (ngx_output_chain_as_is(ctx, ctx->in->buf)) {

                /* move the chain link to the output chain */
//       ,     chain out,      
                cl = ctx->in;
                ctx->in = cl->next;

                *last_out = cl;
                last_out = &cl->next;
                cl->next = NULL;

                continue;
            }

//    ,        buf,  buf        ctx->buf ,       ctx->buf    
            if (ctx->buf == NULL) {

//    ,   buf,     ,          directio  ,        NGX_DECLINED (               )。
                rc = ngx_output_chain_align_file_buf(ctx, bsize);

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

//      ,        
                if (rc != NGX_OK) {

//    buf,   free        buf
                    if (ctx->free) {

                        /* get the free buf */
//  free buf
                        cl = ctx->free;
                        ctx->buf = cl->buf;
                        ctx->free = cl->next;
//     chain   ctx->poll ,   chain   .
                        ngx_free_chain(ctx->pool, cl);

                    } else if (out || ctx->allocated == ctx->bufs.num) {
//      buf     ,     ,       buf.        out    ,nginx     ,    out,         ,        nginx     
                        break;

                    } else if (ngx_output_chain_get_buf(ctx, bsize) != NGX_OK) {
//         ,     buf.             
                        return NGX_ERROR;
                    }
                }
            }
............................................................
    }

위의 코드 를 분석 할 때 매우 중요 한 함수 가 있 는데 그것 이 바로 ngx 입 니 다.output_chain_get_buf, 이 함 수 는 다시 사용 할 buf 가 없 을 때 buf 를 분배 하 는 데 사 용 됩 니 다.
여기 서 주의해 야 할 것 은 현재 buf 가 마지막 chain 에 있 으 면 특별한 처리 가 있 습 니 다.여기 서 특수 처 리 는 두 곳 이 있 습 니 다. 하 나 는 buf 의 recycled 도 메 인 이 고 하 나 는 분 배 될 buf 의 크기 입 니 다.
먼저 recycled 도 메 인 을 말 합 니 다. 이 도 메 인 은 현재 buf 가 회수 되 어야 한 다 는 것 을 표시 합 니 다.그리고 우 리 는 nginx 의 일반적인 상황 에서 (예 를 들 어 last buf 가 아 닌) 일부 buf 를 캐 시 한 다음 에 보 내 는 것 (기본 값 은 1460 바이트) 을 알 고 있 습 니 다. recycled 를 설정 하면 우 리 는 buf 를 캐 시 하지 않 습 니 다. 즉, 가능 한 한 보 낸 다음 에 재 활용 할 수 있 도록 합 니 다.
따라서 마지막 buf 라면 일반적으로 우 리 는 recycled 도 메 인 을 설정 할 필요 가 없습니다. 그렇지 않 으 면 recycled 도 메 인 을 설정 해 야 합 니 다.마지막 buf 가 아니라면 buf 를 다시 사용 해 야 할 수도 있 고, buf 는 보 내야 만 다시 사용 할 수 있 습 니 다.
그리고 사이즈.여기 에는 두 개의 크기 가 있 습 니 다. 하 나 는 우리 가 복사 해 야 할 buf 의 크기 이 고 하 나 는 nginx. conf 에 설 치 된 size 입 니 다.마지막 buf 가 아니라면, 우리 가 설정 한 buf 의 size 크기 만 할당 하면 됩 니 다.마지막 buf 라면 처리 가 다 르 고 아래 코드 를 볼 수 있 습 니 다.

static ngx_int_t
ngx_output_chain_get_buf(ngx_output_chain_ctx_t *ctx, off_t bsize)
{
    size_t       size;
    ngx_buf_t   *b, *in;
    ngx_uint_t   recycled;

    in = ctx->in->buf;
//         buf,          nginx.conf    size
    size = ctx->bufs.size;
//     recycled .
    recycled = 1;
//     buf       chain   。         。
    if (in->last_in_chain) {
//       ,      .
        if (bsize < (off_t) size) {

            /*
             * allocate a small temp buf for a small last buf
             * or its small last part
             */
            size = (size_t) bsize;
            recycled = 0;

        } else if (!ctx->directio
                   && ctx->bufs.num == 1
                   && (bsize < (off_t) (size + size / 4)))
        {
            /*
             * allocate a temp buf that equals to a last buf,
             * if there is no directio, the last buf size is lesser
             * than 1.25 of bufs.size and the temp buf is single
             */

            size = (size_t) bsize;
            recycled = 0;
        }
    }
//    buf  .
    b = ngx_calloc_buf(ctx->pool);
    if (b == NULL) {
        return NGX_ERROR;
    }

    if (ctx->directio) {
//directio    

        b->start = ngx_pmemalign(ctx->pool, size, NGX_DIRECTIO_BLOCK);
        if (b->start == NULL) {
            return NGX_ERROR;
        }

    } else {
//          .
        b->start = ngx_palloc(ctx->pool, size);
        if (b->start == NULL) {
            return NGX_ERROR;
        }
    }

    b->pos = b->start;
    b->last = b->start;
    b->end = b->last + size;
//  temporary.
    b->temporary = 1;
    b->tag = ctx->tag;
    b->recycled = recycled;

    ctx->buf = b;
//  allocated,           1.
    ctx->allocated++;

    return NGX_OK;
}

그리고 이 부분 은 buf 를 복사 한 다음 filter 체인 을 호출 하여 보 내 는 것 입 니 다.

//  buf.
rc = ngx_output_chain_copy_buf(ctx);

            if (rc == NGX_ERROR) {
                return rc;
            }
//    AGAIn,            .
            if (rc == NGX_AGAIN) {
                if (out) {
                    break;
                }

                return rc;
            }

            /* delete the completed buf from the ctx->in chain */
//  ctx->in      buf      buf
            if (ngx_buf_size(ctx->in->buf) == 0) {
                ctx->in = ctx->in->next;
            }

            cl = ngx_alloc_chain_link(ctx->pool);
            if (cl == NULL) {
                return NGX_ERROR;
            }
//  chain out.
            cl->buf = ctx->buf;
            cl->next = NULL;
            *last_out = cl;
            last_out = &cl->next;
            ctx->buf = NULL;
        }

        if (out == NULL && last != NGX_NONE) {

            if (ctx->in) {
                return NGX_AGAIN;
            }

            return last;
        }
//  filter 
        last = ctx->output_filter(ctx->filter_ctx, out);

        if (last == NGX_ERROR || last == NGX_DONE) {
            return last;
        }
//update chain,           chain   free,         busy .
        ngx_chain_update_chains(&ctx->free, &ctx->busy, &out, ctx->tag);
        last_out = &out;

ngx_chain_update_chains 이 함 수 는 제 가 예전 에 블 로 그 를 분석 한 적 이 있 습 니 다. 알 고 싶 은 것 은 제 앞의 블 로 그 를 볼 수 있 습 니 다.

좋은 웹페이지 즐겨찾기