Nginx基础概念及基本方法

基本概念

我们在这里主要讨论connectionrequest两个方法。

连接方法connection

在Nginx中,connection就是对tcp连接的封装,包括连接的socket、读事件、写事件。

结合一个tcp连接的生命周期,我们看看Nginx是如何处理一个链接的。

  1. Nginx刚启动时,解析配置文件,得到需要监听的IP地址和端口
  2. 在master进程初始化socket
  3. fork()多个工作进程,同时工作进程会竞争accept新连接

完成这三步,客户端就可以像Nginx发起连接了。当进行TCP三次握手之后,Nginx的某一个worker进程会accept成功。然后就根据这个socket,创建Nginx对socket的封装,即ngx_connection_t结构体。

当然,Nginx也可以作为客户端来请求其他server的数据。此时,它创建的连接也会封装在ngx_connection_t结构体。
作为客户端,Nginx先获取一个ngx_connection_t结构体,然后创建socket并设置器属性,然后再添加读写事件。

Nginx的最大连接数

在Nginx中,每个进程会有一个连接数的最大上限,这个上限与系统对fd的限制不一样。

在操作系统中,我们可以通过ulimit -n来得到一个进程所能打开的最大的fd的最大数,即nofile。一般来说,每个socket连接会占用一个fd,会影响到我们程序所能支持的最大并发数。不过,这却对Nginx没有影响。

Nginx对连接数的限制与nofile没有直接关系,可以大于nofile,也可以小于
Nginx通过设置worker_connectos来设置每个进程可使用的连接最大值。Nginx在实现的时候,是通过一个连接池来管理的。每个worker进程都有一个独立的连接池,连接池的大小是一个worker_connections大小的一个ngx_connection_t结构的数组。

而且,Nginx会通过一个链表(每个进程都有)free_connections来保存所有空闲的ngx_connection_t,每次获取一个连接时,就从空闲链表中获取一个。用完后,再放回链表里面。

所以,我们可以得出Nginx支持的最大并发数量其实是工作线程数 * 每个工作线程支持的并发数,即worker_connections * worker_processes。当然,如果作为HTTP作为反向代理,并发数应该是worker_connections * worker_processes / 2。因为作为反向代理服务器,每个并发都会建立与客户端以及后端服务的连接。

Nginx连接处理

当一个客户端连接过来,会有很多空闲的工作进程去竞争这个连接。既然有连接,那就会有不公平的情况发生。

如果某个进程得到accept的机会比较多,那么它连接池里面的空闲连接很快就会用完。如果不提前做一些控制,当accept到一个新的tcp连接后,因为没有空闲连接了且无法将连接转交给其他进程,就会导致此链接无法得到处理。

所以,Nginx会先打开accept_mutex选项。此时只有获得连接锁的进程才有资格去添加accept事件。也就是说,Nginx会控制进程是否去添加,Nginx采用ngx_accept_disabled的变量来控制是否去竞争accept_mutex锁。

ngx_accept_disabled = ngx_cycle->connection_n / 8 - ngx_cycle->free_connection_n;

这段代码中用来计算ngx_accept_disabled的值,这个值是Nginx单进程所有连接总数的1/8,然后再减去剩下的空闲连接数量。当剩余连接数小于总连接数的1/8,其值才大于0,且当前进程连接数越多,剩余连接数越小,这个值越大,越不可能获取锁

if(ngx_accept_disabled > 0)
{
   
	ngx_accept_disabled--;
}
else
{
   
	if(ngx_trylock_accept_mutex(cycle) == NGX_ERROR)
	{
   
		return;
	}

	if(ngx_accept_mutex_held)
	{
   
		flags |= NGX_POST_EVENTS;
	}
	else
	{
   
		if(timer == NGX_TIMER_INFINITE || timer > mgx_accept_mutex_delay)
		{
   
			timer = ngx_accept_mutex_delay;
		}
	}
}

我们可以看到,当ngx_accept_disabled大于0的时候,也就是不可获取accept_mutex锁。当空闲连接越少的时候,出让的机会就越多。

请求方法request

我们这里指的是HTTP请求,具体到Nginx就是ngx_http_request_tngx_http_request_t是对一个HTTP请求的封装,包含请求行、请求头、请求体、响应行、响应头、响应体

HTTP请求是典型的请求-响应类型网络协议。而http是文件协议,所以我们在分析请求行、请求头、响应行、响应头都是一行一行进程处理的。
如果我们自己写服务器,通常在一个连接建立好后,客户端会发送请求你过来,然后我们读取一行数据,分析请求行中包含的method、uri、http_version等信息去处理信息。然后再把啊响应发送给客户端。

当然,Nginx和这个有一点点的区别。比如,当请求完成后,就开始进行请求的处理。Nginx通过ngx_http_request_t来保存解析请求与输出响应相关的数据。

Nginx处理请求

对于Nginx来说,请求都是从ngx_http_init_request开始的。在这个函数中,会设置读事件为ngx_http_process_request_line。也就是,接下来的网络事件会从ngx_http_process_request_line来执行,这个函数也就是来处理请求行的。

通过ngx_http_read_request_header来读取请求数据,然后调用ngx_http_parse_request_line函数来解析请求行。
Nginx为了提高效率,采用状态机来解析请求行。整个请求行解析到的参数会保存在ngx_http_request_t结构中。

当Nginx解析到两个回车换行符的时候,就表示请求头的结束,ngx_http_process_request会设置当前的读写事件处理函数为ngx_http_request_handler,然后在调用ngx_http_handler来开始一个真正的http请求。

我们可以看到,读写事件处理函数都是ngx_http_request_handler。在这个函数中,会根据当前是读事件还是写事件分别调用ngx_http_request_t中的read_event_handler或者write_event_handler。到此刻,我们请求头就读完了。

在Nginx中,是先不读取请求body的。所以读事件处理函数会被设置成ngx_http_block_reading,即不读取数据了。

刚刚说到,数据的真正处理是在ngx_http_handler里面。这个函数会设置write_event_handlerngx_http_core_run_phases,并执行ngx_http_core_run_phases函数。

ngx_http_core_run_phases会执行多阶段请求处理。产生的响应头是放在ngx_http_request_theaders_out中。这里的详细会之后再讲。

Nginx的各种阶段会对请求进行处理,最后会调用filter来过滤数据,对数据进行加工。如trunked传输、gzip压缩等。

filter模块

这里的filter包括header filterbody filter,即对响应头或响应体进行处理。

filter是一个链表结构,分别有 header filter 和 body filter,先会执行header中的所有filter,再执行body里面所有的filter。在 header filter 中最后一个filter,即ngx_http_header_filter。这个filter会遍历所有的响应头,最后需要输出的响应头是在一个连续的内存,然后调用ngx_http_write_filter进行输出。ngx_http_write_filter是body里面最后一个,所以Nginx的body信息在经过一系列的body filter之后,最后也会调用ngx_http_write_filter进行输出。

需要注意的是,Nginx会将整个请求头都放在一个buffer里面,这个buffer的大小可以通过配置项client_header_buffer_size来配置,如果用户请求头过大,这个buffer装不下,那么Nginx就会分配更大的一个buffer,这个大buffer可以通过large_client_header_buffer_size来设置。

所以,一个完整的请求行或者请求头,只会保存在一个buffer里面

Nginx处理请求流程图

参考文献

[1] Nginx开发从入门到精通
全部评论

相关推荐

一个菜鸡罢了:哥们,感觉你的简历还是有点问题的,我提几点建议,看看能不能提供一点帮助 1. ”新余学院“别加粗,课程不清楚是否有必要写,感觉版面不如拿来写一下做过的事情,教育经历是你的弱势就尽量少写 2. “干部及社团经历”和“自我评价”删掉 3. 论文后面的“录用”和“小修”啥的都删掉,默认全录用,问了再说,反正小修毕业前肯定能发出来 4. 工作经验和研究成果没有体现你的个人贡献,着重包装一下个人贡献
点赞 评论 收藏
分享
11-27 12:36
已编辑
门头沟学院 前端工程师
Apries:这个阶段来说,很厉害很厉害了,不过写的简历确实不是很行,优势删掉吧,其他的还行
点赞 评论 收藏
分享
评论
1
1
分享
正在热议
# 25届秋招总结 #
440737次浏览 4493人参与
# 春招别灰心,我们一人来一句鼓励 #
41503次浏览 524人参与
# 阿里云管培生offer #
119879次浏览 2219人参与
# 地方国企笔面经互助 #
7928次浏览 18人参与
# 同bg的你秋招战况如何? #
75684次浏览 552人参与
# 虾皮求职进展汇总 #
114355次浏览 884人参与
# 北方华创开奖 #
107312次浏览 599人参与
# 实习,投递多份简历没人回复怎么办 #
2454094次浏览 34848人参与
# 实习必须要去大厂吗? #
55687次浏览 960人参与
# 提前批简历挂麻了怎么办 #
149836次浏览 1977人参与
# 投递实习岗位前的准备 #
1195731次浏览 18546人参与
# 你投递的公司有几家约面了? #
33180次浏览 188人参与
# 双非本科求职如何逆袭 #
661934次浏览 7394人参与
# 如果公司给你放一天假,你会怎么度过? #
4734次浏览 55人参与
# 机械人春招想让哪家公司来捞你? #
157604次浏览 2267人参与
# 如果你有一天可以担任公司的CEO,你会做哪三件事? #
11381次浏览 271人参与
# 发工资后,你做的第一件事是什么 #
12431次浏览 61人参与
# 工作中,努力重要还是选择重要? #
35621次浏览 384人参与
# 参加完秋招的机械人,还参加春招吗? #
20091次浏览 240人参与
# 我的上岸简历长这样 #
451933次浏览 8088人参与
# 实习想申请秋招offer,能不能argue薪资 #
39241次浏览 314人参与
# 非技术岗是怎么找实习的 #
155852次浏览 2120人参与
牛客网
牛客企业服务