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;
}
}