type
Post
status
Published
date
Jan 30, 2026 12:23 PM
slug
summary
Nginx 是一个高性能的Web服务器,采用master-worker架构,其中master进程管理多个worker进程,worker进程负责处理请求。Nginx支持多种配置项,包括基本配置、优化性能的配置、事件类配置等,能够有效管理网络连接、内存和磁盘资源。Nginx还具备反向代理功能,通过负载均衡将请求分发到上游服务器,降低服务器负载。其事件驱动架构和模块化设计使其具备高并发处理能力和良好的可扩展性。
tags
开发
category
八股盛宴
icon
password
Nginx 功能与配置
2.1 master-worker
正式提供服务的产品环境下,部署 Nginx 使用一个 master 进程管理若干 worker 进程,一般情况下,worker 进程数量等同于服务器的 CPU 核心数。
worker 进程真正提供互联网服务。
master 负责监控 worker 进程。
当然 Nginx 支持单进程提供服务(master),采用 master-workers 的好处:
- master 可以专注于管理,并且为程序员提供命令行服务。
- worker 可以充分利用常见 SMP 多核架构,从而实现围观上真正的并发处理。
为什么要把worker进程数量设置得与CPU核心数量一致呢?
这正是 Nginx与Apache服务器的不同之处。
在Apache上每个进程在一个时刻只处理一个请求,因此,如果希望Web服务器拥有并发处理的请求数更多,就要把 Apache的进程或线程数设置得更多,通常会达到一台服务器拥有几百个工作进程,这样大量的进程间切换将带来无谓的系统资源消耗。
而 Nginx则不然,一个worker进程可以同时处理的请求数只受限于内存大小,而且在架构设计上,不同的worker进程之间处理并发请求时几乎没有同步锁的限制,worker进程通常不会进入睡眠状态,因此,当Nginx上的进程数与CPU核心数相等时(最好每一个worker进程都绑定特定的CPU核心),进程间切换的代价是最小的。
2.2 通用语法
块配置项
块配置项名和大括号。
events、http、server、location、upstream等都是块配置项,块配置项之后是否如“location/webstatic{…}”那样在后面加上参数,取决于解析这个块配置项的模块,不能一概而论,但块配置项一定会用大括号把一系列所属的配置项全包含进来,表示大括号内的配置项同时生效。所有的事件类配置都要在events块中,http、server等配置也遵循这个规定。
块配置项可以嵌套。内层块直接继承外层块,例如,上例中,server块里的任意配置都是基于http块里的已有配置的。当内外层块中的配置发生冲突时,究竟是以内层块还是外层块的配置为准,取决于解析这个配置项的模块,第4章将会介绍http块内配置项冲突的处理方法。
配置项
配置项名+配置项值
在行首的是配置项名,这些配置项名必须是Nginx的某一个模块想要处理的,否则Nginx会认为配置文件出现了非法的配置项名。配置项名输入结束后,将以空格作为分隔符。
其次是配置项值,它可以是数字或字符串(当然也包括正则表达式)。针对一个配置项,既可以只有一个值,也可以包含多个值,配置项值之间仍然由空格符来分隔。当然,一个配置项对应的值究竟有多少个,取决于解析这个配置项的模块。
有些模块允许在配置项中使用变量,使用它的时候前面要加上$符号。需要注意的是,这种变量只有少数模块支持,并不是通用的。许多模块在解析请求时都会提供多个变量(如本章后面提到的http coremodule、http proxy module、http upstream module等),以使其他模块的配置可以即时使用。
2.3 基本配置
Nginx在运行时,至少必须加载几个核心模块和一个事件类模块。
这些模块运行时所支持的配置项称为基本配置(所有其他模块执行时都依赖的配置项)。
用于调试进程和定位问题的配置项
- daemon on|off是否以守护进程方式运行Nginx(默认 daemon on;)守护进程(daemon)是脱离终端并且在后台运行的进程。它脱离终端是为了避免进程执行过程中的信息在任何终端上显示,这样一来,进程也不会被任何终端所产生的信息所打断。
- master_process on|off;是否以master/worker方式工作(默认 master_process on;)
- error_log/path/file level;error日志设置(默认 error_log logs/error.log error;)/path/file 可以是一个具体文件,日志保存在该文件;/path/file 也可以是/dev/null,这样不会输出日志;/path/file 也可以是stderr,这样日志会输出到标准错误文件中。level是日志的输出级别,取值范围是debug、info、notice、warn、error、crit、alert、emerg,从左至右级别依次增大。当设定为一个级别时,大于或等于该级别的日志都会被输出到/path/file文件中,小于该级别的日志则不会输出。
- worker_rlimit_core size限制coredump核心转储文件的大小。在 Linux 系统中,当进程发生错误或收到信号而终止时,系统会将进程执行的内存内容(核心影响)写入文件(core 文件),以作为调试之用,这就是所谓核心转储(core dumps)。当 Nginx 进程出现非法操作导致进程直接被操作系统强制结束时,就会生成核心转储文件。
- working_directory path;指定coredump文件生成目录。worker 进程的工作目录,确保 worker 进程有权向指定目录写入文件。
正常运行的配置项
- env VAR|VAR=VALUE定义环境变量
- include/path/file;嵌入其他配置文件到当前 nginx.conf
- pid path/file;(默认 pid logs/nginx.pid;)保存 master 进程 ID 的 pid 文件存放路径。
- user username[groupname];(默认 user nobody nobody;)user用于设置master进程启动后,fork出的worker进程运行在哪个用户和用户组下。当按照“user username;”设置时,用户组名与用户名相同。若用户在configure命令执行时使用了参数–user=username和–group=groupname,此时nginx.conf将使用参数中指定的用户和用户组。
- worker_rlimit_sigpending limit;设置每个用户发往Nginx的信号队列的大小。也就是说,当某个用户的信号队列满了,这个用户再发送的信号量会被丢掉。
- worker_rlimit_nofile limit;指定Nginx worker进程可以打开的最大句柄描述符个数
优化性能的配置项
- worker_processes number;(默认 worker_processes 1;)在master/worker运行方式下,定义worker进程的个数。worker进程的数量会直接影响性能,这实际上与业务需求有关。每个worker进程都是单线程的进程,它们会调用各个模块以实现多种多样的功能。如果这些模块确认不会出现阻塞式的调用,那么,有多少CPU内核就应该配置多少个进程;反之,如果有可能出现阻塞式调用,那么需要配置稍多一些的worker进程。多worker进程可以充分利用多核系统架构,但若worker进程的数量多于CPU内核数,那么会增大进程间切换带来的消耗(Linux是抢占式内核)。一般情况下,用户要配置与CPU内核数相等的worker进程,并且使用下面的worker_cpu_affinity配置来绑定CPU内核。
- worker_cpu_affinity cpumask[cpumask…]绑定Nginx worker进程到指定的CPU内核。假定每一个worker进程都是非常繁忙的,如果多个worker进程都在抢同一个CPU,那么这就会出现同步问题。反之,如果每一个worker进程都独享一个CPU,就在内核的调度策略上实现了完全的并发。(worker_cpu_affinity配置仅对Linux操作系统有效。Linux操作系统使用sched_setaffinity()系统调用实现这个功能。)
- ssl_engine device;如果服务器上有SSL硬件加速设备,那么就可以进行配置以加快SSL协议的处理速度。
- worker_priority nice;(默认 worker_priority 0;)该配置项用于设置Nginx worker进程的nice优先级。在Linux或其他类UNIX操作系统中,当许多进程都处于可执行状态时,将按照所有进程的优先级来决定本次内核选择哪一个进程执行。进程所分配的CPU时间片大小也与进程优先级相关,优先级越高,进程分配到的时间片也就越大。这样,优先级高的进程会占有更多的系统资源。优先级由静态优先级和内核根据进程执行情况所做的动态调整共同决定。nice值是进程的静态优先级,它的取值范围是–20~+19,–20是最高优先级,+19是最低优先级。因此,如果用户希望Nginx占有更多的系统资源,那么可以把nice值配置得更小一些,但不建议比内核进程的nice值(通常为–5)还要小。
事件类配置项
- accept_mutex[on|off](默认 accept_mutext on;)是否打开accept锁accept_mutex 是 Nginx 的负载均衡锁,可以让多个 worker 进程轮流的、序列化的与新的客户端建立 TCP 连接。当一个 worker 进程建立的连接数量达到 worker_connections 配置的最大连接数的 7/8,会大大减小该 worker 进程试图建立新 TCP 连接的机会,以此实现所有 worker 进程处理的客户端请求数尽量接近。accept锁默认是打开的,如果关闭它,那么建立TCP连接的耗时会更短,但worker进程之间的负载会非常不均衡,因此不建议关闭它。
- lock_file path/file;(默认 lock_file logs/nginx.lock;)lock文件的路径accept锁可能需要这个lock文件,如果accept锁关闭,lock_file配置完全不生效。如果打开了accept锁,并且由于编译程序、操作系统架构等因素导致Nginx不支持原子锁,这时才会用文件锁实现accept锁,这样lock_file指定的lock文件才会生效。
- accept_mutex_delay Nms;(默认 accept_mutex_delay 500ms;)使用accept锁后到真正建立连接之间的延迟时间。在使用accept锁后,同一时间只有一个worker进程能够取到accept锁。这个accept锁不是阻塞锁,如果取不到会立刻返回。如果有一个worker进程试图取accept锁而没有取到,它至少要等accept_mutex_delay定义的时间间隔后才能再次试图取锁。
- multi_accept[on|off];(默认 multi_accept off;)是否批量建立新连接。当事件模型通知有新连接时,尽可能地对本次调度中客户端发起的所有TCP请求都建立连接。
- use[kqueue|rtsig|epoll|/dev/poll|select|poll|eventport];(Nginx会自动使用最适合的事件模型。)选择事件模型。对于Linux操作系统来说,可供选择的事件驱动模型有poll、select、epoll三种。epoll当然是性能最高的一种,在9.6节会解释epoll为什么可以处理大并发连接。
- worker_connections number;每个worker的最大连接数。
2.4 ngx_http_core_module 配置静态代理服务器
静态Web服务器的主要功能由ngx_http_core_module模块(HTTP框架的主
要成员)实现。本节主要讨论如何配置一个包含基本功能的静态Web服务
器,文中会完整地说明ngx_http_core_module模块提供的配置项及变量的用
法,但不会过多说明其他HTTP模块的配置项。
虚拟主机与请求分发
存在多个主机域名对应相同 IP 地址的情况,此时可以按照 server_name 并通过 server 块来定义虚拟主机,每个 server 块就是一个虚拟主机,并且只处理阈值相对应的主机域名请求。这样一台服务器上的 Nginx 就能以不同的方式处理访问不同主机域名的 HTTP 请求。
- listen address:port[options];listen 80;server;listen参数 options 决定Nginx服务如何监听端口。default:将所在 server 块设为 Web 服务默认块。倘若没有这个参数 nginx.conf 中的首个 server 块将作为默认 server 块。当一个请求无法匹配文件中的所有主机域名的时候,就会选择默认虚拟主机。backlog = num:表示 TCP 中 backlog 队列大小,默认-1,表示不予设置。在 TCP 建立三次握手过程中,进程还没有开始处理监听句柄,backlog 队列会放置这些新连接,若队列满,新的连接会建立连接失败。rcvbuf=sizesndbuf=sizeaccept_filterdeferred:设置参数后,若用户发起建立连接请求,并完成 TCP 三次握手,内核也不会为此次连接调度 worker 进程处理,只有用户发送请求数据时内核才会唤醒 worker 进程处理连接。这个参数适用于高并发的情况下,减轻 worker 进程的负担。bind:绑定当前端口/地址对。ssl:在当前监听的端口上建立的连接必须基于 SSL 协议。
- server_name name[···]server_name ""serverserver_name 后可以附加多个主机名称。在开始处理 HTTP 请求时,Nginx 会取出 header 头中的 Host 与 server_name 进行匹配来决定处理请求的 server 块。可能会有 Host 与多个 server_name 匹配,这时会根据匹配优先级来选择实际处理的 server 块。Nginx 正是使用此配置项针对特定域名的请求提供不同服务,以此实现虚拟主机功能。
- server_names_hash_bucket_size size;server_names_hash_bucket_size 32|64|128http、server、location为了提高快速寻找到相应server name的能力,Nginx使用散列表来存储server name。server_names_hash_bucket_size设置了每个散列桶占用的内存大小。
- server_names_hash_max_size size;server_names_hash_max_size 512;http、server、locationserver_names_hash_max_size会影响散列表的冲突率。
- server_name_in_redirect on|off;server_name_in_redirect on;http、server或者location重定向主机名称的处理该配置需要配合server_name使用。在使用on打开时,表示在重定向请求时会使用server_name里配置的第一个主机名代替原先请求中的Host头部,而使用off关闭时,表示在重定向请求时使用请求本身的Host头部。
- location[=|||^~|@]/uri/{…}serverlocation 会尝试根据用户请求中的 URL 来匹配上面的/uri 表达式,(uri 参数可以使用正则表达式)如果可以匹配,就选择 location{}块中的配置来处理用户请求。当然 location 的匹配方式多样:=表示完全匹配。表示字母大小写敏感匹配。表示忽略字母大小写匹配。^~表示前半部分相同匹配。@表示仅用于 Nginx 服务内部请求之间的重定向,带有@的location不直接处理用户请求。/表示匹配所有请求。
文件路径的定义
- root path(默认 root html)http、server、location、if定义资源文件相对于 HTTP 请求的根目录。
- alias pathlocation也是用来设置文件资源路径,它与 alias 不同点主要在于如何处理 location 后面的 uri 参数。使用 alias 时会丢弃 location 后面的 uri 参数,root 则不然,它会根据完整的 uri 请求来映射。
- index file···(默认 index index.html)http、server、location有时,访问站点时的 url 是 /,这时一般是返回网站的首页。可以后缀多个文件参数,Nginx 会按照顺序来访问这些文件,如果可以访问则直接返回文件内容并结束请求,否则尝试访问其他。
- error_page code [code···] /sth.htmlhttp server location if对于某个请求返回错误码时,如果匹配上 error_page 中设置的 code,则重定向到新的 URL 中。虽然重定向 URI,但返回的 HTTP 错误码与原来相同。用户可以使用 = 来更改返回的错误码,或者不指定确切的错误码,而是由重定向后的实际处理结果决定。error_page 404 =200 /empty.giferror_page 404 = /empty.gif如果不想修改 uri,可以想让这样的请求重定向到另一个 location 中进行处理:error_page 404 @fallback<br />location @fallback{}
- recursive_error_pages[on|off];
- try_files path1[path2]uri;尝试按照顺序访问每一个path,如果可以有效地读取,就直接向用户返回这个path对应的文件结束请求,否则继续向下访问。如果所有的path都找不到有效的文件,就重定向到最后的参数uri上。因此,最后这个参数uri必须存在,而且它应该是可以有效重定向的。
内存及磁盘资源的分配
处理请求时内存、磁盘资源的分配项。
- client_body_in_file_only on|clean|off;(默认 client_body_in_file_only off;)http、server、locationHTTP 包体只存储到磁盘文件中。当值为非off时,用户请求中的HTTP包体一律存储到磁盘文件中,即使只有0字节也会存储为文件。当请求结束时,如果配置为on,则这个文件不会被删除(该配置一般用于调试、定位问题),但如果配置为clean,则会删除该文件。
- client_body_in_single_buffer on|off;(默认 client_body_in_single_buffer off;)http、server、locationHTTP包体尽量写入到一个内存buffer中。用户请求中的HTTP包体一律存储到内存buffer中。当然,如果HTTP包体的大小超过了下面client_body_buffer_size设置的值,包体还是会写入到磁盘文件中。
- client_header_buffer_size size;(默认 client_header_buffer_size 1k;)http、server存储HTTP头部的内存buffer大小。定义了正常情况下Nginx接收用户请求中HTTP header部分(包括HTTP行和HTTP头部)时分配的内存buffer大小。有时,请求中的HTTP header部分可能会超过这个大小,这时large_client_header_buffers定义的buffer将会生效。
- large_client_header_buffers number size;(默认 large_client_header_buffers 48k; )http、server存储超大HTTP头部的内存buffer大小。large_client_header_buffers定义了Nginx接收一个超大HTTP头部请求的buffer个数和每个buffer的大小。如果HTTP请求行(如GET/indexHTTP/1.1)的大小超过上面的单个buffer,则返回"Request URI toolarge"(414)。请求中一般会有许多header,每一个header的大小也不能超过单个buffer的大小,否则会返回"Bad request"(400)。当然,请求行和请求头部的总和也不可以超过buffer个数*buffer大小。
- client_body_buffer_size size;(默认 client_body_buffer_size 8k/16k;)http、server、location定义了Nginx接收HTTP包体的内存缓冲区大小。也就是说,HTTP包体会先接收到指定的这块缓存中,之后才决定是否写入磁盘。如果用户请求中含有HTTP头部Content-Length,并且其标识的长度小于定义的buffer大小,那么Nginx会自动降低本次请求所使用的内存buffer,以降低内存消耗。
- client_body_temp_path dir-path[level1[level2[level3]]](默认 client_body_temp_path client_body_temp;)http、server、location定义HTTP包体存放的临时目录。在接收HTTP包体时,如果包体的大小大于client_body_buffer_size,则会以一个递增的整数命名并存放到client_body_temp_path指定的目录中。后面跟着的level1、level2、level3,是为了防止一个目录下的文件数量太多,从而导致性能下降,因此使用了level参数,这样可以按照临时文件名最多再加三层目录。
- connection_pool_size size;(默认 connection_pool_size 256)Nginx 对于每个建立成功的 TCP 连接会预先分配一个内存池,size 指定内存池的初始大小,用于减少内核对于小块内存的分配次数。需要谨慎设置,因为更大的 size 会使服务器消耗的内存增多,而更小的 size 会引发更多的内存分配次数。
- request_pool_size size;(默认 request_pool_size 4k;)http、serverNginx 开始处理 HTTP 请求时将会为每个请求都分配一个内存池,size 指定内存池的初始大小,用于减少内核对于小块内存的分配次数。TCP 连接关闭时会销毁 connection_pool_size 指定的连接内存池,HTTP 请求结束时会销毁 request_pool_size 指定的请求内存池,但它们的创建、销毁时间并不一致。(因为一个 TCP 连接可能被复用于多个 HTTP 请求)
网络连接的设置
- client_header_timeout time(client_header_timeout 60;)http、server、location读取HTTP头部的超时时间。客户端与服务器建立连接后将开始接收HTTP头部,在这个过程中,如果在一个时间间隔(超时时间)内没有读取到客户端发来的字节,则认为超时,并向客户端返回408("Request timed out")响应。
- client_body_timeout time( client_body_timeout 60;)http、server、location读取HTTP包体的超时时间。此配置项与client_header_timeout相似,只是这个超时时间只在读取HTTP包体时才有效。
- send_timeout time(send_timeout 60;)http、server、location发送响应的超时时间。这个超时时间是发送响应的超时时间,即Nginx服务器向客户端发送了数据包,但客户端一直没有去接收这个数据包。如果某个连接超过send_timeout定义的超时时间,那么Nginx将会关闭这个连接。
- reset_timeout_connection on|off;(reset_timeout_connection off;)连接超时后将通过向客户端发送RST包来直接重置连接。这个选项打开后,Nginx会在某个连接超时后,不是使用正常情形下的四次握手关闭TCP连接,而是直接向用户发送RST重置包,不再等待用户的应答,直接释放Nginx服务器上关于这个套接字使用的所有缓存(如TCP滑动窗口)。相比正常的关闭方式,它使得服务器避免产生许多处于FIN_WAIT_1、FIN_WAIT_2、TIME_WAIT状态的TCP连接。注意,使用RST重置包关闭连接会带来一些问题,默认情况下不会开启。
- lingering_close off|on|always;(lingering_close on;)http、server、location该配置控制Nginx关闭用户连接的方式。always表示关闭用户连接前必须无条件地处理连接上所有用户发送的数据。off表示关闭连接时完全不管连接上是否已经有准备就绪的来自用户的数据。on是中间值,一般情况下在关闭连接前都会处理连接上的用户发送的数据,除了有些情况下在业务上认定这之后的数据是不必要的。
- lingering_time(lingering_time 30s;)http、server、locationlingering_close启用后,这个配置项对于上传大文件很有用。上文讲过,当用户请求的Content-Length大于max_client_body_size配置时,Nginx服务会立刻向用户发送413(Request entity too large)响应。但是,很多客户端可能不管413返回值,仍然持续不断地上传HTTP body,这时,经过了lingering_time设置的时间后,Nginx将不管用户是否仍在上传,都会把连接关闭掉。
- lingering_timeout( lingering_timeout 5s;)http、server、locationlingering_close生效后,在关闭连接前,会检测是否有用户发送的数据到达服务器,如果超过lingering_timeout时间后还没有数据可读,就直接关闭连接;否则,必须在读取完连接缓冲区上的数据并丢弃掉后才会关闭连接。
- keepalive_timeout time(keepalive_timeout 75;)http、server、location一个keepalive连接在闲置超过一定时间后(默认的是75秒),服务器和浏览器都会去关闭这个连接。当然,keepalive_timeout配置项是用来约束Nginx服务器的,Nginx也会按照规范把这个时间传给浏览器,但每个浏览器对待keepalive的策略有可能是不同的。
- keepalive_requests n;(keepalive_requests 100;)一个keepalive连接上默认最多只能发送100个请求。
MIME类型的设置
- type{…};http、server、location定义MIME type到文件扩展名的映射。多个扩展名可以映射到同一个MIME type。
- default_type MIME-type;(default_type text/plain;)http、server、location当找不到相应的MIME type与文件扩展名之间的映射时,使用默认的MIME type作为HTTP header中的Content-Type。
- types_hash_bucket_size
- types_hash_max_size
对客户端请求的限制
- limit_except method…{…}locationNginx通过limit_except后面指定的方法名来限制用户请求。方法名可取值包括:GET、HEAD、POST、PUT、DELETE、MKCOL、COPY、MOVE、OPTIONS、PROPFIND、PROPPATCH、LOCK、UNLOCK或者PATCH。
- client_max_body_size size;(client_max_body_size 1m;)http、server、location浏览器在发送含有较大HTTP包体的请求时,其头部会有一个Content-Length字段,client_max_body_size是用来限制Content-Length所示值的大小的。因此,这个限制包体的配置非常有用处,因为不用等Nginx接收完所有的HTTP包体——这有可能消耗很长时间——就可以告诉用户请求过大不被接受。例如,用户试图上传一个10GB的文件,Nginx在收完包头后,发现Content-Length超过client_max_body_size定义的值,就直接发送413("RequestEntity Too Large")响应给客户端。
- limit_rate speed;(limit_rate 0;)http、server、location、if此配置是对客户端请求限制每秒传输的字节数。speed可以使用2.2.4节中提到的多种单位,默认参数为0,表示不限速。针对不同的客户端,可以用$limit_rate参数执行不同的限速策略。
- limit_rate_after time;(limit_rate_after 1m;)http、server、location、if此配置表示Nginx向客户端发送的响应长度超过limit_rate_after后才开始限速。
文件操作的优化
- sendfile on|off;(sendfile off;)http、server、location可以启用Linux上的sendfile系统调用来发送文件,它减少了内核态与用户态之间的两次内存复制,这样就会从磁盘中读取文件后直接在内核态发送到网卡设备,提高了发送文件的效率。
- aio on|off;(aio off;)http、server、location此配置项表示是否在FreeBSD或Linux系统上启用内核级别的异步文件I/O功能。注意,它与sendfile功能是互斥的。
- directio size|off;
- directio_alignment
- open_file_cache max=N[inactive=time]|off;(off)http、server、location打开文件缓存
- open_file_cache_errors on|off;(off)此配置项表示是否在文件缓存中缓存打开文件时出现的找不到路径、没有权限等错误信息。
对客户端请求的特殊处理
- ignore_invalid_headers on|off;(on)忽略不合法的HTTP头部如果将其设置为off,那么当出现不合法的HTTP头部时,Nginx会拒绝服务,并直接向用户发送400(Bad Request)错误。如果将其设置为on,则会忽略此HTTP头部。
- underscores_in_headers on|off;(off)HTTP头部是否允许下划线。默认为off,表示HTTP头部的名称中不允许带“_”(下划线)。
- if_modified_since[off|exact|before];(exact)http、server、location出于性能考虑,Web 浏览器一般会在客户端本地缓存一些文件,并存储获取时间。这样,下次向 Web 服务器获取缓存过的资源时,就可以使用 If-Modified-Since 头部把上次获取的时间捎带上,而 if_modified_since 将根据参数决定如何处理该头部:off:表示忽略用户请求中的If-Modified-Since头部。这时,如果获取一个文件,那么会正常地返回文件内容。HTTP响应码通常是200。exact:将If-Modified-Since头部包含的时间与将要返回的文件上次修改的时间做精确比较,如果没有匹配上,则返回200和文件的实际内容,如果匹配上,则表示浏览器缓存的文件内容已经是最新的了,没有必要再返回文件从而浪费时间与带宽了,这时会返回304 Not Modified,浏览器收到后会直接读取自己的本地缓存。before:是比exact更宽松的比较。只要文件的上次修改时间等于或者早于用户请求中的If-Modified-Since头部的时间,就会向客户端返回304 NotModified。
- log_not_found on|off;(on)文件未找到时是否记录到error日志。
- resolver address…;设置DNS名字解析服务器的地址。
- resolver_timeout time;(30)表示DNS解析的超时时间。
- server_tokens on|off;(on)返回错误页面时是否在Server中注明Nginx版本。
2.5 HTTP proxy module 配置反向代理服务器
反向代理方式(reverse proxy)是指用代理服务器接受 Internet 的连接请求,然后将请求转发给内部网络中的上游服务器(upstream),并将从上游服务器上取得的结果返回给请求连接的客户端,反向代理服务器必须能够处理大量并发请求。
Nginx 具有高并发高负载能力,一方面可以作为前端的服务器直接向客户端提供静态文件服务,另一方面在面对复杂多变的业务时可以转发动态请求到 Apache、Tomcat 等服务器来处理。于是 Nginx 通常会被配置为既是静态 Web 服务器,也是反向代理服务器,不适合 Nginx 处理的请求就会被直接转发到上游服务器进行处理。
与其他反向代理服务器相比,Nginx 有自己的特点:
当客户端发来 HTTP 请求时,Nginx 并不会立刻转发到上游服务器,而是先把用户请求完整接受到 Nginx 所在服务器的硬盘或者内存,然后再向上游服务器发起连接,把缓存的客户端请求转发到上游服务器。而 Squid 等 dialing 服务器则采用一边接受客户端请求,一边转发到上游服务器的方式。
Squid 等反向代理服务器在与客户端建立连接且还没有开始接受 HTTP 包体时就已经向上游服务器建立连接。上游服务器在接受完整 HTTP 包体的漫长过程中要始终维持这个链接,这直接对上游服务器的并发能力提出挑战。Nginx 则不然,它在接收到完整请求,才会与上游服务器建立连接并转发请求,由于是内网,这个转发过程会执行得很快。这样一个客户端请求占用上游服务器的连接时间就会非常短,也就是说,Nginx 的这种反向代理方案主要是为了降低上游服务器的并发压力。
优点是降低上游服务器负载,缺点是延长请求处理时间并增加开销(用于缓存请求内容的内存和磁盘空间)
负载均衡基本配置
作为代理服务器,一般都需要向上游服务器的集群转发请求。这里的负载均衡是指选择一种策略,尽量把请求平均地分布到每一台上游服务器上。
- upstream name{…}httpupstream块定义了一个上游服务器的集群,便于反向代理中的proxy_pass使用。
- server name[parameters];upstreamserver配置项指定了一台上游服务器的名字,这个名字可以是域名、IP地址端口、UNIX句柄等,在其后还可以跟下列参数。weight=number:设置向这台上游服务器转发的权重,默认为1。max_fails=number:该选项与fail_timeout配合使用,指在fail_timeout时间段内,如果向当前的上游服务器转发失败次数超过number,则认为在当前的fail_timeout时间段内这台上游服务器不可用。max_fails默认为1,如果设置为0,则表示不检查失败次数。fail_timeout=time:fail_timeout表示该时间段内转发失败多少次后就认为上游服务器暂时不可用,用于优化反向代理功能。它与向上游服务器建立连接的超时时间、读取上游服务器的响应超时时间等完全无关。fail_timeout默认为10秒。down:表示所在的上游服务器永久下线,只在使用ip_hash配置项时才有用。backup:在使用ip_hash配置项时它是无效的。它表示所在的上游服务器只是备份服务器,只有在所有的非备份上游服务器都失效后,才会向所在的上游服务器转发请求。
- ip_hashupstream在有些场景下,我们可能会希望来自某一个用户的请求始终落到固定的一台上游服务器中。ip_hash就是用以解决上述问题的,它首先根据客户端的IP地址计算出一个key,将key按照upstream集群里的上游服务器数量进行取模,然后以取模后的结果把请求转发到相应的上游服务器中。这样就确保了同一个客户端的请求只会转发到指定的上游服务器中。ip_hash与weight(权重)配置不可同时使用。如果 upstream 集群中有一台上游服务器暂时不可用,不能直接删除该配置,而是要 down 参数标识,确保转发策略的一贯性。
反向代理基本配置
- proxy_pass URL;location、if此配置项将当前请求反向代理到URL参数指定的服务器上,URL可以是主机名或IP地址加端口的形式,也可以是 UNIX 句柄,还可以直接使用 upstream 块。
- proxy_method method;http、server、location此配置项表示转发时的协议方法名。例如设置为:proxy_method POST;那么客户端发来的GET请求在转发时方法名也会改为POST。
- proxy_hide_header the_header & proxy_pass_headerhttp、server、locationNginx会将上游服务器的响应转发给客户端,但默认不会转发以下HTTP头部字段:Date、Server、X-Pad和X-Accel-*。使用proxy_hide_header后可以任意地指定哪些HTTP头部字段不能被转发。与proxy_hide_header功能相反,proxy_pass_header会将原来禁止转发的header设置为允许转发。
- proxy_pass_request_body on|off;(on)http、server、location作用为确定是否向上游服务器发送HTTP包体部分。
- proxy_pass_request_headers on|off;(on)http、server、location作用为确定是否转发HTTP头部。
- proxy_redirect[default|off|redirect replacement];(default)http、server、location当上游服务器返回的响应是重定向或刷新请求(如HTTP响应码是301或者302)时,proxy_redirect可以重设HTTP头部的location或refresh字段。例如,如果上游服务器发出的响应是302重定向请求,location字段的URI是http://localhost:8000/two/some/uri/ ,那么在下面的配置情况下,实际转发给客户端的location是http://frontend/one/some/uri/ 。
- proxy_next_upstream[error|timeout|invalid_header|http_500|http_502|http_503|http_504|http_404|off];(proxy_next_upstream error timeout;)http、server、location此配置项表示当向一台上游服务器转发请求出现错误时,继续换一台上游服务器处理这个请求。前面已经说过,上游服务器一旦开始发送应答,Nginx反向代理服务器会立刻把应答包转发给客户端。因此,一旦Nginx开始向客户端发送响应包,之后的过程中若出现错误也是不允许换下一台上游服务器继续处理的。这很好理解,这样才可以更好地保证客户端只收到来自一个上游服务器的应答。proxy_next_upstream的参数用来说明在哪些情况下会继续选择下一台上游服务器转发请求。
Nginx 模块
wait
Nginx 原理
Nginx 基础架构
Web 服务器设计中的关键约束
Nginx作为Web服务器受制于Web传输协议自身的约束,另外,下面将说明的7个关注点也是Nginx架构设计中的关键约束。
- 性能
- 网络性能
- 单次请求延迟
- 网络效率
- 可伸缩性
- 简单性
- 可修改性
- 可见性
- 可移植性
- 可靠性
Nginx 架构设计
模块化设计
事件驱动架构
_事件驱动__:一些事件发生源产生事件,由一个或者多个事件收集器来收集、分发事件,许多时间处理器会注册自己感兴趣的时间,同时消费这些事件。__对于__传统 Web 服务器__而言,采用的事件驱动局限在 TCP 连接建立、关闭事件上,一个连接建立以后,在其关闭之前的所有操作都不是事件驱动,此时退化成按序执行每个操作的批处理模式,这样每个请求在连接建立后都将始终占用系统资源,直到连接关闭才会释放,这造成服务器资源的浪费。对于 Nginx __而言,它不会使用进程或者线程作为事件消费者,事件消费者只能是某个模块。只有事件收集、分发器才有资格占用进程资源,它们会在分发某个事件时调用事件消费模块使用当前占用资源。_前者是每个事件消费者独占一个进程资源,后者的事件消费者只是被事件分发者进程短期调用。这样的设计使得网络性能、用户感知的请求时延都得到提升,整个服务器的网络吞吐量都会由于事件的基石响应而增大。但也带来一个问题:每个事件消费者都不能有阻塞行为,否则将会由于长时间占用事件分发者进程而导致其他事件得不到及时响应。
对于 Nginx 而言,一般由网卡、磁盘产生事件。事件模块负责事件的手机、分发操作。其他所有模块都可能是时间消费者,它们需要先向事件模块注册感兴趣的事件类型。
多阶段异步处理请求
多阶段异步处理请求给予事件驱动架构实现。
所谓多阶段,即一个请求的处理过程按照事件的触发方式划分为多个阶段,每个阶段可以由事件收集、分发器来触发。
至于异步则表现为当一个事件被分发到事件消费者中进行处理,事件消费者处理完这个事件相当于处理完请求的某个阶段,然后等待下次事件出现的时候,epoll 等事件分发器获取通知,继续调用事件消费者处理请求,这样每个阶段中的事件消费者都只能异步被动的等待通知。
这种设计配合配合事件驱动架构,将会提升网络性能,同时使得每个进程全力运转(尽量少地出现进程休眠)。
基于请求处理流程中的阻塞方法划分阶段。
- _将阻塞进程的方法按照相关的触发事件分解成两个阶段。 __阻塞方法改为非阻塞方法调用,这个调用非阻塞方法并将进程归还给事件分发器的阶段就是第一阶段。 __增加新的处理阶段用于处理非阻塞方法最终返回的结果,这里的结果返回事件就是第二阶段的触发事件。_
- 将阻塞方法调用按照时间分解为多个阶段的方法调用。
- 进程空转时,使用定时器划分阶段。
- 如果阻塞方法完全无法继续划分,则必须使用独立的进程执行这个阻塞方法。
管理进程、多工作进程设计
事件和进程的关系事件循环:每个工作进程都有自己的事件循环(event loop)。事件循环是事件驱动架构的核心,它不断检查是否有新的事件发生,并根据事件类型调用相应的处理函数。事件循环通常使用高效的 I/O 多路复用技术(如 epoll、kqueue、select 等)来管理多个文件描述符的 I/O 事件。进程间协作:虽然每个工作进程都有自己的事件循环,但它们共享同一个监听套接字(listening socket)。这意味着所有的工作进程都可以接收新的连接请求。当一个新的连接请求到达时,Nginx 会使用某种策略(如轮询、最少连接数等)将请求分配给某个工作进程。一旦某个工作进程接受了连接请求,该连接的所有后续 I/O 操作都由这个工作进程处理,直到连接关闭。负载均衡:通过多个工作进程,Nginx 可以在多核 CPU 上并行处理请求,从而实现负载均衡。每个工作进程独立运行,减少了进程间的同步开销,提高了系统的整体性能。
平台无关的代码实现
在使用C语言实现Nginx时,尽量减少使用与操作系统平台相关的代码,如某个操作系统上的第三方库。Nginx重新封装了日志、各种基本数据结构(如第7章中介绍的容器)、常用算法等工具软件,在核心代码都使用了与操作系统无关的代码实现,在与操作系统相关的系统调用上则分别针对各个操作系统都有独立的实现,这最终造就了Nginx的可移植性,实现了对主流操作系统的支持。
内存池设计
Nginx 设计简单的内存池。
它通常不负责回收内存池中已经分配出的内存,它的优点在于把多次向系统申请内存的操作整合成一次,减少 CPU 资源消耗和内存碎片。
因此,通常每一个请求都有一个独立的线程池,在请求结束的时候销毁整个内存池,把曾经分配的内存一次性归还给操作系统。
这种设计他提高了模块开发的简单性,分配内存次数的减少使得请求时延降低,同时,通过减少内存碎片,提高了内存的有效利用率和系统可处理的并发连接数。
核心结构体 ngx_cycle_t
Nginx 启动时框架处理流程
- 根据命令行得到配置文件路径。
- 如果处于升级,则监听环境变量中传递的监听句柄。
- 调用所有核心模块的 create.conf 方法生成存放配置项的结构体。
- 调用配置模块解析的配置项方法。 遍历所有配置项,对于任一个配置项,将会检查所有核心模块以找出对他感兴趣的模块,并调用该模块在 ngx_command_t 结构体中定义的配置项处理方法。
- 调用所有核心模块的 init_conf 方法。 这一步骤的目的在于让所有核心模块在解析完配置项后可以做综合性处理。
- 创建目录、打开文件、初始化共享内存等进程间通信方式。
- 打开各个模块从配置文件中读取到的监听端口并监听端口。
- 调用所有模块的 init_module 方法。
- 如果配置为 single 工作模式。
- 则调用 ngx_single_process_cycle 方法进入单进程工作模式。
- 调用所有模块的 init_process 方法,单进程工作模式启动完成。
- 如果配置为 master、worker 工作模式。
- 在启动worker子进程、cache manage子进程、cache loader子进程后,master进程启动流程执行完毕。
- 由 master 进程按照配置文件中 worker 进程的数目,启动子进程。调用所有模块的 init_process 方法,worker 进程的启动工作完成,进入正常的循环处理事件流程。
- 由 master 进程根据之前各模块的初始化情况决定是否启动 cache manager 子进程或者 cache loader 子进程。
事件模块
事件处理框架概述事件处理框架要解决的问题是如何收集、管理、分发事件。事件主要以网络事件和定时器事件为主,而网络事件又以 TCP 网络事件为主。由于网络事件与网卡中断处理程序、内核提供的系统调用密切相关,所以网络事件驱动取决于不同的操作系统平台以及操作系统内核版本。(通常事件驱动机制也被叫做 I/O 多路复用),所以事件框架需要在不同的操作系统内核中选择一种事件驱动机制支持网络事件的处理。(Nginx 定义了一系列运行在不同操作系统、不同内核版本上的事件驱动模块,在 ngx_event_core_module 模块初始的过程选择)
epoll 事件驱动模块
epoll 原理
采用红黑树管理事件,并通过回调实现事件通知,以达到事件触发后被收集到特定数据结构(rdlist),epoll 只需要遍历 rdlist 就可以实现同时监控并处理大量 fd。
epoll_create: 创建 epoll 对象,每一个 epoll 对象都有一个独立的 eventepoll 结构体(主要是红黑树和双向列表)。结构体会在内核空间中创建独立内存,用于存储 epoll_ctl 向 epoll 对象添加的事件,这些事件都会被挂到 rbr 红黑树。_在 epoll 中每一个事件都会建立 epitem 结构体,这里包含每个事件的信息,红黑树中每个节点都是基于 epitem 结构中的 rbn 成员。所有添加到 epoll 对象的事件都会与设备驱动程序建立回调关系,事件发生时会调用回调方法(epoll_callback),然后事件会添加到 rdlist 中。epoll_ctl 向 epoll 对象中添加、修改、删除事件时,从 rbr 红黑树中查找事件非常快。epoll_wait 检查是否有发生事件的连接时,只是检查 eventpoll 对象中 rdlist 是否存在 epitem 元素,存在则复制事件到用户态内存,同时返回事件数量给用户。
epoll 使用
epoll 有 LT 模式(水平触发)和 ET 模式(边缘触发)两种工作模式。
默认情况下,epoll 使用水平触发模式,这时可以处理阻塞和非阻塞套接字。边缘触发模式的效率要更高,但是只支持非阻塞套接字。
二者区别在:ET 模式下 epoll_wait 只在文件描述符状态发生变化时返回,比如从不可读变为可读。这意味着如果应用程序这次没有处理完所有数据,即使文件描述符仍然处于可读状态, epoll_wait 不会再返回该文件描述符,直至下次状态改变。LT模式则相反,只要一个事件对应的套接字缓冲区还有数据,就总能从 epoll_wait中获取这个事件。
实际意义:在 LT 模式下如果一个文件描述符长时间保持可读状态,那可能会导致频繁的 epoll_wait 唤醒,从而增加 CPU 开销,而在 ET 模式下,只有当状态真正改变时才会被唤醒,有助于减少 CPU 负载。
ngx_epoll_module 模块实现
定时器事件
在网络事件中,网络事件的触发是由内核完成的,内核如果支持epoll就可以使用ngx_epoll_module模块驱动事件,内核如果仅支持select那就得使用ngx_select_module模块驱动事件;定时器事件则完全是由Nginx自身实现的,它与内核完全无关。
- 作者:宗海
- 链接:https://nowave.cloud//article/1f7beb96-1d72-8019-b3ef-f040690a4a8f
- 声明:本文采用 CC BY-NC-SA 4.0 许可协议,转载请注明出处。

