Nginx有什么作用呢?
Ngnix作为一款高性能的HTTP服务器、反向代理服务器、电子邮件代理服务器,主要有三方面的应用:
- HTTP服务器
Nginx作为HTTP服务器可独立提供HTTP服务,也可做为静态资源服务器。
- 虚拟主机
Nginx可以实现一台服务器虚拟出多个站点
- 反向代理负载均衡
当访问量达到一定量级后单台服务器无法支撑用户的请求时,可使用多台服务器做集群。此时可使用Nginx做反向代理,并可以为多台服务器做负载均衡以分担负载,因而不会因为某个节点负载过高宕机或某个节点闲置的情况。
什么是虚拟主机呢?
虚拟主机使用的是特殊的软硬件技术,将单台物理机(服务器)切分为多台虚拟的主机,每台虚拟主机都可以具有独立的域名,并具有完整的Internet服务器功能,如WWW、FTP、Email等。同时,同一台物理服务器上的虚拟主机之间是完全独立的。从站点访问者的角度来看,每台虚拟主机和一个独立主机完全是一样的。
利用虚拟主机不必为每个要运行的站点提供一台单独的Nginx服务器,或是单独运行一组Nginx进程。虚拟主机提供了在同一台物理机、同一组Nginx进程上运行多个站点的功能。
Nginx的配置文件的组成结构是什么样的呢?
Nginx配置文件文件可以分为六部分
-
main
全局配置 -
events
Nginx工作模式 -
http
HTTP配置 -
server
Server模块作为HTTP的模块主要用来配置虚拟主机 -
upstream
上游服务器设置,主要为反向代理、负载均衡等相关设置。 -
location
URL匹配特定位置后的设置,每部分包含若干指令。
Nginx将配置文件拆分为若干部分,主要分为
-
nginx.conf
主配置文件 -
vhost.conf
虚拟主机配置文件 -
fastcgi_params
与PHP相关的配置 -
uwsgi_params
与Python相关的配置
...
Nginx可以配置哪几种类型的虚拟主机呢?
- 基于域名的虚拟主机
使用不同的域名对应相同的IP以区分站点
- 基于IP的虚拟主机
使用不同的域名不同的IP区分站点
- 基于端口的虚拟主机
不使用域名、IP来区分不同站点,而是使用不同的TCP端口区分
Nginx虚拟主机的配置文件是什么样的呢?
server {
listen 80;
server_name yxkwx.cn yxkwx.cn;
root "D:/phpStudy/PHPTutorial/WWW/myproj/public";
location / {
index index.html index.htm index.php;
#autoindex on;
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php/$1 last;
break;
}
}
location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
}
配置解读
用户对PHP动态网页访问过程
当Nginx只是作为一个Web服务器时,本身是并没有Apache那么强大的功能的,不过通过支持各种模块让Nginx实现对多种功能的支持。另外,Nginx本身只能处理静态页面的解析,如果要解析动态页面的话,就需要使用到反向代理。也就是说,Nginx会将PHP等动态页面请求转发给后端其它Web服务器进行处理,如Tomat、IIS、Apache等。如果访问量过大的话就是要使用到Nginx的负载均衡架构。
对于PHP页面而言,通过Nginx转发给后端会有两种情况:反向代理、FastCGI,需要注意的是这里反向代理的后端指的是Web服务器,而FastCGI是实现PHP的解析程序。当Nginx将PHP页面请求转发给FastCGI程序时,也就时通过FastCGI方式对PHP进行了支持,PHP页面会交由FastCGI代理处理,这也是反向代理的一个应用。
当用户浏览器发起对yxkwx.cn/index.php
地址的访问时,用户和Nginx服务器会进行三次握手建立TCP链接,这里我们忽略掉Nginx访问控制策略和防火墙访问控制策略等,结合vhost.conf
虚拟主机配置文件来分析下Nginx与PHP、FastCGI、PHP-FPM的运行过程。
第1步
用户将HTTP请求发送给Nginx服务器
第2步
Nginx根据用户访问的URI和后缀对请求进行判断
用户访问yxkwx.cn/index.php
地址时,Nginx会根据配置文件中的location
进行匹配。由于访问的是index.php
所以会被匹配到location ~ \.php(.*)$
,正则所表示的含义是对用户通过URI
访问的资源进行区分大小写的匹配且访问的资源中包含.php
。
Nginx根据用户请求的资源匹配到具体的location
后,会执行location
对应的动作,location
中动作是
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
根据Nginx服务器的配置,可看出用户访问的是动态的PHP资源,Nginx会调用PHP脚本解析器对用户访问的资源进行解析。
具体每行指令的含义分析如下:
include fastcgi_params;
表示Nginx会调用FastCGI这个接口
fastcgi_intercept_errors on;
表示开启FastCGI的中断和错误信息记录
fastcgi_pass 127.0.0.1:9000;
表示Nginx通过fastcgi_pass
将用户请求的资源发送给127.0.0.1:9000
进行解析,这里的Nginx和PHP脚本解析服务器是在同一台机器上的,所以127.0.0.1:9000
表示的是本地的PHP脚本解析服务器。
Nginx通过location
指令将所有包含.php
的文件都交给127.0.0.1:9000
来处理,这里的IP地址和端口就是FastCGI进程监听的IP地址和端口,其整个工作流程如下:
FastCGI进程管理器PHP-FPM自身初始化,启动主进程PHP-FPM并启动
start_servers
这个CGI子进程。主进程PHP-FPM主要是管理FastCGI子进程并监听9000端口,FastCGI子进程等待来自Nginx的连接。当客户端请求到达Nginx时,Nginx通过
location
指令将所有包含.php
的文件交由127.0.0.1:9000
来处理。FastCGI进程管理器PHP-FPM选择并连接到一个子进程CGI解释器,Nginx将CGI环境变量和标准输入发送到FastCGI子进程。
FastCGI子进程完成处理后将标准输出和错误信息从同一连接返回给Nginx,当FastCGI子进程关闭连接时请求便告处理完成。
FastCGI子进程接着等待并处理来自FastCGI进程管理器的下一个连接
第3步
通过第2步可以看出用户请求的是动态内容,Nginx会将请求交给FastCGI客户端,通过fastcgi_pass
将用户的请求发送给PHP-FPM。如果用户访问的是静态资源,Nginx会直接将用户请求的静态资源返回给用户。
这里出现的FastCGI是什么呢?
了解FastCGI之前首先要明白CGI是什么,CGI全程Common Gateway Interface,意为通用网关接口,是用于HTTP服务上的程序服务通信交流的一种工具,是一个可伸缩地、高速地在HTTP服务器和动态脚本语言之间进行通信的接口,这个接口在Linux下也就是Socket,只是这个Socket可以是文件Socket也可以是IP Socket。
对比传统的CGI接口方式,由于传统的CGI接口方式性能与安全性较差,由于每次HTTP服务器遇到动态程序需要重启解析器来执行解析,然后结果被返回给HTTP服务器。这在处理高并发时几乎是不可能的,因此诞生了FastCGI。
PHP-FPM又是什么呢?
PHP-FPM是管理FastCGI的一个管理器,它作为PHP的插件存在。PHP-FPM提供了更好的PHP进程管理方式,可以有效地控制内存和进程,可以平滑重载PHP配置。
第4步
fastcgi_pass
将动态资源交给PHP-FPM后,PHP-FPM会将资源转给PHP脚本解析服务器的wrapper
Nginx不支持对外部程序的直接调用或解析,所有的外部程序如PHP必须通过FastCGI接口来调用。为了调用CGI程序,还需要一个FastCGI的wrapper
,wrapper
可以理解为用于启动另一个程序的程序,这个wrapper
绑定在某个固定socket
上,如端口或文件socket
。当Nginx将CGI请求发送给这个socket
时,通过FastCGI接口wrapper
接收到请求,然后Fork
派生出一个新的线程,这个线程调用解释器或外部程序处理脚本并读取返回数据。接着,wrapper
再将返回的数据通过FastCGI接口,沿着固定的socket
传递给Nginx。最后,Nginx将返回的数据(如HTML页面或图片等)发送给客户端。
简单来说,首先需要一个wrapper
完成以下工作:
- 调用FastCGI库函数使用
socket
与Nginx通信
其读写socket
是FastCGI内部实现的功能,对wrapper
是非透明的。 - 调度线程进行Fork和Kill
- 和应用(PHP)进行通信
第5步
wrapper
收到PHP-FPM转交过来的请求后,wrapper
会生成一个新的线程调用PHP动态程序解析服务器。
如果用户请求的是需要读取,如MySQL数据库等将会触发读库操作。
如果用户请求的是图片或附件等资源,PHP会触发依次查询后端存储服务器,例如通过NFS进行存储的存储集群。
第6步
PHP会将查询到的结果返回给Nginx
第7步
Nginx构造一个响应报文将结果返回给用户
综上所述
这只是Nginx中其中一种实现,用户请求的和返回给用户的结果都是异步进行的,也就是说用户请求的资源在Nginx中做了一次中转。Nginx也支持同步,即解析出来资源后服务器直接返回给用户,不用再中转。
是不是每次用户对动态资源的请求都需要触发一次完整的动态资源解析过程呢?
不是的,其实有两种方法可以解决这个问题:
第1种:启用Nginx本身所具有的缓存功能,将动态资源解析结果缓存起来,下次用户进行对应资源访问时,Nginx进行本次缓存查询,如果查询命中则直接将动态资源解析后的静态资源返回给用户。
第2种:在Nginx后端部署缓存机器,例如部署Varnish缓存集群,对资源进行缓存,用户请求的资源可以先在缓存集群上进行查找。
使用Nginx做缓存是否可行呢?
这个要看实际情况,如果在整个Web架构中Nginx不是瓶颈的前提下,Nginx是可以用来做缓存的,但并不建议这样来做,因为Nginx是用户请求和应答的必经之路。如果Nginx出现了瓶颈则后端的其它如存储集群等性能再好也没用。所以,在实际部署中并不建议启用Nginx的缓存功能(在将Nginx作为HTTP服务器的情况下)。因为启用了Nginx缓存功能后会降低Nginx的性能,另外会消耗部署Nginx对应服务器的硬件资源。
配置语法
正则表达式匹配
-
=
表示请求字符串与URI严格匹配,一旦匹配成功则停止,用于标准URI前。 -
~
表示匹配时区分大小写,用于正则URI前。 -
~*
表示匹配时不区分大小写,用于正则URI前。 -
!~
表示区分大小写则不匹配 -
!~*
表示不区分大小写则不匹配 -
^~
表示匹配以什么开头的
用于标准URI前,要求Nginx找到标识URI和请求字符串匹配度最高的location
后立即使用此location
处理请求,而不再使用location
块中的正则URI和请求字符串做匹配。 -
$
表示匹配以什么结尾的 -
\
转义字符,用于字符的转义。 -
*
表示任意字符
文件与目录的匹配
-
-f
表示存在某文件 -
!-f
表示不存在某文件 -
-d
表示存在某目录 -
!-d
表示不存在某目录 -
-e
表示存在文件或目录 -
!-e
表示不存在文件或目录 -
-x
表示文件可执行 -
!-x
表示文件无法执行
例如:
-
location / {}
表示匹配任何查询,因为所有请求都是以/
开头,但正则规则和长的块规则将被优先和查询匹配。 -
location ^~ /images/ {}
表示匹配任意以/images/
开头的查询请求并停止搜索 -
location ~* \.(gif|jpg|jpeg)$ {}
表示匹配任何以.gif
、.jpg
、.jpeg
结尾的请求
配置指令
server
server {}
### listen
Nginx的虚拟主机通过server
节点指定,若要设置多个虚拟主机则可配置多个server
节点即可。
listen 80;
listen
用于指定虚拟主机监听的TCP端口号,也就是虚拟主机的服务端口,默认80为WWW
服务对应的端口号。
server_name
server_name yxkwx.cn yxkwx.cn;
server_name
用于指定虚拟主机名,当用户使用链接访问虚拟主机名时就会由所对应的虚拟主机进行处理。
虚拟主机名可以有四种格式
- 准确的名字,例如
yxkwx.cn
- 以
*
开头的名字,例如*.yxkwx.cn
- 以
*
结尾的名字,例如yxkwx.*
- 正则表达式形式,例如
~^www\d+\.yxkwx\.com$
,注意必须以~
开头。
虚拟主机名可以同时设置多个,设置多个时以空格隔开。
例如: server_name yxkwx.cn www.yxkwx.cn *.yxkwx.cn
当设置多个虚拟主机名时,其优先级先后顺序为
- 准确的名字
- 最长的以
*
开头的通配符的名字 - 最长的以
*
结尾的同配置的名字 - 第一个匹配的正则表达式名字
root
root "D:/phpStudy/PHPTutorial/WWW/myproj/public";
root
指定当前虚拟主机实际物理根目录的绝对路径
location
location
模块是Nginx中最常使用也是最重要的模块,用来定位并解析URL
。
location
提供了强大的正则匹配功能,也支持条件判断匹配。
用户可使用location
指令实现Nginx对动态与静态网页进行过滤处理。
location
指令的格式为location [modifier] uri {...}
,当一个请求进入时URI
会被检测并匹配一个最佳的location
,首先会进行精确匹配若匹配失败则会进行字符串的前缀匹配,依次进行正则匹配、匹配常规字符串模糊匹配,若以上匹配都不成功,将会执行默认匹配。
location = /
表示精确匹配
location /documents/
表示匹配常规字符串,模糊匹配。如果有正则检查则正则优先。
location /
由于所有的请求都是以/
开头,所以location /
表示匹配所有请求,另外也表示匹配访问站点根目录。当所有location
都不能匹配后的默认匹配原则。
location ~
location
指令可以使用正则匹配,开启正则匹配是使用location ~
。
location ^~ /images/
表示先进行字符串的前缀匹配,如果匹配到了就不做正则匹配检查。
location ~* .(gif|jpg|jpeg|png)$
表示正则匹配,*
表示不区分大小写。
例如:
location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
\.php(.*)$
是使用正则匹配以.php
结尾的URL,进而用来解析PHP文件。
例如:Nginx设置禁止访问某类文件
location ~* \.(txt|doc)$
{
if (-f $request_filename)
{
root /usr/local/nginx/html/test;
break;
}
}
location ~* \.(txt|doc)$
{
root /usr/local/nginx/html/test;
deny all;
}
例如:Nginx设置静态文件处理
location ~* \.(css|js|jpg|jpeg|gif|png|swf)$
{
if (-f $request_filename)
{
root /usr/local/nginx/html;
expires 1d;
}
break;
}
index
index index.html index.htm index.php;
index
指定当前虚拟主机默认访问首页的文件名,index
中是有先后顺序的,如果没有开启目录浏览权限同时又找不找这些默认首页的话,就会出现403错误。
autoindex
autoindex on;
autoindex
表示是否开启网站根目录文件列表功能,开启后访问目录时会列出根目录中的文件列表,默认关闭off
。
autoindex_exact_size on;
autoindex_exact_size
表示是否显示文件列表中每个文件的确切大小,注意单位是字节bytes
,默认为开启状态on
。
autoindex_localtime off;
autoindex_localtime
表示是否显示文件列表中每个文件的服务器本地时间,时间格式为GMT格林威治时间,默认为关闭状态off
。
### allow
allow all;
允许所有访问
charset
charset
charset
用于配置网页的默认编码格式
access_log
access_log
access_log
用来指定当前虚拟主机的访问日志存放路径,最后的main
用于指定访问日志的输出格式。
rewrite
rewrite
指令主要用于实现URL
地址重写,Nginx的重写规则需要PCRE
的支持,即通过Perl
兼容正则表达式语法进行规则匹配。默认参数编译时Nginx就会安装支持rewrite
的模块,但也必须要有PCRE
软件的支持。
rewrite
指令语法:rewrite regex replacement [flag]
例如:URL重写跳转为rewrite ^/(.*) http://www.baidu.com/$1 permanent;
rewrite
为重写参数,正则部分为^/(.*) http://www.baidu.com/$1
,其中$1
表示获取正则()
括号部分的值。
flag
标记符号的取值范围
-
last
表示本条规则匹配完成后,继续向下匹配新的location URI
规则。 -
break
表示本条规则匹配完成即终止,不再匹配后面的任何规则。 -
redirect
返回302临时重定向,浏览器地址栏会显示跳转后的URL地址。 -
permanent
返回302永久重定向,浏览器地址栏会显示跳转后的URL地址。
错误处理
错误1:浏览器访问http://192.168.50.25:81/admin/login
出现404错误
Nginx多域名配置中支持PATHINFO
路由模式
server {
listen 81;
server_name yxkwx.cn yxkwx.cn;
root "D:/phpStudy/PHPTutorial/WWW/myproj/public";
location / {
index index.html index.htm index.php;
#autoindex on;
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php/$1 last;
break;
}
}
location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
}
错误2:浏览器访问http://192.168.50.25:82
,页面出现错误No input file specified.
。
查看Nginx错误日志文件error.log
文件发现错误信息
2019/05/15 11:03:18 [crit] 13928#9540:
*5 GetFileAttributesEx() "D:\phpStudy\PHPTutorial\WWW
myproj\public/api.html" failed
(123: The filename, directory name, or volume label syntax is incorrect),
client: 192.168.50.25,
server: nbb.cn,
request: "GET /api.html HTTP/1.1",
host: "192.168.50.25:82"
以上错误信息重点关注The filename, directory name, or volume label syntax is incorrect
说明文件路径有问题
查看多域名配置文件vhost.conf
中root
配置项目root "D:\phpStudy\PHPTutorial\WWW\myproj\public";
,将正斜杠\
修改为反斜杠/
重启Nginx服务器即可。
server {
listen 82;
server_name nbb.cn nbb.cn;
root "D:/phpStudy/PHPTutorial/WWW/myproj/public";
location / {
index index.html index.htm index.php;
#autoindex on;
if (!-e $request_filename) {
rewrite ^/(.*)$ /index.php/$1 last;
break;
}
}
location ~ \.php(.*)$ {
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_split_path_info ^((?U).+\.php)(/?.+)$;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_param PATH_TRANSLATED $document_root$fastcgi_path_info;
include fastcgi_params;
}
}
未完待续...