您的位置:首页 > 其它

请求处理

2016-04-19 19:59 232 查看
Nginx对于它监听到的每个客户端的连接,都会把它的读事件的handler设置为ngx_http_init_request,它是处理请求的开端,处理请求的过程主要就是解析http请求,再根据请求生成响应

客户端连接来的时候调用Ngx_event_accept,再调用ngx_http_init_connection初始化连接,再把读事件的handler设置为ngx_http_init_request,当客户端第一次发送请求的时候调用该函数初始化请求,接着调用ngx_http_process_request_line处理request_line,再调用ngx_http_parse_request_line处理request_line,再调用ngx_http_process_request_headers处理request_headers,再调用ngx_http_parse_request_line解析header,再调用Ngx_http_process_request_header根据header做一些处理,接下来处理请求,调用ngx_http_process_request设置读写事件的handler,ngx_http_handler设置请求的keepalive属性以及初始化phase_handler,接着调用ngx_http_core_run_phases调用所有phase_handler

1.ngx_http_init_request:

开始的代码主要是创建一个新的连接,并且会从事件的data字段获取对应得连接,如果该事件超时,直接销毁该请求,如果是之前的accept连接上过来的请求,那么首先会给他分配一个新的ngx_http_request_t结构体,这个结构用来保存该请求所有的信息,分配好以后把该请求的引用保存在连接的hc的request字段中,目的是为了复用该请求

在nginx配置文件中可以设置多个监听在不同端口和地址的虚拟服务器,另外还根据域名(server_name指令设置虚拟服务器的域名)来区分监听在相同端口和地址的虚拟服务器,每个虚拟服务器可以有不同的配置内容,这些配置内容决定了nginx在接收到请求以后如何处理请求,在找到相应的配重块以后相应的配置保存在该请求对应得ngx_http_request_t结构体中,注意这里根据端口和地址找到的默认配置只是临时使用,而最终还是会根据域名确定真正的配置块。

if (ngx_connection_local_sockaddr(c, NULL, 0) != NGX_OK)

//获取连接c的socket绑定的本地地址

根据连接c的socket地址匹配port->addrs,找到对应得addr:port,最后把找到的地址赋值给addr_conf,

r->virtual_names = addr_conf->virtual_names;

cscf = addr_conf->default_server;

/* 初始化为default server的配置,后续虚拟主机匹配成功会采用对应的配置 */
r->main_conf = cscf->ctx->main_conf;
r->srv_conf = cscf->ctx->srv_conf;
r->loc_conf = cscf->ctx->loc_conf;


//这里就是采用默认的虚拟配置块

rev->handler = ngx_http_process_request_line;

r->read_event_handler = ngx_http_block_reading;

//设置读事件

分别为连接和请求分配空间,

分别分配header_in和header_out,ctx

为一些变量分配空间,并设置这些变量的属性为默认值

rev->handler(rev);

//最后调用该函数,也就是ngx_http_process_request_line

2.ngx_http_process_request_line

该函数会调用ngx_http_read_request_header从socket读取内容到一个缓冲区,并且让header_in和连接的一个buffer字段指向这个缓冲区,目的是为了实现缓冲区的重用,同时会调用ngx_http_parse_request_line函数去记录请求行的请求方法,请求uri以及http协议版本

首先ngx_http_read_request_header函数会检查请求的header_in指向的缓冲区是否有数据,有的话直接返回,如果没有的话,他就会从套接口一次性读取更多的内容到缓冲区,如果客户端没有数据发过来的话,就会返回ngx_again,在返回之前需要做俩件事,一个是设置定时器,另外处理读事件,

如果它正常返回的话,将会调用Ngx_http_parse_request_line来解析,即记录请求方法,url以及http协议版本的一些在缓冲区的位置,Nginx往往是为了减少内存拷贝,而只是拷贝它的起始和结束的位置,如果该函数返回错误,则直接给客户返回400,如果返回ngx_again,则需要判断缓冲区空间是否足够,或者是已读数据不够,如果是缓冲区不够,则返回414,如果返回ngx_ok,则表示解析正确完成,这时记录相应的结构到请求结构中的一些字段里面

注意此时如果使用的http协议版本小于1.0.那么不支持header和请求体,直接调用ngx_http_process_request处理请求,前面已经根据请求的addr:port确定了该请求的虚拟主机集合,接下来就是根据host最终确定使用哪个虚拟主机配置块,通过ngx_http_find_virtual_server处理,r->headers_in.server存放的就是host。然后将request的执行环境设置为该虚拟主机对应得环境,而对于协议版本比较高的而言,它们还支持header和cookie,所以还需要调用ngx_http_process_request_headers来解析头部

3.. ngx_http_process_request_headers:

同样开始还是超时判断

n = ngx_http_read_request_header//读取请求头到header_in缓冲区

rc = ngx_http_parse_header_line(r, r->header_in,

把解析到的header添加到requests的headers_in.header链表中,这里会去ngx_http_core_main_conf_t中的一个header的hash map检查解析出的header是否在这个map中,如果在的话会调用对应得handler

当解析完header以后会根据host匹配虚拟主机,而后调用ngx_http_process_request进行后续的请求处理

4.ngx_http_process_request:

这个函数驱动请求的处理,并为处理做一些准备。

主要是设置读写事件的handler.

c->read->handler = ngx_http_request_handler;

c->write->handler = ngx_http_request_handler;

ngx_http_handler(r);

//接着调用这个函数处理

// 处理subrequest

ngx_http_run_posted_requests(c);

5.ngx_http_handler:

为跑一遍phase handler做准备:

首先他会初始化phase_handler,也就是第一个执行的phase_handler在handlers数组中的下标,

这里会通过一个internal字段,它是指是否在内部做跳转,通过他的状态来最终确定phase_handler的值

r->write_event_handler = ngx_http_core_run_phases;

ngx_http_core_run_phases(r); //最后的这个函数是真正的执行phase_handler的函数,才是真正处理请求的函数

6.ngx_http_core_run_phase:

该函数会遍历phase上注册的所有handler,在调用checker的同时改变phase_handler,在跑完所有的phase handler之后,这个请求就被处理完毕,实际的响应内容的输出,是content phase阶段的handler中调用filter输出实现的

7.filter模块:用于输出的模块:

就是在content phase这个阶段,它主要是用于生成响应内容的,

主要用于对回复的头和内容进行处理,它的处理时间是在获取回复内容之后,向用户发送请求之前,包括俩个阶段,过滤http回复的头部和主体

ngx_http_top_header_filter(r)

ngx_http_top_body_filter(r,in)

1》执行顺序:

过滤模块的顺序在编译的时候就已经确定好了,

ngx_module_t *ngx_modules[] = {



&ngx_http_write_filter_module,:仅对http包体做处理,负责发送客户端响应

&ngx_http_header_filter_module:

对http头部处理,它会把r->headers_out中结构体成员序列化为返回给用户的Http响应字符流,包括响应行和响应头部,

&ngx_http_chunked_filter_module,

&ngx_http_range_header_filter_module,

&ngx_http_gzip_filter_module,

&ngx_http_postpone_filter_module:

用于subrequest产生的子请求,他使得多个子请求同时向客户端发送响应时能够有序

&ngx_http_ssi_filter_module,

&ngx_http_charset_filter_module,

&ngx_http_userid_filter_module,

&ngx_http_headers_filter_module,

&ngx_http_copy_filter_module,

&ngx_http_range_body_filter_module,

&ngx_http_not_modified_filter_module,

NULL

};

模块的执行顺序是相反的,第三方的模块只能加入到copy_filter和headers_filter。

它的实现原理是有俩个全局和局部变量:

ngx_http_next_header_filter = ngx_http_top_header_filter;

ngx_http_top_header_filter = ngx_http_example_header_filter;

ngx_http_next_body_filter = ngx_http_top_body_filter;

ngx_http_top_body_filter = ngx_http_example_body_filter;

//每个filter模块都有这样俩个赋值的过程,这样就把新的filter插入进去了,这段代码往往位于初始化的那个函数

在上面那俩个函数执行的过程中也就是直接调用下面的top值

2》ngx_chain_t

struct ngx_chain_s {

ngx_buf_t *buf;

ngx_chain_t *next;

};

//所有的输出内容是通过该单项链表实现的

ngx_buf_t:

size = 0; /* 待输出的内容的大小 */

flush = 0; /* 是否需要flush */

last_buffer= 0; /* 是否是最后一个buffer */

ll = &r->out; /* 保存上次没有输出的ngx_buf_t */

3》响应头过滤函数:

ngx_int_t

ngx_http_send_header(ngx_http_request_t *r)

{

return ngx_http_top_header_filter(r);

}

ngx_http_header_filter_module过滤模块把所有的HTTP头组合成一个完整的buffer,最终ngx_http_write_filter_module过滤模块把buffer输出

在内部通过ngx_http_next_header_filter一级一级往上调用,直到调用到最后一个filter即ngx_http_header_filter,最后这个函数负责计算总的响应头的大小,并分配内存组装响应头,并调用ngx_http_write_filter发送,

4》响应体过滤函数:

ngx_int_t

ngx_http_output_filter(ngx_http_request_t *r, ngx_chain_t *in)

{

ngx_int_t rc;

ngx_connection_t *c;

c = r->connection;

rc = ngx_http_top_body_filter(r, in);

if (rc == NGX_ERROR) {
/* NGX_ERROR may be returned by any filter */
c->error = 1;
}

return rc;


}

每个模块都会有自己的ngx_http_example_body_filter和ngx_http_example_header_filter函数

上面的ngx_http_output_filter函数可以被一般的静态处理模块调用,也可能是在upstream模块里面调用,它的作用就是把响应内容过滤,然后发给客户端,采用流式的输出,并不等到整个响应体都生成了才发送,并且发送的内容可以在文件可以在内存,当发送缓冲区满了,还会保存没有发送完的数据。只需重新调用ngx_http_output_filter

5》可以看一个模块具体的过滤函数:

ngx_http_write_filter:他完成的功能就是遍历buffer链表,注意每一个buffer都是在请求结构体的内存池中申请的,可以进行重用,遍历完以后输出响应内容,Ngx_request_t中还有一个out字段用来保存上一次没有发送的chain.当接收到新的chain的时候会把它连接到旧的chain

代码实现:

size = 0; /* 待输出的内容的大小 */

flush = 0; /* 是否需要flush */

last_buffer= 0; /* 是否是最后一个buffer */

ll = &r->out; /* 保存上次没有输出的ngx_buf_t */

接下来的代码统计上次没有发送的chain的大小,并且根据具体的情况设置flush和last_buffer的值

将旧的chain连接到新的chain后面

接下来就是对chain进行一些处理:

a. clcf->postpone_output:由于处理postpone_output指令,用于设置延时输出的阈值。比如指令“postpone s”,当输出内容的size小于s,并且不是最后一个buffer,也不需要flush,那么就延时输出。

b. c->write->delayed:表示当前连接是否因为某种原因需要延时输出,比如超过发送速率限制等,只有在其他地方取消delayed标记后才能继续输出。在delayed设置的情况下,需要设置c->buffered,并返回NGX_AGAIN。

c. c->buffered:在buffer没有输出完的情况下,标记具体在哪个模块被buffer住了。buffered的取值:

不同的模块有不同的取值,如果buffered的最后4位为1的话就代表还有模块要处理该请求

d.. r->limit_rate:对应limit_rate指令,表示request的发送速率限制值。一般地,通过这个值去设置c->write->delayed,当发送速率超过limit时就会设置c->write->delayed,这种情况下就会延迟发送,从而降低request的发送速率。

接下来的代码就是具体的发送数据的过程:

/* send_chain返回的是没有发完的chain */

chain = c->send_chain(c, r->out, limit);

/* 释放已经发送的chain的内存 */

for (cl = r->out; cl && cl != chain; /* void */) {

ln = cl;

cl = cl->next;

ngx_free_chain(r->pool, ln);

}

/* 重新赋值尚未发送的chain */

r->out = chain;

/* 如果chain不为空,那么buf没有发送完,需要设置buffered标记,并返回NGX_AGAIN */

if (chain) {

c->buffered |= NGX_HTTP_WRITE_BUFFERED;

return NGX_AGAIN;

}

/* 如果已经没有未发送的chain,就清空buffered标记 */

c->buffered &= ~NGX_HTTP_WRITE_BUFFERED;

/* 如果其他filter模块buffer了chain并且postponed为NULL,那么返回NGX_AGAIN,需要继续处理buf */

if ((c->buffered & NGX_LOWLEVEL_BUFFERED) && r->postponed == NULL) {

return NGX_AGAIN;

}

6.ngx_http_copy_filter_module:

这个模块主要是将一些需要复制的buf重新复制一份交给后面的filter模块处理,注意它只有body filter,一般都是在header filter函数中根据请求响应头设置一个模块上下文,用来保存相关信息,body filter使用,但是对于这个模块来说,都是在body filter中进行,它的ctx就是ctx就是ngx_output_chain_ctx_t,因为它的主要逻辑都是在ngx_output_chain模块中,该模块在core下
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: