nginx-slice模块源码解析

nginx slice 源码解析

过程总结

客户端向nginx请求一个10M文件,nginx进行4m的切片,整个过程大概是

  • 1.客户端向nginx请求10M
  • 2.nginx发起第一个切片(主请求)请求range:0-4194303
  • 3.第一个切片(主请求)请求的内容全部发给客户端后,在slice模块的body_filter发起第二个切片(子请求),请求range: 4194304-8388607
  • 4.第二个切片(子请求)请求的内容完全发完給客户端后,切回主请求
  • 5.主请求在slice模块的body_filter发起第三个切片(子请求),请求range: 8388608-12582911
  • 6.第三个切片(子请求)请求的内容(8388608-10485759)完全发完給客户端后,切回主请求
  • 7.主请求在slice模块的body_filter判断已经将10M的文件发給客户端,不再进行slce的模块处理

源码分析

请求一个2M文件后,发生的流程

# 配置
location / {
    slice             1m;
    proxy_set_header  Range $slice_range
    proxy_buffering off;
    proxy_pass http://testupstream;
}

# curl  "http://localhost/2M.dat" -voa

1.在upstream注册读写事件

nginx 接收到请求后一直进入到ngx_http_proxy_handler后返回NGX_DONE

ngx_http_process_request_headers ->
ngx_http_core_run_phases ->
ngx_http_core_content_phase ->
ngx_http_proxy_handler

static ngx_int_t
ngx_http_proxy_handler(ngx_http_request_t *r)
{
    ...
    rc = ngx_http_read_client_request_body(r, ngx_http_upstream_init);

    if (rc >= NGX_HTTP_SPECIAL_RESPONSE) {
        return rc;
    }

    return NGX_DONE;
    #在ngx_http_upstream_init中初始化包体。注册读写事件后,最终在这里返回
}

遇到NGX_DONE后,在ngx_http_process_request_headers中执行ngx_http_run_posted_requests

static void
ngx_http_process_request_headers(ngx_event_t *rev)
{
    ...
        if (rc == NGX_HTTP_PARSE_HEADER_DONE) {
            ...
            ngx_http_process_request(r);
            break;
        }
        
    ngx_http_run_posted_requests(r)  #执行到这里      
}

进入到ngx_http_run_posted_requests时, 此时r->main->posted_requests 为空,不执行其它操作

2.初始化$slice_range变量,主请求向上游请求第一部分range

range头部 Range: bytes=0-1048575

在第1步中的初始化upstream包体时,会执行配置中的指令,去获取$slice_range 的值

proxy_set_header  Range $slice_range

源码src/http/modules/ngx_http_slice_filter_module.c中$slice_range 变量会调用函数ngx_http_slice_range_variable



static ngx_str_t  ngx_http_slice_range_name = ngx_string("slice_range");

static ngx_int_t
ngx_http_slice_add_variables(ngx_conf_t *cf)
{
    ngx_http_variable_t  *var;

    var = ngx_http_add_variable(cf, &ngx_http_slice_range_name, 0);
    if (var == NULL) {
        return NGX_ERROR;
    }

    var->get_handler = ngx_http_slice_range_variable;

    return NGX_OK;
}

static ngx_int_t
ngx_http_slice_range_variable(ngx_http_request_t *r,
    ngx_http_variable_value_t *v, uintptr_t data)
{
    ...
    ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);

    if (ctx == NULL) {
    # 进入到这里,创建ctx
        if (r != r->main || r->headers_out.status) {
            v->not_found = 1;
            return NGX_OK;
        }
    }
    ...
    # ctx->start = 1048576
    ctx->start = slcf->size * (ngx_http_slice_get_start(r) / slcf->size);

    # ctx->range 设为bytes=0-1048575
    ctx->range.data = p;
    ctx->range.len = ngx_sprintf(p, "bytes=%O-%O", ctx->start,
                                    ctx->start + (off_t) slcf->size - 1)
}

3.主请求接收上游响应的range,进入ngx_http_slice_header_filter

在第1步upstream注册读写事件后,写事件触发,发送http头部

ngx_http_upstream_handler ->
ngx_http_upstream_process_header ->
ngx_http_upstream_send_response -> 
ngx_http_slice_header_filter>

static ngx_int_t
ngx_http_slice_header_filter(ngx_http_request_t *r)
{
    ...
    ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);
    if (ctx == NULL) {
        # 如果没有配置proxy_set_header  Range $slice_range,
        # 即没有使用$slice_range,即没有创建ctx,最终会走到这里
        return ngx_http_next_header_filter(r);
    }

    if (r->headers_out.status != NGX_HTTP_PARTIAL_CONTENT) {
        # 主请求发range 若收到不为206,则slice模块不进行处理
        if (r == r->main) {
            ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
            return ngx_http_next_header_filter(r);
        }
        ...
        return NGX_ERROR;
    }

    ...
    # 解析Content-Range: bytes 0-1048575/2097152
    # cr.start=0, cr.end=1048576, cr.complete_length = 2097152
    if (ngx_http_slice_parse_content_range(r, &cr) != NGX_OK) {
        ngx_log_error(NGX_LOG_ERR, r->connection->log, 0,
                      "invalid range in slice response");
        return NGX_ERROR;
    }

    ...

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);

    end = ngx_min(cr.start + (off_t) slcf->size, cr.complete_length);

    ...
    #这里ctx-start 由0设置成1048576, 为下一个切片做准备
    ctx->start = end;
    ctx->active = 1;

    r->headers_out.status = NGX_HTTP_OK;
    r->headers_out.status_line.len = 0;
    r->headers_out.content_length_n = cr.complete_length;
    r->headers_out.content_offset = cr.start;

    rc = ngx_http_next_header_filter(r);

    if (r != r->main) {
        return rc;
    }

    # 只有主请求的header_filter会走到这里
    # 设置了ctx->end = 2097152,后面ctx->end值保持不变
    r->preserve_body = 1;

    if (r->headers_out.status == NGX_HTTP_PARTIAL_CONTENT) {
        if (ctx->start + (off_t) slcf->size <= r->headers_out.content_offset) {
            ctx->start = slcf->size
                         * (r->headers_out.content_offset / slcf->size);
        }

        ctx->end = r->headers_out.content_offset
                   + r->headers_out.content_length_n;

    } else {
        ctx->end = cr.complete_length;
    }
    
    return rc;

4.主请求进入ngx_http_slice_body_filter

static ngx_int_t
ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    # 没有ctx或不是主请求直接返回
    ctx = ngx_http_get_module_ctx(r, ngx_http_slice_filter_module);

    if (ctx == NULL || r != r->main) {
        return ngx_http_next_body_filter(r, in);
    }

    for (cl = in; cl; cl = cl->next) {
        if (cl->buf->last_buf) {
            cl->buf->last_buf = 0; 
            cl->buf->last_in_chain = 1;
            cl->buf->sync = 1;
            # 注意这个last
            ctx->last = 1;
        }
    }

    rc = ngx_http_next_body_filter(r, in);

    # 内容没有发送完直接返回
    if (rc == NGX_ERROR || !ctx->last) {
        return rc;
    }
    
    ...

    if (ctx->start >= ctx->end) {
    # 注意这里,完整文件发完后最终会走到这里
        ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
        ngx_http_send_special(r, NGX_HTTP_LAST);
        return rc;
    }
    ...
    # 这里创建子请求(第二个切片),在主请求完全发完后执行
    if (ngx_http_subrequest(r, &r->uri, &r->args, &ctx->sr, NULL,
                            NGX_HTTP_SUBREQUEST_CLONE)
        != NGX_OK)
    {
        return NGX_ERROR;
    }

    ngx_http_set_ctx(ctx->sr, ctx, ngx_http_slice_filter_module);

    slcf = ngx_http_get_module_loc_conf(r, ngx_http_slice_filter_module);

    # 设置ctx->range为 1048576-2097151,为第二个切片准备
    # 最终会在第二个切片的proxy_set_header  Range $slice_range;中返回
    ctx->range.len = ngx_sprintf(ctx->range.data, "bytes=%O-%O", ctx->start,
                                 ctx->start + (off_t) slcf->size - 1)
                     - ctx->range.data;

    ...
    
    return rc;
}

5.主请求进入结束,设置写事件为ngx_http_writer,接着子请求启动

ngx_http_upstream_handler->
ngx_http_upstream_finalize_request->
ngx_http_finalize_request

void
ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
    ...
    if (r->buffered || c->buffered || r->postponed) {
        # 主请求进入这里,设置写事件为ngx_http_writer
        if (ngx_http_set_write_handler(r) != NGX_OK) {
            ngx_http_terminate_request(r, 0);
        }

        return;
    }
}

static void
ngx_http_upstream_handler(ngx_event_t *ev)
{
    ...
    if (ev->write) {
        u->write_event_handler(r, u);

    } else {
        # 主请求执行完这里
        u->read_event_handler(r, u);
    }

    # 此时r->main->posted_requests有挂载子请求了,执行主请求下的子请求
    ngx_http_run_posted_requests(c);
}

6.子请求即第二个切片执行

子请求执行的是ngx_http_core_run_phases过程,和主请求走的(2,3,4步骤)几乎一样,最后进入

void
ngx_http_finalize_request(ngx_http_request_t *r, ngx_int_t rc)
{
    ...
    子请求会走到这里
    if (r != r->main) {
        ...

        pr = r->parent;

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

            ...

            r->done = 1;

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

            将c的data数据设置为主请求
            c->data = pr;
        }
         
         #将父请求加入posted_request队尾,获得一次运行机会,这样主请求就会加入到posted_requests
         if (ngx_http_post_request(pr, NULL) != NGX_OK) {
            ...
            return;
        }   
         ...
        return;
    }
}

static void
ngx_http_upstream_handler(ngx_event_t *ev)
{
    ...
    if (ev->write) {
        u->write_event_handler(r, u);

    } else {
        # 子请求从这里进入ngx_http_finalize_request
        u->read_event_handler(r, u);
    }

    # 此时是子请求,但是子请求下的入posted_request挂载了父请求(主请求),
    # 于是在ngx_http_run_posted_requests函数里执行主请求的写事件,ngx_http_writer
    ngx_http_run_posted_requests(c);
}

7.主请求执行ngx_http_writer

在执行主请求的ngx_http_writer时,可能客户端已经收到完整包体,请求已经结束了,那么主请求接着只是告诉slice模块事情已经结束了,然后看看要不要设置keep_alive 之类的

static void
ngx_http_writer(ngx_http_request_t *r)
{
    ...
    # 从这里进去,直到ngx_http_slice_body_filter
    rc = ngx_http_output_filter(r, NULL);
}

static ngx_int_t
ngx_http_slice_body_filter(ngx_http_request_t *r, ngx_chain_t *in)
{
    ...
    
    if (ctx->start >= ctx->end) {
        # 执行到这里面
        ngx_http_set_ctx(r, NULL, ngx_http_slice_filter_module);
        ngx_http_send_special(r, NGX_HTTP_LAST);
        return rc;
    }
}

转载于:https://my.oschina.net/u/4001231/blog/3018013

你可能感兴趣的:(nginx-slice模块源码解析)