一.什么是Nginx?
Nginx是一个轻量级Web服务器,不仅是一个高性能HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP代理服务器。Nginx以事件驱动的方式编写,所以有很好的性能,同时也是一个非常高效的反向代理,负载均衡的服务器。在性能上,Nginx占用很少的系统资源,能支持更多的并发连接,达到更高的访问效率。在安装上,Nginx安装简单,配置灵活。Nginx支持热部署,启动速度非常快,还可以在不间断服务的情况下对软件版本或者配置进行升级,即使运行数月也无需启动。
在微服务的体系下,Nginx正在被越来越多的项目作为网关,配合Luau做限流熔断等处置。
对于大多数使用者来说,Nginx只是一个静态文件服务器或者http请求转发器,它可以把静态文件的请求直接返回静态文件资源,把动态文件的请求转发给后台的处理程序,例如php-fpm、apache、tomcat、jetty等,这些后台服务,即使没有nginx的情况下也是可以直接访问的(有些时候这些服务器是放在防火墙的面,不是直接对外暴露,通过nginx做了转换)。
二.Nginx的配置文件
nginx安装目录下,其配置文件都放在安装目录的conf目录下,主配置文件nginx.conf也在其中,后续对Nginx的使用也是对这个文件进行修改。将这个配置文件分为三部分:
第一部分:全局块
从配置文件开始到events块的内容,主要会设置一些影响Nginx服务器整体运行的配置指令,主要包括配置运行Nginx服务器的用户(组),允许生成的work process数,进程PID存放路径,日志存放路径和类型以及配置文件的引入。
#启动进程,通常设置成和cpu的数量相等,这个值越大,可以支持并发数量越多
worker_processes 1;
#全局错误日志及PID文件
#error_log logs/error.log;
#error_log logs/error.log notice;
#error_log logs/error.log info;
#pid logs/nginx.pid;
这个部分主要影响Nginx服务器对用户的网络连接,常用的设置包括是否开启对多work process下的网络连接进行序列化,是否允许同时接受多个网络连接,选取那个事件驱动模型来连接请求,每个work process可以同时支持最大连接数。
#工作模式及连接数上限
events {
#epoll是多路复用IO(I/O Multiplexing)中的一种方式,
#仅用于linux2.6以上内核,可以大大提高nginx的性能
use epoll;
#单个后台worker process进程的最大并发链接数
worker_connections 1024;
# 并发总数是 worker_processes 和 worker_connections 的乘积
# 即 max_clients = worker_processes * worker_connections
# 在设置了反向代理的情况下,max_clients = worker_processes * worker_connections / 4 为什么
# 为什么上面反向代理要除以4,应该说是一个经验值
# 根据以上条件,正常情况下的Nginx Server可以应付的最大连接数为:4 * 8000 = 32000
# worker_connections 值的设置跟物理内存大小有关
# 因为并发受IO约束,max_clients的值须小于系统可以打开的最大文件数
# 而系统可以打开的最大文件数和内存大小成正比,一般1GB内存的机器上可以打开的文件数大约是10万左右
# 我们来看看360M内存的VPS可以打开的文件句柄数是多少:
# $ cat /proc/sys/fs/file-max
# 输出 34336
# 32000 < 34336,即并发连接总数小于系统可以打开的文件句柄总数,这样就在操作系统可以承受的范围之内
# 所以,worker_connections 的值需根据 worker_processes 进程数目和系统可以打开的最大文件总数进行适当地进行设置
# 使得并发总数小于操作系统可以打开的最大文件数目
# 其实质也就是根据主机的物理CPU和内存进行配置
# 当然,理论上的并发总数可能会和实际有所偏差,因为主机还有其他的工作进程需要消耗系统资源。
# ulimit -SHn 65535
}
第三部分:http块
这算是 Nginx 服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里。 这部分还分为http全局块和server块。
http全局块:http 全局块配置的指令包括文件引入、MIME-TYPE 定义、日志自定义、连接超时时间、单链接请求数上限等。
http {
#设定mime类型,类型由mime.type文件定义
include mime.types;
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 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,
#对于普通应用,必须设为 on,
#如果用来进行下载等应用磁盘IO重负载应用,可设置为 off,
#以平衡磁盘与网络I/O处理速度,降低系统的uptime.
sendfile on;
#tcp_nopush on;
#连接超时时间
#keepalive_timeout 0;
keepalive_timeout 65;
tcp_nodelay on;
#开启gzip压缩
gzip on;
gzip_disable "MSIE [1-6].";
#设定请求缓冲
client_header_buffer_size 128k;
large_client_header_buffers 4 128k;
server块:这块和虚拟主机有密切关系,虚拟主机从用户角度看,和一台独立的硬件主机是完全一样的,该技术的产生是为了 节省互联网服务器硬件成本。 每个 http 块可以包括多个 server 块,而每个 server 块就相当于一个虚拟主机。 而每个 server 块也分为全局 server 块,以及可以同时包含多个 locaton 块。
全局server块:最常见的配置是本虚拟机主机的监听配置和本虚拟主机的名称或 IP 配置。
server {
#侦听80端口
listen 80;
#定义使用 www.nginx.cn访问
server_name www.nginx.cn;
#定义服务器的默认网站根目录位置
root html;
#设定本虚拟主机的访问日志
access_log logs/nginx.access.log main;
location块:一个 server 块可以配置多个 location 块。 这块的主要作用是基于 Nginx 服务器接收到的请求字符串(例如 server_name/uri-string),对虚拟主机名称 (也可以是 IP 别名)之外的字符串(例如 前面的 /uri-string)进行匹配,对特定的请求进行处理。地址定向、数据缓 存和应答控制等功能,还有许多第三方模块的配置也在这里进行。
#默认请求
location / {
#定义首页索引文件的名称
index index.php index.html index.htm;
}
# 定义错误提示页面
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
#静态文件,nginx自己处理
location ~ ^/(images|javascript|js|css|flash|media|static)/ {
#过期30天,静态文件不怎么更新,过期可以设大一点,
#如果频繁更新,则可以设置得小一点。
expires 30d;
}
#PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置.
location ~ .php$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
#禁止访问 .htxxx 文件
location ~ /.ht {
deny all;
}
}
}
这里的location命令的详细配置:location指令的使用
三.代理
3.1 什么是正向代理和反向代理?
正向代理类似于一个跳板机,代理访问外部资源。
正向代理的用途:
(1)访问原来无法访问的资源
(2)可以做缓存,加速访问资源
(3)对客户端访问授权,上网进行认证
(4)代理可以记录用户访问记录,对外隐藏信息
反向代理实际运行方式是指以代理服务器类接受internet上的连接请求,然后将请求转发到内部网络上的服务器,并将从服务器上得到的结果返回给internet上请求连接的客户端,此时代理服务器对外变现就是一个服务器。
(1)保证内网安全,防止web攻击,大型网站通常将反向代理作为公网访问地址,Web服务器是内网
(2)负载均衡,通过反向代理服务器来优化网站的负载
(3)提高性能,通过异步非阻塞的方式把请求传递给后端,提高并发能力
(4)利用缓存,压缩响应提高响应速度
正向代理即是客户端代理,代理客户端,服务端不知道实际发起请求的客户端
反向代理即是服务端代理,代理服务端,客户端不知道实际提供服务的服务端
3.2 Nginx实现正向代理
1.增加dns解析resolver
2.增加无server_name的server
3.proxy_pass指令
4.重启nginx
http {
resolver 8.8.8.8;
server {
listen 8088;
location / {
proxy_pass http://$http_host$request_uri;
}
}
3.3 Nginx实现反向代理
Nginx反向代理不需要编译额外的模块,默认自带proxy_pass和fastcgi_pass指令,通过在location配置块中增加指令就可以实现反向代理功能。
server {
listen 80;
server_name www.nginx.cn nginx.cn;
location /app {
proxy_pass http://127.0.0.1:8080;
}
}
四.负载均衡
Nginx的负载均衡使用了upstream,这个的使用:upstream配置和使用
这个负载均衡策略:负载均衡策略的使用
五.动静分离
Nginx动静分离简单来说就是把动态和静态请求分开,不能理解成只是单纯的把动态页面和静态页面物理分离。严格意义上说应该是动态请求和静态请求分开,可以理解来说Nginx处理静态页面,Tomcat处理动态页面。动静分离从目前角度来说分为两种:
(1)把静态文件独立成单独的域名,放在独立的服务器上,也是目前比较推崇的方案
(2)就是把静态和动态文件混合在一起,然后使用Nginx来分开
通过Location指定不同的后缀名实现不同请求转发。通过expires参数设置,可以使浏览器缓存过期时间,减少与服务器之间的请求和流量。expires:是给一个资源设定一个过期时间,也就是说无需去服务端验证,直接通过浏览器自身确定是否过期即可,所以也不会产生额外的流量。此种方法非常适合不经常变动的资源。
六.Nginx原理
整体架构:
6.1 多进程模型
6.1.1 master和worker
我们可以看到这个里面使用的是多进程模型:
Nginx里有一个master进程和多个worker进程。master进程并不处理网络请求,主要负载调度工作进程:加载配置,启动工作进程及非停升级。worker进程主要负责处理网络请求和响应。
master进程主要用来管理worker进程,具体包括4个主要功能:
(1)接受来自外界的信号
(2)向各worker进程发送信号
(3)监控worker进程的运行状态
(4)当worker进程退出后(异常情况下),会自动重新启动新的worker进程。
worker进程主要用来处理基本的网络事件:
(1)多个worker进程之间是对等且相互独立的,他们同等竞争来自客户端的请求
(2)一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其他进程的请求
(4)work进程的个数是可以设置的,一般我们会设置与机器cpu核数一致。同时,nginx为了更好的利用多核特性,具有cpu绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效。
master和worker进程的好处:首先,对于每个worker进程来说,独立的进程,不需要加锁,所以省点了锁的开销。同时在编程以及问题查找时,也会方便很多。其次,采用独立的进程,可以让互相之间不影响,一个进程退出之后,其他进程还在工作,服务不会中断,master进程会很快启动worker进程。当然,worker进程异常退出,肯定是程序有bug,异常退出,会导致当前worker上所有请求失败,不过不会影响到所有请求,所以降低了风险。
还记得之前的nginx.conf的events块中的配置worker_connection:这个值表示每个worker进程所能建立连接到最大值,所以,一个Nginx的建立最大的连接数,应该是worker_connection*worker_processes。当然这里说的最大连接数,对于HTTP请求本地资源来说,能够支持的最大并发数量是worker_connection*worker_processes。如果是支持http1.1的浏览器每次访问占用两个连接,所以普通静态访问支持的最大并发数量worker_connection*worker_processes/2。而如果是http为反向代理来说,最大并发数量是worker_connection*worker_processes/4。因为作为反向代理服务器,每个并发会建立客户端的连接和后端的连接,会占用两个连接。
6.1.2 进程控制方式
对Nginx进程的控制主要是对master进程来做到的,主要有两种方式:
手动发送信号:master接受信号管理众多worker进程,那么可以通过kill向master发送信号,比如kill -HUP pid用以通知Nginx从容重启。那么从容重启就是不中断服务:master在接受信号的时候,会先重新加载配置,然后再启动新进程开始接受新请求,并向所有老进程发送信号告知不再接受新请求并在处理完未处理的请求后自动退出。
自动发送信号:可以通过带命令行参数启动新进程来发送信号给master进程。比如./nginx -s reload用以启动一个新的Nginx进程,而新进程在解析到reload参数后会向master进程发送信号(新进程会帮我们把手动发送信号中的动作自动完成)。当然也可以这样./nginx -s stop来停止Nginx。
6.2 网络事件模块
Nginx采用异步非阻塞方式来处理网络事件。
master进程先建好需要listen的socket后,然后再fork出多个worker进程,每个worker进程都可以accept这个socket。当一个client连接到来时,所有accept的work进程都会收到通知,但只有一个能accept成功,其他的accept都会失败。Nginx提供一把共享锁accept_mutex来保证同一时刻只有一个worker进程accept连接,从而解决惊群问题。当一个worker进程accept连接后,就可以读取请求,解析请求,处理请求,产生数据,返回给客户端,最后才断开连接,这样一个完成的请求就结束了。
对于一个基本的web服务器来说,事件通常有三种类型,网络事件,信号,定时器。网络事件可以通过异步非阻塞(I/O模型)可以很好解决掉。
那什么是惊群现象?惊群简单来说就是多个进程或者线程在等待同一个事件,当事件发生时,所有线程和进程都会被内核唤醒。唤醒后通常只有一个进程获得了该事件并进行处理,其他进程发现获取事件失败后又继续进入了等待状态,在一定程度上降低了系统性能。具体来说惊群通常发生在服务器的监听等待调用上,服务器创建监听socket,后fork多个进程,在每个进程中调用accept或者epoll_wait等待终端的连接。
Nginx的惊群现象:每个worker进程都是从master进程fork过来。在master进程里面,先建立好需要listen的socket之 后,然后再fork出多个worker进程,这样每个worker进程都可以去accept这个socket(当然不是同一个socket,只是每个进程 的这个socket会监控在同一个ip地址与端口,这个在网络协议里面是允许的)。一般来说,当一个连接进来后,所有在accept在这个socket上 面的进程,都会收到通知,而只有一个进程可以accept这个连接,其它的则accept失败。
Nginx如何处理惊群?不让多个进程在同一时间监听接受连接的socket,而是让每个进程轮流监听,这样当有连接过来的时候,就只有一个进程在监听那肯定就没有惊群的问题。具体做法是:利用一把进程间锁,每个进程中都尝试获得这把锁,如果获取成功将监听socket加入wait集合中,并设置超时等待连接到来,没有获得所的进程则将监听socket从wait集合去除。