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下