Nginx笔记
Nginx基础架构:
Nginx采用一个master管理进程、多个worker工作进程的设计方式,包括完全相同的worker进程和一个可选的cache manager进程以及一个可选的cache loader进程。
这种设计带来一下优点:
(1)、利用多核系统的并发处理能力
现代操作系统已经支持多核CPU架构,这使得多个进程可以占用不同的CPU核心来工作。如果只有一个进程在处理请求,则必然会造成CPU资源的浪费。如果多个进程间的地位不平等,则必然会有某一级同一地位的进程成为瓶颈,因此,Nginx中所有的worker工作进程都是完全平等的。这提高了网络性能,降低了请求的时延。
(2)、负载均衡
多个worker工作进程间通过进程间通信来实现负载均衡,也就是说,一个请求到来时更容易被分配到负载较轻的worker工作进程中处理。这将降低请求的时延,并在一定程度上提高网络性能。
(3)、管理进程会负责监控工作进程的状态,并负责管理其行为。管理进程不会占用多少系统资源,它只是用来启动、停止、监控或使用其他行为来控制工作进程。首先,这提高了系统的可靠性,当工作进程出现问题时,管理进程可以启动新的工作进程来避免系统性能的下降。其次,管理进程支持Nginx服务运行中的程序升级、配置项的修改等操作,这种设计使得动态可扩展性、动态定制性、动态可进化性比较容易实现。
平台无关的代码实现
在使用C语言实现Nginx时,尽量减少使用与操作系统平台相关的代码,如某个操作系统上的第三方库。Nginx重新封装了日志、各种基本数据结构、常用算法等工具软件,在核心代码都使用了与操作系统无关的代码实现,在于操作系统相关的系统调用上则分别针对各个操作系统都有独立的实现,这最终造就了Nginx的可移植性,实现了对主流操作系统的支持。
内存池的设计
为了避免出现内存碎片、减少向操作系统申请内存的次数、降低各个模块的开发复杂度,Nginx设计了简单的内存池。这个内存池没有很复杂的功能,通常它不负责回收内存池中已经分配出的内存。这种内存池最大的优点在于:把多次向系统中申请的内存的操作整合成一次,这大大减少了CPU资源的消耗,同时减少了内存碎片。
因此,通常每一个请求都有一个这种简易的独立内存池,而在请求结束时则会销毁整个内存池,把曾经分配的内存一次性的归还给操作系统,这种设计大大提高了模块开发的简单性,而且因为分配内存次数的减少使得请求执行的时延得到了降低,同时,通过减少内存碎片,提高了内存的有效利用和系统可处理的并发连接数,从而增强了网络性能。
使用统一管道过滤器模式的HTTP过滤模块
有一类HTTP模块被命名为HTTP过滤模块,其中每一个过滤模块都有输入端和输出端,这些输入端和输出端都具有统一的接口。这些过滤模块将按照configure执行时决定的顺序组成一个流水线式的加工HTTP响应的中心,每一个过滤模块都是完全独立的,它处理着输入端接收到的数据,并由输出端传递给下一个过滤模块。每一个过滤模块都必须可以增量地处理数据,也就是说能够正确处理完整数据流的一部分。
Nginx框架中的核心结构体ngx_cycle_t:Nginx核心的框架代码一直在围绕着一个结构体展开,它就是ngx_cycle_t。无论是master管理进程、worker工作进程还是cache manager(loader)进程,每一个进程逗毫无例外的拥有一个ngx_cycle_t结构体。服务在初始化时就以ngx_cycle_t对象为中心来提供服务,在正常运行时仍然会以ngx_cycle_t对象为中心。
Ngx_cycle_t结构体
Nginx框架是围绕着ngx_cycle_t结构体来控制进程运行的。Ngx_cycle_t结构体的prefix、conf_prefix、conf_file等字符串类型成员保存着Nginx配置文件的路径,Nginx的可配置性完全依赖于nginx.conf配置文件,Nginx所有模块的可定制性、可伸缩性等诸多特性也是依赖于nginx.conf配置文件的,可以想见,这个配置文件路径必然是保存在ngx_cycle_t结构体中的。
Master进程是如何工作的
Master进程不需要处理网络事件,它不负责业务的执行,只会通过管理worker等子进程来实现重启任务、平滑升级、更换日志文件、配置文件实时生效等功能。Master进程fork系统调用即可完成。Ngx_spawn_process方法封装了fork系统调用,并且会从ngx_processes数组中选择一个还未使用的ngx_process_t元素存储这个子进程的相关信息。如果有1024个数组元素中已经没有空余的元素,也就是说,子进程个数超过了最大值1024,那么将会返回NGX_INVALID_PID。
事件模块
毫无疑问,linux内核提供的文件异步IO是不同于glibc库实现的多线程伪异步IO的,它充分利用了Linux在内核中CPU与IO设备独立工作的特性,使得进程在提交文件异步IO操作后可以占用CPU做其他工作。
事件处理框架概述
事件处理框架所要解决的问题是如何收集、管理、分发事件。这里所说的事件,主要以网络事件和定时器事件为主,而网络事件又以TCP网络事件为主。由于网络事件与网卡中断处理程序、内核提供的系统调用密切相关,所以网络事件的驱动既取决于不同的操作系统平台,在同一个操作系统中也受制于不同的操作系统内核版本。这样的话,nginx支持多少种操作系统,就必须提供多少个事件驱动机制,因为基本上每个操作系统提供的事件驱动机制都是不相同的。如此一来,事件处理框架需要在不同的操作系统内核中选择一种事件驱动机制支持网络事件的处理。
首先,它定义了一个核心模块ngx_events_moudle,这样在nginx启动时会调用ngx_init_cycle方法解析配置项,一旦在nginx.conf配置文件中找到ngx_events_moudle感兴趣的events{}配置项,ngx_events_moudle模块就开始工作了。Ngx_events_moudle模块定义了事件类型的模块,它的全部工作就是为了所有的事件模块解析events{}中的配置项,同时管理这些事件模块存储配置项的结构体。
在ngx_event_core_moudle模块的初始化过程中,将会从9个模块中选取一个作为nginx进程的事件驱动模块。
事件模块是一种新的模块类型,ngx_moudle_t表示nginx模块的基本接口,而针对于每一种不同类型的模块,都有一个结构体来描述这一类模块的通用接口,这个接口保存在ngx_moudle_t结构体的ctx成员中。
Nginx事件的定义
在Nginx中,每一个事件都由ngx_event_t结构体来表示。
Epoll事件驱动模块
设想一个场景:有100万用户同时与一个进程保持着TCP连接,而每一时刻只有几十个或几百个TCP连接是活跃的(接收到TCP包),也就是说,在每一时刻,进程只需要处理这100万连接中的一小部分连接。那么,如何才能高效的处理这种场景呢?进程是否在每次询问操作系统收集有事件发生的TCP连接时,把这100万个连接告诉操作系统,然后由操作系统找出其中有事件发生的几百个连接呢?实际上,在Linux内核2.4版本以前,那时的select或者poll事件驱动方式就是这样做的。
这里有个非常明显的问题,即在某一时刻,进程收集有事件的连接时,其实这100万连接中的大部分都是没有事件发生的。因此,如果每次收集事件时,都把这100万连接的套接字传给操作系统(这首先就是用户态内存到内核态内存的大量复制),而由操作系统内核寻找这些连接上有没有未处理的事件,将会是巨大的资源浪费,然而select和poll就是这样做的,因此它们最多只能处理几千个并发连接。而epoll不这样做,它在Linux内核中申请了一个简易的文件系统,把原先的一个select或者poll调用分成了三部分:调用epoll_create建立一个epoll对象(在epoll文件系统中给这个句柄分配资源)、调用epoll_ctl向epoll对象中添加这100万个连接的套接字、调用epoll_wait收集发生事件的连接。这样,只需要在进程启动时建立一个epoll对象,并在需要的时候向它添加或删除连接就可以了,因此在实际收集事件时,epoll_wait的效率就会非常高,因为调用epoll_wait时并没有向它传递这100万个连接,内核也不需要去遍历全部的连接。
当某一个进程调用epoll_create方法时,Linux内核会创建一个eventpoll结构体,这个结构体中有两个成员与epoll的使用方式密切相关。
Struct rb_root rbr;
Struct list_head rdllist;
每个epoll对象都有一个独立的eventpoll结构体,这个结构体会在内核空间中创造独立的内存,用于存储使用epoll_ctl方法向epoll对象中添加进来的事件。这些事件都会挂到rbr红黑树中,这样,重复添加的事件就可以通过红黑树而高效的识别出来。所有添加到epoll中的事件都会与设备驱动程序建立回调关系,也就是说,相应的事件发生时会调用这里的回调方法。这个回调方法在内核中叫做ep_poll_callback,它会把这样的事件放到上面的rdllist双向链表中。这个内核中的双向链表与ngx_queue_t容器几乎是完全相同的。在epoll中,对于每一个事件都会建立一个epitem结构体。
当调用epoll_wait检查是否有事件的连接时,只是检查eventpoll对象中的rdllist双向链表中是否有epitem元素而已,如果rdllist链表不为空,则把这里的事件复制到用户态内存中,同时将事件数量返回给用户。因此,epoll_wait的效率非常高。Epoll_ctl在向epoll对象中添加、修改、删除事件时,从rbr红黑数中查找事件也非常快,也就是说,epoll时非常高效的,他可以轻易的处理百万级别的并发连接。
Epoll通过下面三个epoll系统调用为用户提供服务。
(1)、epoll_create系统调用:epoll_create在C库中的原型如下。
Int epoll_create(int size);
Epoll_create返回一个句柄,之后epoll的使用都将依靠这个句柄来标识
Nginx服务器基础配置指令
默认的Nginx服务器的配置文件都存放在安装目录conf中,主配置文件名为nginx.conf。
Nginx.conf文件的结构
...... #主要定义nginx的全局配置
events{ #events(事件)块:主要配置网络连接相关
}
http{ #http块:代理缓存和日志定义绝大多数功能和第三方模块的配置可以放这
... #http全局块
server{ #server块:每个server相当于一台虚拟主机,它内部可以有多台主机联合提供服务,一起对外提供在逻辑上关系密切的一组服务
... #server全局块
location /{ #location:基于nginx服务器接收到的请求字符串,对除虚拟主机名之外的字符串进行匹配,对特定的请求进行处理
...
}
}
}
......
1、全局块
全局块是默认配置文件从开始到events块之间的一部分内容,主要设置一些影响Nginx服务器整体运行的配置指令,因此,这些指令的作用域是Nginx服务器全局。
通常包括配置运行Nginx服务器的用户、允许生成的worker process数、Nginx进程PID存放路径、日志的存放路径和类型以及配置文件引入等。
2、events块
events块涉及的指令主要影响Nginx服务器与用户的网络连接。常用到的设置包括是否开启对worker process下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型处理连接请求,每个worker process可以同时支持的最大连接数等。
这一部分的指令对Nginx服务器的性能影响较大,在实际配置中应该根据实际情况灵活调整。
3、http块
http块是Nginx服务器配置中的重要部分。代理、缓存和日志定义等绝大部分的功能和第三方模块的配置都可以放在这个模块中。
可以在http全局块中配置的指令包括文件引入、MIME-Type定义,日志自定义、是否使用sendfile传输文件、连接超时时间、单连接请求数上限等。
4、server块
server块和虚拟主机的概念有密切的联系。所谓虚拟主机,又称虚拟服务器、主机空间或是网页空间,它是一种技术。该技术是为了节省互联网服务器硬件成本而出现的。这里的主机和空间是由实体的服务器延申而来,硬件系统可以基于服务器群,或者单个服务器等。虚拟主机技术主要应用于HTTP、FTP及HTML等多项服务,将一台服务器的某项或者全部服务内容逻辑划分为多个服务单位,对外表现为多个服务器,从而充分利用服务器硬件资源。从用户角度来看,一台虚拟主机和一台独立的硬件主机是完全一样的。
在使用Nginx服务器提供web服务时,利用虚拟主机的技术就可以避免为每一个要运行的网站提供单独的Nginx服务器,也无需为每个网站对应运行一组Nginx进程。每一个http块都可以包含多个server块,而每个server块就相当于一台虚拟主机,它内部可以有多台主机联合提供服务,一起对外提供服务。
server块也可以包含自己的全局块,同时可以包含多个location块。在server全局块中,最常见的两个配置项是本虚拟机主机监听配置和本虚拟主机的名称和IP配置。
5、location块
每个server块中可以包含多个location块。从严格意义上来说,location其实是一个server块的一个指令,只是由于其在整个Nginx配置文档中起着首要的作用,而且Nginx服务器在许多功能上的灵活性往往在location指令的配置中体现出来。location块的主要作用是,基于Nginx服务器接收到的请求字符串,对除虚拟主机名称之外的字符串进行匹配,对特定的请求进行处理。地址定向、数据缓存和应答控制等功能都是在这部分实现。
Nginx服务器的基本命令
Nginx的启动
./sbin/Nginx
如果没有任何错误信息输出,Nginx服务就启动了。可以使用ps -ef | grep nginx命令查看Nginx服务的进程状态。
Nginx服务的停止
停止Nginx服务有两种方法:一种是快速停止;一种是平缓停止。快速停止是指立即停止当前Nginx服务正在处理的所有网络请求,马上丢弃连接,停止工作;平滑停止是指允许Nginx服务将当前正在处理的网络请求处理完成,但不再接收新的请求,之后关闭连接,停止工作。
停止Nginx服务的操作比较多。可以发送信号:
./sbin/Nginx -g TERM | INT | QUIT
其中,TERM和INT信号用于快速停止,QUIT用于平缓停止。
或者
kill TERM | INT | QUIT `/Nginx/logs/nginx.pid`
Nginx服务的重启
更改Nginx服务器的配置和加入新模块后,如果希望当前的Nginx服务应用新的配置或使新模块生效,就需要重启Nginx服务。平滑重启是这样一个过程,Nginx服务进程接收到信号后,首先读取新的Nginx配置文件,如果配置语法正确,则启动新的Nginx服务,然后平缓关闭旧的服务进程;如果新的Nginx配置有问题,将显示错误,仍然使用旧的Nginx进程提供服务。
使用以下命令实现Nginx服务的平滑重启:
./sbin/nginx -g HUP
HUP信号用于发送平滑重启信号。
Nginx服务器的升级
如果要对当前的Nginx服务器进行版本升级,应用新模块、最简单的办法是停止当前Nginx服务,然后开启新的Nginx服务,但这样会导致在一段时间内,用户无法访问服务器。为了解决中各问题,Nginx提供了平滑升级的功能。
平滑升级的过程是这样的,Nginx服务接收到USR2信号后,首先将旧的nginx.pid文件添加后缀oldbin,变为nginx.pid.oldbin文件;然后执行新版本的Nginx服务器的二进制文件启动服务。如果新的服务启动成功,系统中将有新旧两个Nginx服务共同提供web服务。之后,需要向旧的Nginx服务进程发送WINCH信号,使旧的Nginx服务平滑停止,并删除nginx.pid.oldbin文件。在发送WINTCH信号之前,可以随时停止新的Nginx服务。
注意:为了实现Nginx服务器的平滑升级,新的服务器安装路径应该和旧的保持一致。因此建议用户在安装新服务器之前先备份旧服务器。如果由于某种原因导致无法保持新旧服务安装路径一致,则可以先使用以下命令将旧服务器的安装路径更改为新服务器的安装路径。
./Nginx/nginx -p newInstallPath
其中,newInstallPath为新服务器的安装路径。之后,备份旧服务器,安装新服务器即可。
做好准备工作以后,使用以下命令实现Nginx服务的平滑升级:
./sbin/Nginx -g USR2
其中,USE2信号用于发送平滑升级信号。或者,使用
kill USR2 `/Nginx/logs/nginx.pid`
通过ps -ef | grep nginx 查看新的Nginx服务启动正常,再使用:
./sbin/Nginx -g WINCH
其中,WINCH信号用于发送平滑停止旧服务信号。或者,使用:
kill WINCH `/Nginx/logs/nginx.pid`
这样就在不停止提供web服务的前提下完成了Nginx服务器的平滑升级。
Nginx服务器基础配置指令
默认的Nginx服务器配置文件都存放在安装目录conf中,主配置文件名为nginx.conf。
注意:在nginx.conf原始文件中,还包括注释内容,注释标志为"#"
worker_processes 1; # 全局生效
events {
worker_connections 1024; # 在events部分中生效
use epoll;
}
http {
include mime.types; # 以下指令在http部分中生效
default_type application/octet-stream;
#log_format main '$remote_addr - $remote_user [$time_local] "$request" '
# '$status $body_bytes_sent "$http_referer" '
# '"$http_user_agent" "$http_x_forwarded_for"';
#access_log logs/access.log main;
sendfile on;
#tcp_nopush on;
#keepalive_timeout 0;
keepalive_timeout 65;
#gzip on;
server {
listen 80; # 以下指令在http的server部分中生效
server_name mrgao.work;
location / { # 以下指令在http/server的location中生效
root html;
index index.html index.htm;
}
}
}
nginx.conf文件的结构
nginx文件的基本结构为:
...
events
{
...
http {
server {
location {}
}
}
}
最外层的花括号将内容整体分为两部分,再加上最开始的内容,即第一行省略号表示的,nginx.conf一共由三部分组成,分别为全局块,events块和http块。在http块中,又包含http全局块、多个server块。每个server块中,可以包含server全局块和多个location块。在同一配置块中嵌套的配置块,各个之间不存在次序关系。
配置文件支持大量可配置的指令,绝大多数指令不是特定属于某一个块的。同一个指令放在不同层级的块中,其作用域也不同,一般情况下,高一级块中的指令可以作用于自身所在的块和此块包含的所有低层级块。如果某个指令在两个不同层级的块中同时出现,则采用就近原则,即以较低层级块中的配置为准。
- 全局块
全局块是默认配置文件从开始到events块之间的一部分内容,主要设置一些影响Nginx服务器整体运行的配置指令,因此,这些指令的作用域是Nginx服务器全局。
通常包括配置运行Nginx服务器的用户(组)、允许生成的worker process数、Nginx进程PID存放路径、日志的存放路径和类型以及配置文件引入等。 - events块
events块涉及的指令主要影响Nginx服务器与用户的网络连接。常用到的设置包括是否开启对多worker process下的网络连接进行序列化,是否允许同时接收多个网络连接,选取哪种事件驱动模型处理连接请求,每个worker process可以同时支持的最大连接数等。 - http块
http块是Nginx服务器配置中的重要部分,代理、缓存和日志定义等绝大多数的功能和第三方模块的配置都可以放在这个模块中。 - server块
虚拟主机,又称虚拟服务器、主机空间或者网页空间,它是一种技术。该技术是为了节省互联网服务器硬件成本而出现的。这里的主机或者空间是由实体的服务器延伸而来,硬件系统可以基于服务器群,或者单个服务器等。虚拟主机技术主要应用于HTTP、FTP及EMAIL等多项服务,将一台服务器的某项或者全部服务内容逻辑划分为多个服务单位,对外表现为多个服务器,从而充分利用服务器硬件资源。从用户角度来看,一台虚拟主机和一台独立的硬件主机是完全一样的。
在使用Nginx服务器提供web服务时,利用虚拟主机的技术就可以避免为每一个要运行的网站提供单独的Nginx服务器,也无需为每个网站对应运行一组Nginx进程。虚拟主机技术使得Nginx服务器可以在同一台服务器上只运行一组Nginx进程。
注意:在http全局块中介绍过的部分指令可以在server块中和location块中使用,其作用域问题已经说明。
和http相同,server块也可以包含自己的全局块,同时可以包含多个location块。在server全局块中,最常见的两个配置项是本虚拟主机的监听配置和本虚拟主机的名称或IP配置。 - location块
每个server块中可以包含多个location块。从严格意义上说,location其实是server块的一个指令,只是由于其在整个Nginx配置文档中起着重要的作用,而且Nginx服务器有许多功能上的灵活性往往在location指令的配置中体现出来。
这些location块的主要作用是,基于Nginx服务器接收到的请求字符串,对除虚拟主机名称(也可以是IP别名)之外的字符串进行匹配,对特定的请求进行处理。地址空间,数据缓存和应答控制等功能都在这部分实现。许多第三方模块的配置也是在location块中提供功能。
配置运行Nginx服务器用户组
用于配置运行Nginx服务器用户(组)的指令是user,其语法格式为:
user user [group];
user,指定可以运行Nginx服务器的用户。
group,可选项,指定可以运行Nginx服务器的用户组。
只有被设置的用户或者成员才有权限启动Nginx进程,如果其他用户尝试启动Nginx进程,将会报错。
如果希望所有用户都可以启动Nginx进程,有两种办法:一是将此指令行注释掉:
#user [user] [group];
或者将用户(和用户组)设置为nobody:
user nobody nobody;
这也是user指令的默认配置。user指令值能在全局块中配置。
注意,在Nginx配置文件中,每一条指令配置都必须以分号结束,请不要忘记。
配置允许生成的worker process数
worker process是Nginx实现并发处理服务的关键所在。从理论上来说,worker process的值越大,可以支持的并发处理量也越多,但实际上还要受到来自软件本身、操作系统本身资源和能力、硬件设备等的制约。
配置允许生成的worker process数的指令是worker_processes,其语法格式为:
worker_processes number | auto;
number,指定Nginx进程最多可以产生的worker process数。
auto,设定此值,Nginx进程将自动检测。
在默认的配置文件中,number=1。启动Nginx服务器以后,使用以下命令可以看到Nginx服务器除了主进程master process之外,还生成了一个worker process:
ps -ef | grep nginx
此指令只能在全局块中设置。
配置Nginx进行PID的存放路径
Nginx进程作为系统的守护进程运行,我们需要在某个文件中保存当前运行程序的主进程号。Nginx支持对它的存放路径进行自定义配置,指令是pid,其语法格式为:
pid file;
其中,file指定存放路径和文件名称。
配置文件默认将此文件存放在Nginx安装目录logs下,名字为nginx_pid。path可以是绝对路径,也可以是以Nginx安装目录为根目录的相对路径。
注意:在指定[path]的时候,一定要包括文件名,如果只设置了路径,没有设置文件名,则会报错。
此指令只能在全局块中进行配置。
配置错误日志的存放路径
在全局块、http块和server块中都可以对Nginx服务器的日志进行相关配置。其语法格式为:
error_log file | stderr | [debug | info | notice | warn | error | crit | alert | emerg];
从语法结构可以看到,Nginx服务器的日志支持输出到某一固定文件file或者输出到标准错误输出stderr;日志级别是可选项,由低到高分为debug、info、notice、warn、error、crit、alert、emerg等。需要注意的是,设置莫伊级别后,比这一级别高的日志也会被记录。比如设置warn级别后,级别为warn以及error、crit、alert和emerg的日志都会被记录下来。
注意:指定的文件对于运行Nginx进程的用户具有写权限,否则在启动Nginx进程的时候会出现报错。
此指令可以在全局块、http块、server块以及location块中配置。
配置文件的引入
在一些情况下,我们可能需要将其他的Nginx配置或者第三方模块的配置引用到当前的主配置文件中。Nginx提供了include指令来完成配置文件的引入,其语法结构为:
include file;
其中,file是要引入的配置文件,它支持相对路径。
注意:新引用来的文件同样要求运行Nginx进程的用户对其具有写权限,并且符合Nginx配置文件规定的相关语法和结构。
此指令可以放在配置文件的任意地方。
设置网络连接的序列化
在UNIX网络编程第一卷里提到过一个叫惊群的问题,大致的意思是,当某一时刻只有一个网络连接到来时,多个睡眠进程会被同时叫醒,但只有一个进程可获得连接。如果每次唤醒的进程数目太多,会影响一部分系统性能。在Nginx服务器的多进程下,就有可能出现同样的问题。
为了解决这样的问题,Nginx配置中包含了这样一条指令accept_mutex,当其设置为开启的时候,将会对多个Nginx进程接收连接进行序列化,防止多个Nginx进程接收连接进行序列化,防止多个进程对连接的争抢。其语法结构为:
accept_mutex on | off;
此指令默认为开启(on)状态,其只能在events块中进行配置。
设置是否允许同时接收多个网络连接
每个Nginx服务器的worker_process都有能力同时接收多个新到达的网络连接,但是这需要在配置文件中进行设置,其指令为multi_accept,语法结构为:
multi_accept on | off;
此指令默认为关闭(off)状态,即每个worker_process一次只能接收一个新到达的网络连接。此指令只能在events块中进行配置。
事件驱动模型的选择
Nginx服务器提供了多种事件驱动模型来处理网络消息。配置文件中为我们提供了相关的指令来强制Nginx服务器选择哪种事件驱动模型进行消息处理,指令为use,语法结构为:
use method;
其中,method可选择的内容有:select、poll、kqueue、epoll、rtsig、/dev/poll以及eventport,其中几种模型是比较常用的。
此指令只能在events块中进行配置。
配置最大连接数
指令worker_connections主要用来配置允许每一个worker process同时开启的最大连接数。其语法结构为:
worker_connections number;
此指令的默认设置为512。
注意:这里的number不仅仅包括和前端用户建立的连接数,而是包括所有可能的连接数。另外,number值不能大于操作系统支持打开的最大文件句柄数量。
此指令只能在events块中进行配置。
定义MIME-Type
我们知道在常用的浏览器中,可以显示的内容有HTML、XML、GIF及Flash等种类繁多的文本、媒体等资源,浏览器为区分这些资源,需要使用的MIME-Type。换言之,MIME-Type是网络资源的媒体类型。Nginx服务器作为Web服务器,必须能够识别前端请求的资源类型。
在默认的Nginx配置文件中,我们看到在http全局块中有以下两行配置;
include mime.types;
default_type application/octet-stream;
默认值为text/plain。此指令还可以在http块,server块或者location块中进行配置。
自定义服务日志
Nginx服务器支持对服务日志的格式、大小、输出等进行配置,需要使用两个指令,分别是access_log和log_format指令。
access_log指令的语法结构为:
access_log path[format [buffer = size]];
- path,配置服务日志的文件存放路径和名称。
- format,可选项,自定义服务日志的格式字符串,也可以通过格式串的名称使用log_format指令定义好的格式。格式串中的名称在log_format指令中定义。
- size,配置临时存放日志的内容缓存区大小。
此指令可以在http块、server块或者location块中进行设置。默认的配置为:
access_log logs/access.log combined;
其中,combined为log_format指令默认定义的日志格式字符串的名称。
如果需要取消记录服务日志的功能,则使用:
access_log off;
和access_log联合使用的另一个指令是log_format,它专门用于定义服务日志的格式,并且可以为格式字符串定义一个名字,以便access_log指令可以直接调用。其语法格式为:
log_format name string ...;
- name,格式字符串的名字,默认的名字为combined。
- string,服务日志的格式字符串。在定义过程中,可以使用Nginx配置预设的一些变量获取相关的内容,变量的名称使用双引号括起来,string整体使用单引号括起来。
配置允许sendfile方式传输文件
sendfile和sendfile_max_chunk以及它们的语法结构:
sendfile on | off;
用于开启或者关闭使用sendfile()传输文件,默认值为off,可以在http块、server块或者location块中进行配置。
sendfile_max_chunk size;
其中,size值如果大于0,Nginx进程的每个worker process每次调用sendfile()传输的数据量最大不能超过这个值;如果设置为0,则无限制。默认值为0。此指令可以在http块、server块或者location块中配置。
配置连接超时时间
与用户建立会话连接后,Nginx服务器保持这些连接打开一段时间,指令keepalive_timeout就是用来设置此事件的,其语法结构是:
keepalive_time timeout [header_timeout];
- timeout。服务器端对连接的保持时间。默认值为75。
- header_timeout,可选项,在应答报文头部的keep-Alive域设置超时时间:Keep-Alive:timeout = header_timeout。报文中的这个指令可以被Mozila或者Konqueror识别。
此指令还可以出现在server块和location块中。
单连接请求书上限
Nginx服务器端和用户端建立会话连接后,用户端通过此连接发送请求。指令keepalive_requests用于限制用户通过某一连接向Nginx服务器发送请求的次数。其语法结构为:
keepalive_requests number;
此指令还可以出现在server块和location块中,默认设置为100。
配置网络监听
配置监听使用指令listen,其配置方法主要有三种,第一种配置监听的IP地址,语法结构为:
listen address [:port][default_server];
第二种配置监听端口,其语法结构是:
listen port [default_server][setfib=number][backlog=number];
第三种配置UNIX Domain Socket
listen unix:path;
基于名称的虚拟主机配置
这里的主机,就是指此server块对外提供的虚拟主机。设置了主机的名称并配置好DNS,用户就可以使用这个名称向此虚拟主机发送请求了。配置主机名称的指令为server_name,其语法结构为:
server_name name;
对于name来说,可以只有一个名称,也可以由多个名称并列,之间用空格隔开。每个名字就是一个域名,由两段或者三段组成,之间由点号“.”隔开。
Nginx服务器规定,第一个名称作为虚拟主机的主要名称。
在name中可以使用通配符"*",但通配符只能用在由三段字符串组成的名称的首段和尾段,或者由两段字符串组成的名称的尾段。
配置location块
在Nginx的官方文档中定义的location的语法结构为:
location [= | ~ | ~* | ^~ ] uri {...}
其中,uri变量是待匹配的请求字符串,可以是不含正则表达式的字符串,也可以是包含有正则表达的字符串。
配置请求的根目录
Web服务器接收到网络请求之后,首先要在服务器端指定目录中寻找请求资源。在Nginx服务器中,指令root就是用来配置这个根目录的,其语法结构为:
root path;
其中,path为Nginx服务器接收到请求以后查找资源的根目录路径。path变量中可以包含Nginx服务器预设的大多数变量。
此指令可以在http块,server块或者location块中配置。由于使用Nginx服务器多数情况下要配置多个location块对不同的请求分别作出处理,因此该命令通常在location块中进行设置。
更改location的URI
在location块中,除了使用root指令指明请求处理根目录,还可以使用alias指令改变location接收到的URI的请求路径,其语法结构为:
alias path;
其中,path即为修改后的路径。同样,此变量中也可以包含除了$document_root和$realpath_root之外的其他Nginx服务器预设变量。
设置网站的默认首页
指令index用于设置网站的默认首页,它一般可以有两个作用:一是,用户在发出请求访问网站时,请求地址可以不写首页名称;二是可以对一个请求,根据其请求内容而设置不同的首页。该指令的语法结构为:
index file;
其中,file变量可以包括多个文件名,其间使用空格分隔,也可以包含其他变量。此变量的默认值为"index.html"。
设置网站的错误页面
Nginx服务器设置网站错误页面的指令为error_page,语法结构为:
error_page code ... [=[response]] uri;
- code,要处理的HTTP错误代码。
- response,可选项,将code指定的错误代码转化为新的错误代码response。
- uri,错误页面的路径或者网站地址。如果设置为路径,则是以Nginx服务器安装路径下的html目录为根路径的相对路径;如果设置为网址,则Nginx服务器会直接访问该网址获取错误页面,并返回给用户端。
error_page指令可以在http块、server块和location块中配置。
基于IP配置Nginx的访问权限
Nginx配置通过两种途径支持基本访问权限的控制,其中一种是由HTTP标准模块ngx_http_access_moudle支持的,其通过IP来判断客户端是否拥有对Nginx的访问权限,这里有两个需要我们学习。
allow指令,用于设置允许访问Nginx的客户端IP,语法结构为:
allow address | CIDR | all;
- address,允许访问客户端的IP,不支持同时设置多个。如果有多个IP需要设置,需要重复使用allow指令。
- CIDR,允许访问客户端的CIDR地址。
- all,代表允许所有客户端的访问。
另一个指令为deny,作用刚好与allow指令相反,它用于设置禁止访问Nginx的客户端IP,语法结构为:
deny address | CIDR | all;
- address,禁止访问客户端的IP,不支持同时设置多个。如果有多个IP需要设置,需要重复使用deny指令。
- CIDR,禁止访问客户端的CIDR地址。
- all,代表禁止所有客户端的访问。
这两个指令可以在http块、server块或者location块中配置。在使用这两个指令时,要注意设置为all的用法。
基于密码配置Nginx的访问权限
Nginx还支持基于HTTP Basic Authentication协议的认证。该协议是一种HTTP性质的认证办法,需要识别用户名和密码,认证失败的客户端不拥有访问Nginx服务器的权限。该功能由HTTP标准模块ngx_http_auth_basic_module支持,这里有两个指令需要学习。
auth_basic指令,用于开启或者关闭该认证功能,语法结构为:
auth_basic string | off;
- string,开启该认证功能,并配置验证时的指示信息。
- off,关闭该认证功能。
auth_basic_user_file file;
其中,file为密码的绝对路径。
这里的密码文件支持明文或者密码加密后的文件。
加密密码可以使用crypt()函数进行密码加密的格式,在Linux平台上可以使用htpasswd命令生成。运行后输入密码即可。
Nginx服务器的Web请求处理机制
从设计架构上来说,Nginx服务器是与众不同的。不同之处一方面体现在它的模块化设计,另一方面,也是更重要的一方面,体现在它对客户端请求的处理机制上。
Web服务器和客户端是一对多的关系,Web服务器必须有能力同时为多个客户端提供服务。一般来说,完成并行处理请求工作有三种方式可供选择:多进程方式,多线程方式和异步方式。
多进程方式
多进程方式是指,服务器每当接收到一个客户端时,就由服务器主进程生成一个子进程出来和该客户端建立连接进行交互,直到连接断开,该子进程就结束了。
多进程的优点在于,设计和实现相对简单,各个子进程之间相互独立,处理客户端请求的过程彼此不受到干扰,并且当一个子进程产生问题时,不容易将影响蔓延到其他进程中,这保证了提供服务的稳定性。当子线程退出时,其占用资源会被操作系统回收,也不会留下任何垃圾。而其缺点也是很明显的。操作系统中生成一个子进程需要进行内存复制等操作,在资源和时间上会产生一定的额外开销,因此,如果Web服务器接收到大量并发请求,就会对系统资源造成压力,导致系统性能下降。
初期的Apache服务器就是采用这种方式对外提供服务的。为了应对大量并发请求,Apache服务器采用预生成进程的机制对多进程方式进行了改进。预生成进程的工作方式很好理解。它将生成子进程的时机提前,在客户端请求还没有到来之前就预先生成好,当请求到来时,主进程分配一个子进程和该客户端进行交互,交互完成之后,该进程也不结束,而被主进程管理起来等待下一个客户端请求的到来。改进的多进程方式在一定程度上缓解了大量并发请求清醒下Web服务器对系统资源造成的压力。但是由于Apache服务器在最初的结构设计上采用了多进程方式,因此这不能从根本上解决问题。
多线程方式
多线程和多进程的方式相似,服务器每当接收到一个客户端时,会由服务器主进程派生一个线程出来和该客户端进行交互。由于操作系统产生一个线程的开销远远小于产生一个进程的开销,所以多线程方式在很大程度上减轻了Web服务器对系统资源的要求。该方式使用线程进行任务调度,开发方面遵循一定的标准,这相对来说比较规范和有利于协作。但在线程管理方面,该方式有一定的不足。多个线程位于同一个进程内,可以访问同样的内存空间,彼此之间相互影响;同时,在开发过程中不可避免地要由开发者自己对内存进行管理,其增加了出错的风险。服务器系统需要长时间连续不停地运转,错误的逐渐积累可能最终对整个服务器产生重大影响。
异步方式
异步方式是和多进程方式及多线程方式完全不同的一种处理客户端请求的方式。
网络通信中的同步机制和异步机制是描述通信模式的概念。同步机制,是指发送方发送请求后,需要等待接收到接收方发回的响应后,菜接着发送下一个请求;异步机制,和同步机制正好相反,在异步机制中,发送方发出一个请求后,不等待接收方响应这个请求,就继续发送下一个请求。在同步机制中,所有的请求在服务器端得到同步,发送方和接收方对请求的处理步调是一致的;在异步机制中,所有来自发送方的请求形成一个队列,接收方处理完成后通知发送方。
阻塞和非阻塞用来描述进程处理调用的方式,在网络通信中,主要指网络套接字Socket的阻塞和非阻塞的方式,而Socket的实质也就是IO操作。Socket的阻塞调用方式为,调用结果返回之前,当前线程从运行状态被挂起,一直等到调用结果返回之后,才进入就绪状态,获取CPU后继续执行;Socket的非阻塞调用方式和阻塞调用方式正好相反,在非阻塞方式中,如果调用结果不能马上返回,当前线程也不会被挂起,而是立即返回执行下一个调用。
在网络通信中,经常可以看到有人将同步和阻塞等同、异步和非阻塞等同。事实上,这两对概念上有一定的区别,不能混淆。两对概念的组合,就会产生四个新的概念,同步阻塞、异步阻塞、同步非阻塞、异步非阻塞。
- 同步阻塞方式,发送方向接收方发送请求后,一直等待响应;接收方处理请求时进行的IO操作如果不能马上得到结果,就一直等到返回结果后,才响应发送方,期间不能进行其他工作。这种方式实现简单,但是效率不高。
- 同步非阻塞方式,发送方向接收方发送请求后,一直等待响应;接收方处理请求时进行的IO操作如果不能马上得到结果,就立即返回,去做其他事情,但由于没有得到请求处理的结果,不能响应发送方,发送方一直等待。一直到IO操作完成后,接收方获得结果响应发送方后,接收方才进入下一次请求的过程。在实际中不使用这种方式。
- 异步阻塞方式,发送方向接收方发送请求后,不用等待响应,可以接着进行其他工作;接收方处理请求时进行的IO操作如果不能马上得到结果,就一直等到返回结果后,才响应发送方,期间不能进行其他工作。这种方式在实际中不能使用。
- 异步非阻塞方式,发送方向接收方发送请求后,不用等待响应,可以继续其他工作;接收方处理请求时进行的IO操作如果不饿能马上得到结果,也不等待,而是马上返回去做其他事情,当IO操作完成之后,将完成的状态和结果通知接收方,接收方再响应发送方。
Nginx服务器如何处理请求
Nginx服务器启动后,可以产生一个主进程和多个工作进程,其中可以在配置文件中指定产生的工作进程数量。Nginx服务器的所有工作进程都用于接收和处理客户端的请求。
注意:实际上,Nginx服务器的进程模型有两种:Single模型和Master-Worker模型。Single模型为单进程方式,性能较差,一般在实际工作中不使用。Mater-Worker模型实际上被更广泛地称为Master-Slave模型。在Nginx服务器中,充当Slave角色的时工作进程。
每个工作进程使用了异步非阻塞的方式,可以处理多个客户端请求。当某个工作进程接收到客户端的请求以后,调用IO进行处理,如果不能立即得到结果,就去处理其他的请求;而客户端在此期间也无需等待响应,可以去处理其他的事情;当IO调用返回结果时,就会通知此工作进程;该进程得到通知,暂时挂起当前处理的事务,去响应客户端请求。
客户端请求数量增长、网络负载繁重时,Nginx服务器使用多进程机制能够保证不增长对系统资源的压力;同时使用异步非阻塞的方式减少了工作进程在IO调用上的阻塞延迟,保证了不降低对请求的处理能力。
Nginx服务器的事件处理机制
Nginx服务器的工作进程调用IO后,就去进行其他工作了;当IO调用返回后,会通知工作进程。这里有一个问题,IO调用是如何把自己的状态通知给工作进程的呢?
一般解决这个问题的方案有两种。一是,让工作进程在进行其他工作的过程中间隔一段时间就去检查IO的运行状态,如果完成,就去响应客户端,如果未完成就继续正在进行的工作;二是IO调用完成之后主动能够通知工作进程。对于前者,虽然工作进程在IO调用过程中没有等待,但不断地检查仍然在时间和资源上导致了不小的开销,最理想的解决方案是第二种。
具体来说,select/poll/epoll/kqueue等这样的系统调用就是用来支持第二种解决方案的,这些系统调用,也常被称为事件驱动模型,它提供了一种机制,让进程可以同时处理多个并发请求,不用关心IO调用的具体状态。IO调用完全由事件驱动模型来管理,事件准备好后就通知工作进程事件已经就绪。
Nginx服务器事件驱动模型
事件驱动模型是Nginx服务器保障完整功能和具有良好性能的重要机制之一。
事件驱动模型概述
顾名思义,事件驱动就是在持续事务管理过程中,由当前时间点上出现的事件引发的调动可用资源执行相关任务,解决不断出现的问题,防止事务堆积的一种策略。
事件驱动模型一般是由事件收集器、事件发送器和事件处理器三部分基本单元组成。
其中,事件收集器专门负责收集所有的事件,包括来自用户、硬件和软件的。事件发送器负责将收集器收集到的事件分发到目标对象中。目标对象就是事件处理器所处的位置。事件处理器主要负责具体事件的响应工作,它往往要到实现阶段才完全确定。
事件驱动程序可以由任何编程语言来实现,只是难易程度有别。如果一个系统是以事件驱动程序模型作为编程基础的,那么,它的架构基本上是这样的:预先设计一个事件循环所形成的程序,这个事件循环程序构成了事件收集器,他不断地检查目前要处理的事件信息,然后使用事件发送器传递给事件处理器。事件处理器一般运用虚函数机制来实现。
Nginx中的事件驱动模型
Nginx服务器响应和处理web请求的过程,就是基于事件驱动模型的,它也包含事件收集器、事件发送器和事件处理器等三部分基本单元。
通常,我们编写服务器处理模型的程序时,基于事件驱动模型,目标对象中的事件处理器可以有以下几种实现办法:
- 事件发送器每传递过来一个请求,目标对象就创建一个新的进程,调用事件处理器来处理该请求。
- 事件发送器每传递过来一个请求,目标对象就创建一个新的线程,调用事件处理器来处理该请求。
- 事件发送器每传递过来一个请求,目标对象就将其放入一个待处理事件的列表,使用非阻塞IO方式调用事件处理器来处理该请求。
事件驱动处理库又被称为多路IO复用方法,最常见的包括以下三种:select模型,poll模型和epoll模型。
select库
select库,是各个版本的Linux和Windows平台都支持的基本驱动模型库,并且在接口的定义上也基本相同,只是部分参数的含义略有差异。使用select库的步骤一般是:
首先创建所关注事件的描述符集合。对于一个描述符,可以关注其上面的读事件、写事件以及异常发生事件,所以要创建三类事件描述符集合,分别用来收集读事件的描述符、写事件的描述符和异常事件的描述符。
其次,调用底层提供的select函数,等待时间发生。这里需要注意的一点是,select的阻塞与是否设置了非阻塞IO是没有关系的。
然后,轮询所有事件描述符集合中的每一个事件描述符,检查是否有相应的事件发生,如果有,就进行处理。
Nginx服务器在编译过程中如果没有为其指定其他高性能事件驱动模型库,它将自动编译该库。
poll库
poll库,作为Linux平台上的基本事件驱动模型,是在Linux2.1.23中引入的。
poll与select的基本工作方式是相同的,都是先创建一个关注事件的描述符集合,再去等待这些事件发生,然后再轮询描述符集合,检查有没有事件发生,如果有,就进行处理。
poll库与select库的主要区别在于,select库需要为读事件、写事件和异常事件分别创建一个描述符集合,因此在轮询的时候,需要分别轮询这三个集合。而poll库只需要创建一个集合,在每个描述符对应的结构上分别设置读事件、写事件或者异常事件,最后轮询的时候,可以同时检查这三种事件是否发生。可以说,poll库是select库的优化实现。
epoll库
epoll库是Nginx服务器支持的高性能事件驱动库之一,它是公认的非常优秀的事件驱动模型,和poll库及select库有很大的不同。poll和select库在实际工作中,最大的区别在于效率。
从前面的介绍我们知道,它们的处理方式都是创建一个待处理事件列表,然后把这个列表发给内核,返回的时候,再去轮询检查这个列表,以判断事件是否发生。这样在描述符比较多的应用中,效率就显得比较低下了。一种比较好的做法是,把描述符列表的管理交由内核负责,一旦有某种事件发生,内核就把事件的描述符列表通知给进程,这样就避免了轮询整个描述符列表。epoll库就是采用这样的一种模型。
首先,epoll库通过相关的调用内核创建一个有N个描述符的事件列表;然后,给这些描述符设置所关注的事件,并把它添加到内核的事件列表中去,在具体的编码过程中也可以通过相关调用对事件列表中的描述符进行修改和删除。
完成设置之后,epoll库就开始等待内核通知事件发生了。某一事件发生之后,内核将发生事件的描述符列表上报给epoll库。得到事件列表的epoll库,就可以进行事件处理了。
epoll库在Linux平台上是高效的。它支持一个进程打开大数目的事件描述符,上限是系统可以打开文件的最大数目;同时,epoll库的IO效率不随描述符数目增加而线性下降,因为它只会对内核上报的活跃的描述符进行操作。
Nginx服务器架构
Nginx服务器启动后,产生一个主进程(master process),主进程执行一系列工作后产生一个或者多个工作进程(worker process)。主进程主要进行Nginx配置文件解析、数据结构初始化、模块配置和注册、信号处理、网络监听生成,工作进程生成和管理等工作;工作进程主要进行进程初始化,模块调用和请求处理等工作,是Nginx服务器提供服务的主体。
在客户端请求动态站点的过程中,Nginx服务器还涉及和后端服务器的通信。Nginx服务器将接收到的Web请求通过代理转发到后端服务器,由后端服务器进行数据处理和页面组织,然后将结果返回。
另外,Nginx服务器为了提高对请求的响应效率,进一步降低网络压力,采用了缓存机制,将历史应答数据缓存到本地。在每次Nginx服务器启动后的一段时间内,会启动专门的进程对本地缓存的内容重建索引,保证对缓存文件的快速访问。
Nginx服务器的结构大致分为主进程、工作进程、后端服务器和缓存等部分。
Nginx服务器的进程
Nginx服务器的三大类进程:一类是主进程,另一类是主进程生成的工作进程,还有刚才提到的用于为缓存文件建立索引的进程。
- 主进程(Master Process):Nginx服务器启动时运行的主要进程。它的主要功能是与外界通信和对内部其他进程进行管理,具体来说有以下几点:
- 读取Nginx配置文件并验证其有效性和正确性。
- 建立、绑定和关闭Socket。
- 按照配置生成、管理和结束工作进程。
- 接收外界指令、比如重启、升级及退出服务器等指令。
- 不中断服务,实现平滑重启,应用新配置。
- 不中断服务,实现平滑升级,升级失败进行回滚处理。
- 开启日志文件,获取文件描述符。
- 编译和处理perl脚本。
- 工作进程(Worker Process):由主进程生成,生成数量可以通过Nginx配置文件指定,正常情况下生存于主进程的整个生命周期。该进程的主要工作有以下几项:
- 接收客户端请求。
- 将请求依次送入各个功能模块进行过滤处理。
- IO调用,获取响应数据。
- 与后端服务器通信,接收后端服务器处理结果。
- 数据缓存、访问缓存索引、查询和调用缓存数据。
- 发送请求结果,响应客户端请求。
- 接收主进程指令,比如重启、升级和退出等指令。
工作进程完成的工作还有很多,我们在这里列出了主要的几项。从这些工作中可以看到,该进程是Nginx服务器提供Web服务、处理客户端请求的主要进程,完成了Nginx服务器的主体工作。因此在实际使用中,作为服务器管理者,我们应该重点监视工作进程的运行状态,保证Nginx服务器对外提供稳定的Web服务。
- 缓存索引重建及管理进程(Cache Loader & Cache Manger):Cache模块,主要由缓存索引重建(Cache Loader)和缓存索引管理(Cache Manager)两类进程完成工作。缓存索引重建进程是在Nginx服务器启动一段时间之后(默认是1分钟)由主进程生成,在缓存元数据重建完成之后就自动退出;缓存索引管理进程一般存在于主进程的整个生命周期,负责对缓存索引进行管理。
缓存索引重建进程完成主要工作是,根据本地磁盘上的缓存文件在内存中建立索引元数据库。该进程启动后,对磁盘上存放的缓存文件的目录结构进行扫描,检查内存中已有的缓存元数据是否正确,并更新索引元数据库。
缓存索引管理进程主要负责在索引元数据更新完成之后,对元数据是否过期做出判断。
这两个进程维护的内存索引元数据库,为工作进程对缓存数据的快速查询提供了便利。
进程交互
Nginx服务器在使用Master-Worker模型时,会涉及主进程与工作进程之间的交互和工作进程之间的交互。这两类交互都依赖于管道机制,交互的准备工作都是在工作进程生成时完成的。
- Master-Worker交互
工作进程是由主进程生成的(使用fork函数)。Nginx服务器启动以后,主进程根据配置文件决定生成的工作进程的数量,然后建立一张全局的工作进程表用于存放当前未退出的所有工作进程。
在主进程生成工作进程后,将新生成的工作进程加入到工作进程表中,并建立一个单向管道并将其传递给该工作进程。该管道与普通的管道不同,它是由主进程指向工作进程的单向管道,包含了主进程向工作进程发出的指令,工作进程ID、工作进程在工作进程表中的索引和必要的文件描述符等信息。
主进程与外界通过信号机制进行通信,当接收到需要处理的信号时,它通过管道向相关的工作进程发送正确的指令。每个工作进程都有能力捕获管道中可读事件,当管道中有可读事件时,工作进程从管道读取并解析指令,然后通过采取相应的措施。这样就完成了Master-Worker的交互。 - Worker-Worker交互
Worker-Worker交互在实现原理上与Master-Worker交互基本是一样的。只要工作进程之间能够得到彼此的信息,建立管道,即可通信。由于工作进程之间是相互隔离的,因此一个进程要想知道另一个进程的信息,只能通过主进程来设置了。
为了达到工作进程之间交互的目的,主进程在生成工作进程之后,在工作进程表中进行遍历,将该新进程的ID以及针对该进程建立的管道句柄传递给工作进程表中的其他进程,为工作进程之间的交互做准备。每个工作进程捕获管道中可读事件,根据指令采取响应措施。
当工作进程W1需要向W2发送指令时,首先在主进程给它的其他工作进程信息中找到W2的进程ID,然后将正确的指令写入指向W2通道。工作进程W2捕获管道中的事件后,解析指令并采取相应的措施。这样就完成了Worker-Worker交互。
Nginx服务器的高级配置
针对IPv4的内核7个参数的配置优化
这里提及的参数是和IPv4网络有关的Linux内核参数。我们可以将这些内核参数的值追加到Linux系统的/etc/sysctl.conf文件中,然后使用如下命令使修改生效:
#/sbin/sysctl -p
这些常用的参数包括以下这些:
- net.core.netdev_max_backlog参数:参数net.core.netdev_max_backlog,表示当每个网络接口接收数据包的速率比内核处理这些包的速率快时,允许发送到队列的数据包的最大数目。一般默认值为128。
net.core.netdev_max_backlog = 262144
- net.core.somaxconn参数:该参数用于调节参数同时发起TCP连接数,一般默认值为128。在客户端存在高并发请求的情况下,该默认值较小,可能导致连接超时或者重传问题。
net.core.somaxconn = 262144
- net.ipv4.tcp_max_orphans参数:该参数用于设定系统中最多允许存在多少TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,没有与用户文件句柄关联的TCP套接字将立即被复位,同时给出警告信息。这个闲置只是为了防止简单的Dos攻击。一般系统内存比较充足的情况下,可以增大这个参数的赋值。
net.ipv4.tcp_max_orphans = 262144
- net.ipv4.tcp_max_syn_backlog参数:该参数用于记录尚未收到客户端确认信息的连接请求的最大值。对于拥有128MB内存的系统而言,此参数的默认值为1024,对小内存的系统则是128。一般在系统内存比较充足的情况下,可以增大这个参数的赋值。
net.ipv4.tcp_max_syn_backlog = 262144
- net.ipv4.tcp_timestamps参数:该参数用于设置时间戳,这可以避免序列号的卷绕。在一个1Gb/s的链路上,遇到以前用过的序列号的概率很大。当此值赋值为0时,禁用对于TCP时间戳的支持。在默认情况下,TCP协议会让内核接受这种异常的数据包。针对Nginx服务器来说,建议将其关闭。
net.ipv4.tcp_timestamps = 0
- net.ipv4.tcp_synack_retries参数:该参数用于设置内核放弃TCP连接之前向客户端发送SYN+ACK包的数量。为了建立对端的连接服务,服务器和客户端需要进行三次握手,第二次握手期间,内核需要发送SYN并附带一个回应前一个SYN的ACK,这个参数主要影响这个过程,一般赋值为1,即内核放弃连接之前发送一次SYN+ACK包,可以设置为:
net.ipv4.tcp_synack_retries = 1
- net.ipv4.tcp_syn_retries参数:该参数的作用与上一个参数类似,设置内核放弃建立连接之前发送SYN包的数量,它的赋值和上一个参数一样即可:
net.ipv4.tcp_syn_retries = 1
针对CPU的Nginx配置优化的2个指令
处理器已经进入多核时代。多内核是指在一枚处理器中集成两个或多个完整的计算引擎,多核处理器是单枚芯片。一枚多核处理器上可以承载多枚内核,但只需要单一的处理器插槽可以工作。同时,目前流行的操作系统都已经可以利用这样的资源,将每个执行内核作为分立的逻辑处理器,通过在多个执行内核之间划分任务,在特定的时间周期内执行更多任务,提高并行处理任务的能力。
在Nginx配置文件中,有这样的两个指令:worker_processes和worker_cpu_affinity,它们可以针对多核CPU进行配置优化。
- worker_processes指令
worker_processes指令用来设置Nginx服务的进程数。官方文档建议此指令一般设置为1即可,赋值太多会影响系统的IO效率,降低Nginx服务器的性能。 - worker_cpu_affinity指令:worker_cpu_affinity指令用来为每个进程分配CPU的工作内核。
与网络连接相关的配置的4个指令
- keepalive_timeout指令:该指令用于设置Nginx服务器与客户端保持连接的超时时间。
这个指令支持两个选项,中间用空格隔开。第一个选项指定客户端连接保持活动的超时时间,在这个时间之后,服务器会关闭此链接;第二个选项可选,其指定了使用Keep-Alive消息头保持活动的有效时间,如果不设置它,Nginx服务器不会向客户端发送Keep-Alive消息头以保持与客户端某些浏览器的连接,超过设置的时间后,客户端就可以关闭连接,而不需要服务器关闭了。你可以根据自己的实际情况设置此值,建议从服务器的访问数量,处理速度以及网络状况方面考虑。
keepalive_timeout 60 50;
该值表示Nginx服务器与客户端保持活动的时间是60s,60s后服务器与客户端断开连接;使用Keep-Alive消息头保持与客户端某些浏览器的连接时间为50s,50s后浏览器主动与服务器断开连接。
2. send_timeout指令:该指令用于设置Nginx服务器响应客户端的超时时间,这个超时时间仅针对两个客户端和服务器之间建立连接后,某次活动之间的时间。如果这个时间后客户端没有任何活动,Nginx服务器将会关闭连接。此指令的设置需要考虑服务器访问数量和网络状况等方面。
send_timeout 10s
该设置表示Nginx服务器与客户端建立连接后,某次会话中服务器等待客户端响应超过10s,就会自动关闭连接。
3. client_header_buffer_size指令:该指令用于设置Nginx服务器允许的客户端请求头部缓冲区的大小,默认为1KB。此指令的赋值可以根据系统分页大小来设置。分页大小可以用以下命令取得:
#getconf PAGESIZE
Nginx服务器返回400错误的情况,查找Nginx服务器的400错误原因比较困难,因为此错误并不是每次都会出现,出现错误的时候,通常在浏览器和日志里面也看不到任何有关提示信息。根据实际的经验来看,有很大一部分情况是客户端的请求头部过大造成的。请求头部过大,通常客户端cookie中写入了较大的值引起的。于是适当增大此指令的赋值,允许Nginx服务器接收较大的请求头部,可以改善服务器对客户端的支持能力。
4. multi_accept指令:该指令用于配置Nginx服务器是否尽可能多地接收客户端的网络连接请求,默认值为off。
与事件驱动模型相关的配置的8个指令
- use指令:use指令用于指定Nginx服务器使用的事件驱动模型。
- worker_connections指令:该指令用于设置Nginx服务器的每个工作进程允许同时连接客户端的最大数量,语法为:
worker_connections number;
其中,number为设置的最大数量。结合worker_processes指令,我们可以计算出Nginx服务器允许同时连接的客户端最大数量Client = worker_processes * worker_connections/2。
此指令一般设置为65535。
worker_connections 65535;
此指令的赋值与linux操作系统中进程可以打开的文件句柄数量有关系。
此指令的赋值与Linux操作系统中进程可以打开的文件句柄数量有关系。究其原因,Linux系统中有一个系统指令open file source limit,它设置了进程可以打开的文件句柄数量。worker_connections指令的赋值当然不能超过open file resource limit的赋值。可以使用以下命令查看在你的Linux系统中open resource limit指令的值:
# cat /proc/sys/fs/file-max
可以通过以下命令将open file resource limit指令的值设为2390251:
#echo "2390251" > /proc/sys/fs/file-max; sysctl -p
这样,Nginx的worker_connections指令赋值为65535就没问题了。
3. worker_rlimit_sigpending指令
该指令用于设置Linux 2.6.6-mm2版本之后linux平台的事件信号队列长度上限。其语法结构为:
worker_rlimit_sigpending limit;
其中,limit为Linux平台事件信号队列的长度上限值。
该指令主要影响事件驱动模型中rtsig模型可以保存的最大信号数。Nginx服务器的每一个工作进程有自己的事件信号队列用于暂存客户端请求发生信号,如果超过长度上限,Nginx服务器自动专用poll模型处理来处理的客户端请求。为了保证Nginx服务器对客户端请求的高效处理。
4. devpoll_changes和decpoll_events指令
这两个指令用于设置在/dev/poll事件驱动模式下Nginx服务器可以与内核之间传递事件的数量,前者用于设置传递给内核的事件数量,后者设置从内核获取的事件数量,其语法结构为:
devpoll_changes number
decpoll_events number
其中,number为要设置的数量,默认值均为32。
5. kqueue_changes和kqueue_events指令
这两个指令用于设置在kqueue事件驱动模式下Nginx服务器可以与内核之间传递事件的数量,前者设置传递给内核的事件数量,后者设置从内核获取的事件数量,其语法结构为:
kqueue_changes number
kqueue_events number
其中,number为要设置的数量,默认值为512。
6. epoll_events指令
该指令用于设置在epoll事件驱动模式下Nginx服务器可以与内核之间传递事件的数量,其语法结构为:
epoll_changes number
其中,number为要设置的数量,默认值均为512。
7. rtsig_signo指令
该指令用于设置stsig模式使用的两个信号中的第一个,第二个信号是在第一个信号的编号上加一,语法为:
rtsig_signo signo
默认的第一个信号设置为SIGRTMIN+10。
8. rtsig_overflow_*指令
该指令代表三个具体指令,分别为rtsig_overflow_events指令、rtsig_overflow_test指令和rtsig_overflow_threshold指令,这些指令用来控制rtsig模式中信号队列溢出时Nginx服务器的处理方式。
rtsig_overflow_* number
其中,number要设定的值。
rtsig_overflow_events指令指定队列溢出时使用poll库处理的事件数,默认值16。
rtsig_overflow_test指令指定poll库处理完第几件事件后将清空rtsig模型使用的信号队列,默认值为32。
rtsig_overflow_threshold指令指定rtsig模式使用的信号队列中的事件超过多少时就需要清空队列了。该指令只对Linux 2.4.x及以下版本有效。在这些版本中包含两个参数:分别是proc/sys/kernel/rtsig-nr和/proc/sys/kernel/rtsig-max/rtsig_overflow_threshold,后面就是该指令设定的值。当Nginx服务器检测到前者大于后者时,将情况队列。该指令默认值为10,代表rtsig-max的1/10。
Nginx服务器的Rewrite功能
Nginx后端服务器组的配置的5个指令
- upstream指令
该指令是设置后端服务器组的主要指令,其他的指令都在该指令中进行配置。upstream指令类似于之前提到的http块,server块等,其语法结构为:
upstream name {...}
其中,name是给后端服务器组起的组名。花括号中列出后端服务器组中包含的服务器。
默认情况下,某个服务器组接收到请求以后,按照轮叫调度(Round-Robin,RR)策略顺序选择组内服务器处理的请求。如果一个服务器在处理请求的过程中出现错误,请求会被顺次交给组内下一个服务器进行处理,以此类推,直到返回正常响应。但如果所有组内服务器都出错,则返回最后一个服务器的处理结果。
2. server指令
该指令用于设置组内的服务器,其语法结构为:
server address [parameters];
- address,服务器的地址,可以包含端口号的IP地址、域名或者unix为前缀的进程间通信的Unix Domain Socket。
- parameter,为当前服务器配置更多属性。这些属性变量包括以下内容:
- weight = number,为组内服务器设置权重,权重值高的服务器被优先用于处理请求。此时组内服务器的选择策略为加权轮询策略。组内所有服务器的权重默认设置为1,即采用一般轮询调用原则处理请求。
- max_fails = number,设置一个请求失败的次数。在一定时间范围内,当对组内某台服务器请求失败的次数超过该变量设置的值时,认为该服务器无效。请求失败的各种情况与proxy_next_upstream指令的配置相匹配。默认设置为1.如果设置为0,则不使用上面的办法检查服务器是由有效。
注意:Http 404状态不被认为请求失败。
- ip_hash指令
该指令用于实现会话保持功能,将某个客户端的多次请求定向到组内同一台服务器上,保证客户端与服务器之间建立稳定的会话。只有当该服务器处于无效状态时,客户端请求才会被下一个服务器接收和处理,其语法结构为:
ip_hash;
- keepalive指令
该指令用于控制网络连接保持功能。通过该指令,能够保证Nginx服务器的工作进程为服务器组打开一部分网络连接,并且将数量控制在一定的范围之内,其语法结构为:
keepalive connections;
其中,connections为Nginx服务器的每一个工作进程允许该服务器组内保持的空闲网络连接数的上限值。如果超过该值,工作进程将采用最近最少使用的策略关闭网络连接。