Nginx基本概念
Nginx(发音为“engine-x”)是用于 HTTP、HTTPS、SMTP、POP3 和 IMAP 协议的开源反向代理服务器,以及负载均衡器、
HTTP 缓存和 Web 服务器(源服务器)。nginx 项目一开始就非常关注高并发、高性能和低内存使用,性能是其最重要的考量,
实现上非常注重效率,能经受高负载的经验,有报告表明能支持高达50000个并发连接数。

一个Master和多个Worker的好处?
- 可以使用nginx -s reload热部署,利于nginx进行热部署操作
- 每个worker是独立的进程,如果有一个worker出现问题,其他worker是独立的,继续进行争抢,实现请求过程,不会造成服务中断。
设置多少Worker合适?
Nginx同Redis类似都采用io多路复用机制,每个Worker都是一个独立的进程,
但每个进程里只有一个主线程,通过异步非阻塞方式来处理请求,即使是成千上万个请求也不在话下,
每个Worker的线程可以把一个Cpu的性能发挥到极致.所以worker数和服务器的Cpu数相等是最为适宜。
设少了会浪费Cpu,设多了会造成Cpu平凡切换上下文带来损耗。
发送请求,占用了worker的几个连接数(worker connection)?
两个或者四个(看应用场景)
Nginx有一个master,有四个worker,每个worker支持最大的连接数是1024,支持的并发数是多少个?
普通的静态访问最大并发数:worker_connectionworker_process/2
作为反向代理来说,最大支持并发数量应该是:worker_connectionworker_process/4
Nginx安装、常用命令
最简安装
1 | docker run --name daibeisi-nginx -v ./html:/usr/share/nginx/html:ro -d nginx |
普通安装
高可用
常用命令
- 查看nginx的版本号 nginx -v
- 启动nginx nginx
- 关闭nginx nginx -s stop
- 重新加载nginx ./nginx -s reload
Nginx配置
配置文件介绍
配置文件位置
/usr/local/nginx/conf/nginx.conf。配置文件组成
全局块
从配置文件开始到events块之间的内容,主要会设置一些影响nginx服务器整体运行的配置指令,主要包括配置运行nginx服务器的用户(组)、
允许生成的work process数,进程PID存放路径、日志存放路径和类型以及配置文件的引入等。
例如worker processes 1;worker_processes值越大,可以支持的并发处理量也越多。events块
events块涉及的指令主要影响nginx服务器与用户的网络连接,常用的设置包括是否开启对多work process下的网络连接进行序列化,
是否允许同时接收多个网络连接,选取哪种事件驱动模型来处理连接请求, 每个word process可以同时支持的最大连接数等。
events { worker_connections 1024; } 上述例子就表示每个work process支持的最大连接数为1024。
这部分的配置对nginx的性能影响较大,在实际中应该灵活配置。http块
这块是nginx服务器中配置最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。需要注意的是,
http块也可以包括http全局块、server块。http全局块,http全局块配置的指令包括文件引入、MIMIE-TYPE定义、
连接超时时间、单链接请求数上限等。server块
server块,这块和虚拟主机有密切关系,虚拟主机从用户角度看,和一台独立的硬件主机是完全一样的,
该技术的产生是为了节省互联网服务器的硬件成本。每个http块可以包括多个server块,而每个server块就相当于一个虚拟主机。
而每个server块也分为全局server块,以及可以同时包含多个location块。location块
如果url包含正则表达式,则必须要有或者标识。location [ = | ~ | ~ | ^~] url { }
- =:用于不含正则表达式的url前,要求请求字符串与url严格匹配,如果匹配成功,就停止继续向下搜索并立即处理该请求。
- ~:用于表示url包含正则表达式,并且区分大小写。
- ~*:用于表示url包含正则表达式,并且不区分大小写。
- ^~:用于不包含正则表达式的url前,要求nginx服务器找到标识url和请求字符串匹配度最高的location后,立即使用此location处理请求,而不再用location块中的正则url和请求字符串做匹配。
6.
配置文件详解
nginx.conf
正向代理

介绍:在客户端(浏览器)配置代理服务器,通过代理服务器进行互联网访问。
实现效果:打开浏览器,设置代理服务器,可以访问无法访问的其他域地址。
具体配置:
1 | server { |
反向代理

介绍:反向代理,其实客户端对代理是无感知的,因为客户端不需要任何配置就可以访问,我们只需要将请求发送到反向代理服务器,
由反向代理服务器去选择目标服务器获取数据后,再返回给客户端,此时反向代理服务器和目标服务器对外就是一个服务器,暴露的是代理服务器的地址,
隐藏了真实服务器IP地址。
实现效果:打开浏览器,在浏览器地址栏输入网址www.bookhub.com.cn,跳转linux系统中部署的服务主页。
具体配置:
1 | server { |
负载均衡

介绍:单个服务器解决不了,我们增加服务器的数量,然后将请求分发到各个服务器上,
将原先请求集中到单个服务器上的情况改为将请求分发到多个服务器上,将负载分发到不同的服务器,也就是我们所说的负载均衡。
随着互联网信息爆炸性增长,负载均衡已经不再是一个很陌生的话题,顾名思义,负载均衡即是将负载分摊到不同的服务单元,既保证服务的可用性,
又保证响应的足够快,给用户很好的体验。在linux下有nginx、LVS、Haproxy等等服务可以提供负载均衡服务,
而且Nginx提供了几种分配方式:
轮询每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。权重weight代表权重,默认为一,权重越高被分配的客户端越多。 指定轮询几率,weight和访问率成正比,用于后端服务器性能不均的情况。ip_hash每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。Fair按后端服务器的响应时间来分配请求,响应时间短的优先分配。
实现效果:浏览器输入网址,请求按某种规则分配到后端服务上。
具体配置:
1 | http{ |
动静分离

介绍:为了加快网站的解析速度,可以把动态页面和静态页面由不同的服务器来解析,加快解析速度。降低原来单个服务器的压力。
Nginx动静分离简单来说就是把静态和动态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。
动静分离从目前实现角度来讲大致分两种,一种是纯粹把静态文件独立成单独域名,放在独立的服务器上,也是目前主流推崇的方案。
另外一种方案是动态文件跟静态文件混合在一起发布,通过Nginx分开。通过location指定不同的后缀名实现不同的请求转发,
通过expires参数设置,可以设置浏览器缓存的过期时间,减少客户端对服务器之间的请求和流量。
具体expires定义:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确认是否过期即可,
所以不会产生额外的流量,此种方法非常适合不经常变动的资源(如果经常更新的文件,不建议使用expirse来缓存),
设置3d,表示在这三天之内去访问这个url,发送一个请求,比对该文件最后更新时间没有变化,则不会从服务器抓取,
返回状态码304,如果有修改,则直接从服务器重新下载,返回状态码200。
实现效果:访问静态文件直接请求文件服务器,其他请求才会访问后端服务。
配置:
1 | location /static { |
SSL证书配置
介绍:HTTPS比HTTP多了安全验证,通过传输加密和身份认证保证了传输过程的安全性。拥有HTTPS的网站不会提示用户此网站不安全。
实现效果:浏览器访问网站,网页地址栏出现小锁标志
配置:
1 | server { |
HTTP重定向到HTTPs
老式的方式将HTTP重定向到HTTPs
强制从 HTTP 到 HTTPs 重定向的经典方法是让一个 vhost 在端口 :80 上监听,另一个在端口 :443 上监听,并让端口 :80 vhost 将所有流量重定向到 HTTPs 版本。1
2
3
4
5
6
7
8
9
10
11
12
13
14server {
listen 80;
server_name .bookhub.com.cn;
# 301 = permanent redirect, 302 = temporary redirect
return 301 https://your.site.tld$request_uri;
}
server {
listen 443 ssl;
server_name .bookhub.com.cn;
ssl on;
...
}强制在非标准端口上进行HTTP重定向
如果您在自定义端口上运行 HTTPs/SSL,则不能使用该技巧,因为没有“不安全”端口 :80 可以捕获请求以重定向它们。Nginx 创建了一个自定义HTTP 状态代码,允许您强制重定向任何通过 HTTP 浏览虚拟主机的人到 HTTPs 版本,称为497。仅当您在自定义端口上运行 SSL 时才需要这样做,否则您可以使用上面显示的配置进行重定向。1
2
3
4
5
6
7
8server {
listen 1234 ssl;
server_name your.site.tld;
ssl on;
...
error_page 497 https://$host:1234$request_uri;
...
}
资源压缩
介绍:Nginx资源压缩建立在动静分离的基础之上,如果一个静态资源的size越小,那么自然传输速度会更快,同时也会更节省带宽。
在项目部署时,可以通过Nginx对于静态资源实现压缩传输,一方面可以节省带宽资源,第二方面也可以加快响应速度并提升系统吞吐。
Nginx提供了3个支持资源压缩的模块ngx_http_gzip_module、ngx_http_gzip_static_module和ngx_http_gunzip_module,
其中ngx_http_gzip_module属于内置模块,代表可以直接使用该模块下的一些压缩命令,部分配置指令如下。
实现效果:传输文件被压缩,降低带宽占用。
具体配置:
1 | http{ |
IP黑白名单
介绍:有时候往往有些需求,可能某些接口只能开放给对应的合作商,或者购买/接入API的合作伙伴,那么此时就需要实现类似于IP白名单的功能。
而有时候有些恶意攻击者或爬虫程序,被识别后需要禁止其再次访问网站,因此也需要实现IP黑名单。
那么这些功能无需交由后端实现,可直接在Nginx中处理。
Nginx做黑白名单机制,主要是通过allow、deny配置项来实现。要同时屏蔽、开放多个IP访问时,
如果所有IP全部写在nginx.conf文件中定然是不显示的,这种方式比较冗余,那么可以新建两个文件BlocksIP.conf、WhiteIP.conf。
分别将要禁止、开放的IP添加到对应的文件后,可以再将这两个文件在nginx.conf中导入。
对于文件具体在哪儿导入,这个也并非随意的,如果要整站屏蔽、开放就在http中导入,如果只需要一个域名下屏蔽、开放就在sever中导入,
如果只需要针对于某一系列接口屏蔽/开放IP,那么就在location中导入。
上述只是最简单的IP黑/白名单实现方式,同时也可以通过ngx_http_geo_module、ngx_http_geo_module
第三方库去实现(这种方式可以按地区、国家进行屏蔽,并且提供了IP库)。
实现效果:根据配置的黑白名单,开发还是拒绝对应ip请求。
具体配置:
1 | http{ |
跨域配置
介绍:产生跨域问题的主要原因就在于同源策略,为了保证用户信息安全,防止恶意网站窃取数据,同源策略是必须的,
否则cookie可以共享。由于http无状态协议通常会借助cookie来实现有状态的信息记录,例如用户的身份/密码等,
因此一旦cookie被共享,那么会导致用户的身份信息被盗取。 同源策略主要是指三点相同,协议+域名+端口 相同的两个请求,
则可以被看做是同源的,但如果其中任意一点存在不同,则代表是两个不同源的请求,同源策略会限制了不同源之间的资源交互。
实现效果:
具体配置:
1 | location / { |
防盗链
介绍:盗链是指外部网站引入当前网站的资源对外展示,防盗链是为避免自己网站资源被其它网站盗用。Nginx的防盗链机制实现,
跟http请求的一个头部字段:Referer有关,该字段主要描述了当前请求是从哪儿发出的,那么在Nginx中就可获取该值,
然后判断是否为本站的资源引用请求,如果不是则不允许访问。Nginx中存在一个配置项为valid_referers,正好可以满足前面的需求。
防盗链机制也无法解决爬虫伪造referers信息的这种方式抓取数据。
valid_referers none | blocked | server_names | string …;
- none:表示接受没有Referer字段的HTTP请求访问。
- blocked:表示允许 http:// 或 https// 以外的请求访问。
- server_names:资源的白名单,这里可以指定允许访问的域名。
- string:可自定义字符串,支配通配符、正则表达式写法。
实现效果:其他网站引入本网站资源无效。
具体配置:
1 | # 在动静分离的location中开启防盗链机制 |
大文件传输
某些业务场景中需要传输一些大文件,但大文件传输时往往都会会出现一些Bug,比如文件超出限制、文件传输过程中请求超时等,
那么此时就可以在Nginx稍微做一些配置。
1 | client_max_body_size 设置请求体允许的最大体积 |
Nginx性能优化
调整Worker工作进程
Nginx启动后默认只会开启一个Worker工作进程处理客户端请求,而我们可以根据机器的CPU核数开启对应数量的工作进程,以此来提升整体的并发量支持, 也应调整一下每个工作进程能够打开的文件句柄数。
1 | # 自动根据CPU核心数调整Worker进程数量 |
开启CPU亲和机制
对于并发编程较为熟悉的伙伴都知道,因为进程/线程数往往都会远超出系统CPU的核心数,因为操作系统执行的原理本质上是采用时间片切换机制,也就是一个CPU核心会在多个进程之间不断频繁切换,造成很大的性能损耗。而CPU亲和机制则是指将每个Nginx的工作进程,绑定在固定的CPU核心上,从而减小CPU切换带来的时间开销和资源损耗。
1 | worker_cpu_affinity auto; |
开启epoll模型及调整并发连接数
Nginx都是基于多路复用模型去实现的程序,但最初版的多路复用模型select/poll最大只能监听1024个连接,而epoll则属于select/poll接口的增强版,因此采用该模型能够大程度上提升单个Worker的性能。
1 | events { |
配置长连接
通常Nginx作为代理服务,负责分发客户端的请求,那么建议开启HTTP长连接,用户减少握手的次数,降低服务器损耗。
1 | upstream xxx { |
反向代理缓冲区
介绍:Nginx作为一个常用的反向代理,提供了代理缓冲的功能。它允许nginx将server端的响应读取下来缓存在本地内存或磁盘中,
再已合适的速度发送给客户端。在互联网中,nginx作为反向代理层,往往和server是在同区机房的。
这意外这nginx和server之间的网络质量很好,nginx可以用很快的速度从server端读取响应。
但是客户端往往是用户网络,到nginx的机房网络质量不可控,肯定是要远远低于nginx到后端server的网速。
这就会带来一个问题:因为客户端到nginx的网速过慢,导致nginx只能以一个较慢的速度将响应传给客户端,
进而导致后端server也只能以同样较慢的速度传递响应给nginx,造成一次请求连接耗时过长。
在高并发的情况下,后端server可能会出现大量的连接积压,最终拖垮server端。
优点:开启代理缓冲后,nginx可以用较快的速度尽可能将响应体读取并缓冲到本地内存或磁盘中,
然后同时根据客户端的网络质量以合适的网速将响应传递给客户端。
这样既解决了server端连接过多的问题,也保证了能持续稳定的像客户端传递响应。
缺点:开启代理缓冲会消耗nginx服务器的内存,如果请求过多,可能会导致nginx内存消耗过大。
所以缓冲区的大小设置需要根据实际的服务器配置和请求量进行评估。在响应过大的情况下,设置的缓冲区无法存下整个响应体,
nginx会将剩余的内容写到磁盘临时文件中。在请求量较大的情况下,可能会导致nginx服务器磁盘io过高。
而往往临时文件都存放在/tmp下,而/tmp目录一般挂载在系统盘上,系统盘io过高会进一步导致系统负载上涨。
在客户端网络质量很好的情况下,比如客户端到nginx也是同机房内,这时关闭代理缓冲直接将响应实时转发给客户端效率更高。
实现效果:客户端和Nginx之间连接网速过慢情况下,降低后端server连接。
具体配置:
1 | http{ |
缓存机制
介绍:proxy_cache主要用于反向代理时,对后端内容源服务器进行缓存,可能是任何内容,包括静态的和动态。
proxy_cache缓存减少了nginx与后端通信的次数,节省了传输时间和后端宽带。
实现效果:降低Nginx访问后端server次数
具体配置:
1 | http{ |
配置零拷贝
零拷贝读取机制与传统资源读取机制的区别,从两个过程对比,很轻易就能看出两者之间的性能区别。
- 传统方式:硬件–>内核–>用户空间–>程序空间–>程序内核空间–>网络套接字
- 零拷贝方式:硬件–>内核–>程序内核空间–>网络套接字
1 | sendfile on; # 开启零拷贝机制 |
无延迟或多包并发机制
TCP/IP协议中默认是采用了Nagle算法的,即在网络数据传输过程中,每个数据报文并不会立马发送出去,
而是会等待一段时间,将后面的几个数据包一起组合成一个数据报文发送,但这个算法虽然提高了网络吞吐量,但是实时性却降低了。
因此你的项目属于交互性很强的应用,那么可以手动开启tcp_nodelay配置,让应用程序向内核递交的每个数据包都会立即发送出去。
但这样会产生大量的TCP报文头,增加很大的网络开销。
相反,有些项目的业务对数据的实时性要求并不高,追求的则是更高的吞吐,那么则可以开启tcp_nopush配置项,
这个配置就类似于“塞子”的意思,首先将连接塞住,使得数据先不发出去,等到拔去塞子后再发出去。设置该选项后,
内核会尽量把小数据包拼接成一个大的数据包(一个MTU)再发送出去。当然若一定时间后(一般为200ms),内核仍然没有积累到一个MTU的量时,也必须发送现有的数据,否则会一直阻塞。
1 | #设置数据包会累积一下再一起传输,可以提高一些传输效率。 tcp_nopush 必须和 sendfile 搭配使用。 |
问题解决
惊群现象
主进程(master 进程)首先通过 socket() 来创建一个 sock 文件描述符用来监听,然后fork生成子进程(workers 进程),
子进程将继承父进程的 sockfd(socket 文件描述符),之后子进程 accept() 后将创建已连接描述符(connected descriptor)),
然后通过已连接描述符来与客户端通信。
那么,由于所有子进程都继承了父进程的 sockfd,那么当连接进来时,所有子进程都将收到通知并“争着”与它建立连接,这就叫“惊群现象”。大量的进程被激活又挂起,只有一个进程可以accept() 到这个连接,这当然会消耗系统资源。
Nginx对惊群现象的处理
Nginx 提供了一个 accept_mutex 这个东西,这是一个加在accept上的一把共享锁。
即每个 worker 进程在执行 accept 之前都需要先获取锁,获取不到就放弃执行 accept()。
有了这把锁之后,同一时刻,就只会有一个进程去 accpet(),这样就不会有惊群问题了。
accept_mutex 是一个可控选项,我们可以显示地关掉,默认是打开的。
Nginx 配置 https 出现no “ssl_certificate” is defined问题原因及解决方法
ssl_certificate必须在http段中先定义, 在server段才配置ssl_certificate已经来不及了。
在nginx的配置文件nginx.conf中加入如下代码。
1 | http { |