上一篇《WEB请求处理一:浏览器请求发起处理》,咱们讲述了浏览器端请求发起过程,经过DNS域名解析服务器IP,并创建TCP链接,发送HTTP请求。本文将讲述请求到达反向代理服务器的一个处理过程,好比:在Nginx中请求的反向代理处理流程,请求都是通过了哪些模块,作了哪些处理,又是如何找到应用服务器呢?javascript
为直观明了,先上一张图,红色部分为本章所述模块:php
本章所述模块css
正如标题所述,Nginx功能是进行请求的反向代理,在讲解Nginx请求处理以前,首先要给你们清楚地说明下反向代理是什么?它的功能是什么?它在Nginx中又是怎么配置实现的?
html
反向代理(Reverse Proxy)方式是指以代理服务器来接受internet上的链接请求,而后将请求转发给内部网络上的服务器,并将从服务器上获得的结果返回给internet上请求链接的客户端,此时代理服务器对外就表现为一个服务器。前端
举个例子,好比我想访问 http://www.test.com/readme ,但www.test.com上并不存在readme页面,因而他是偷偷从另一台服务器上取回来,而后做为本身的内容返回用户,但用户并不知情。这里所提到的 www.test.com 这个域名对应的服务器就设置了反向代理功能。java
结论就是,反向代理服务器对于客户端而言它就像是原始服务器,而且客户端不须要进行任何特别的设置
。客户端向反向代理的命名空间(name-space)中的内容发送普通请求,接着反向代理服务器将判断向何处(原始服务器)转交请求,并将得到的内容返回给客户端,就像这些内容本来就是它本身的同样。node
正向代理,既然有反向代理,就确定有正向代理。什么叫正向代理呢?linux
正向代理(Forward Proxy)一般都被简称为代理,就是在用户没法正常访问外部资源,比方说受到GFW的影响没法访问twitter的时候,咱们能够经过代理的方式,让用户绕过防火墙,从而链接到目标网络或者服务。nginx
正向代理的工做原理就像一个跳板,好比:我访问不了google.com,可是我能访问一个代理服务器A,A能访问google.com,因而我先连上代理服务器A,告诉他我须要google.com的内容,A就去取回来,而后返回给我。从网站的角度,只在代理服务器来取内容的时候有一次记录,有时候并不知道是用户的请求,也隐藏了用户的资料,这取决于代理告不告诉网站。git
结论就是,正向代理是一个位于客户端和原始服务器(origin server)之间的服务器
。为了从原始服务器取得内容,客户端向代理发送一个请求并指定目标(原始服务器),而后代理向原始服务器转交请求并将得到的内容返回给客户端。
反向代理VS正向代理:
反向代理VS正向代理
用户经过域名发出访问Web服务器的请求,该域名被DNS服务器解析为反向代理服务器的IP地址;
反向代理服务器接受用户的请求;
反向代理服务器在本地缓存中查找请求的内容,找到后直接把内容发送给用户;
若是本地缓存里没有用户所请求的信息内容,反向代理服务器会代替用户向源服务器请求一样的信息内容,并把信息内容发给用户,若是信息内容是缓存的还会把它保存到缓存中。
一般的代理服务器,只用于代理内部网络对Internet外部网络的链接请求,客户机必须指定代理服务器,并将原本要直接发送到Web服务器上的http请求发送到代理服务器中
。不支持外部网络对内部网络的链接请求,由于内部网络对外部网络是不可见的。当一个代理服务器可以代理外部网络上的主机,访问内部网络时,这种代理服务的方式称为反向代理服务
。此时代理服务器对外就表现为一个Web服务器,外部网络就能够简单把它看成一个标准的Web服务器而不须要特定的配置。不一样之处在于,这个服务器没有保存任何网页的真实数据,全部的静态网页或者CGI程序,都保存在内部的Web服务器上
。所以对反向代理服务器的攻击并不会使得网页信息遭到破坏,这样就加强了Web服务器的安全性。
代理服务器充当内容服务器的替身,若是您的内容服务器具备必须保持安全的敏感信息,如信用卡号数据库,可在防火墙外部设置一个代理服务器做为内容服务器的替身。当外部客户机尝试访问内容服务器时,会将其送到代理服务器。实际内容位于内容服务器上,在防火墙内部受到安全保护。代理服务器位于防火墙外部,在客户机看来就像是内容服务器
。
当客户机向站点提出请求时,请求将转到代理服务器。而后,代理服务器经过防火墙中的特定通路,将客户机的请求发送到内容服务器
。内容服务器再经过该通道将结果回传给代理服务器。代理服务器将检索到的信息发送给客户机,好像代理服务器就是实际的内容服务器。若是内容服务器返回错误消息,代理服务器会先行截取该消息并更改标头中列出的任何URL,而后再将消息发送给客户机
。如此可防止外部客户机获取内部内容服务器的重定向URL。
这样,代理服务器就在安全数据库和可能的恶意攻击之间提供了又一道屏障。与有权访问整个数据库的状况相对比,就算是侥幸攻击成功,做恶者充其量也仅限于访问单个事务中所涉及的信息。未经受权的用户没法访问到真正的内容服务器,由于防火墙通路只容许代理服务器有权进行访问
。
能够配置防火墙路由器,使其只容许特定端口上的特定服务器有权经过防火墙进行访问,而不容许其余任何机器进出。安全反向代理,指当代理服务器与其余机器之间有一个或多个链接使用安全套接字层 (SSL) 协议加密数据时,即会进行安全反向代理
。
企业内全部的网站共享一个在internet中注册的IP地址,这些服务器分配私有地址,采用虚拟主机的方式对外提供服务。
反向代理就是一般所说的web服务器加速,它是一种经过在繁忙的web服务器和外部网络之间增长一个高速的web缓冲服务器来下降实际的web服务器的负载的一种技术。反向代理是针对web服务器提升加速功能,做为代理缓存,它并非针对浏览器用户,而针对一台或多台特定的web服务器,它能够代理外部网络对内部网络的访问请求
。
反向代理服务器会强制将外部网络对要代理的服务器的访问通过它,这样反向代理服务器负责接收客户端的请求,而后到源服务器上获取内容,把内容返回给用户,并把内容保存到本地,以便往后再收到一样的信息请求时,它会把本地缓存里的内容直接发给用户,以减小后端web服务器的压力,提升响应速度。所以Nginx还具备缓存功能。
(1)请求的统一控制,包括设置权限、过滤规则等;
(2)区分动态和静态可缓存内容;
(3)实现负载均衡,内部能够采用多台服务器来组成服务器集群,外部仍是能够采用一个地址访问;
(4)解决Ajax跨域问题;
(5)做为真实服务器的缓冲,解决瞬间负载量大的问题;
写到这时,一直在因为要不要去开这一节Nginx配置的讲解,若是讲的话,感受与本文的主题有所偏离,但又考虑到,若是对Nginx配置文件都不熟悉的话,下面的内容再去讲解Nginx反向代理处理流程就有点纸上谈兵了,担忧你们有些云里雾里,毫无收获。
终究旨在为了要让你们有所收获的初衷,决定仍是要着重讲解Nginx的几种常见配置,其中包括:动静分离、缓存设置、负载均衡、反向代理、还有虚拟主机功能。
Mac平台,我用brew安装的:
/usr/local/bin/nginx # 启动 /usr/local/bin/nginx -s reload #平滑重启 /usr/local/etc/nginx/nginx.cnf #配置文件。
其实,对比,apache的配置文件,它的相对比较清晰和简单,以前以为很难,如今沉下心来想一想,其实很简单。大体的分块下,基本就分为如下几块:
main # 全局设置 events { # Nginx工做模式 .... } http { # http设置 .... upstream myproject { # 负载均衡服务器设置 ..... } server { # 主机设置 .... location { # URL匹配 .... } } server { .... location { .... } } .... }
下面是一个main区域,它是一个全局的设置:
user nobody nobody; worker_processes 2; error_log /usr/local/var/log/nginx/error.log notice; pid /usr/local/var/run/nginx/nginx.pid; worker_rlimit_nofile 1024;
user 来
指定Nginx Worker进程运行用户以及用户组
,默认由nobody帐号运行。worker_processes 来
指定了Nginx要开启的子进程数。每一个Nginx进程平均耗费10M~12M内存。
根据经验,通常指定1个进程就足够了,若是是多核CPU,建议指定和CPU的数量同样的进程数便可
。我这里写2,那么就会开启2个子进程,总共3个进程。error_log
来定义全局错误日志文件
。日志输出级别有debug、info、notice、warn、error、crit可供选择,其中,debug输出日志最为最详细,而crit输出日志最少。pid 来
指定进程id的存储文件位置
。worker_rlimit_nofile 来
指定一个nginx进程能够打开的最多文件描述符数目
,这里是65535,须要使用命令“ulimit -n 65535”来设置。
events模块来用指定nginx的工做模式和工做模式及链接数上限,通常是这样:
events { use kqueue; #mac平台 worker_connections 1024; }
use
用来指定Nginx的工做模式
。Nginx支持的工做模式有select、poll、kqueue、epoll、rtsig和/dev/poll。其中select和poll都是标准的工做模式,kqueue和epoll是高效的工做模式
,不一样的是epoll用在Linux平台上,而kqueue用在BSD系统中,由于Mac基于BSD,因此Mac也得用这个模式,对于Linux系统,epoll工做模式是首选。worker_connections
用于定义Nginx每一个进程的最大链接数,即接收前端的最大请求数,默认是1024。
最大客户端链接数由worker_processes和worker_connections决定,即Max_clients = worker_processes * worker_connections,在做为反向代理时,Max_clients变为:Max_clients = worker_processes * worker_connections / 4。
进程的最大链接数受Linux系统进程的最大打开文件数限制
,在执行操做系统命令“ulimit -n 65536”后worker_connections的设置才能生效。
http模块能够说是最核心的模块了,它负责HTTP服务器相关属性的配置,它里面的server和upstream子模块,相当重要,等到反向代理和负载均衡以及虚拟目录等会仔细说。
http { 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 /usr/local/var/log/nginx/access.log main; sendfile on; tcp_nopush on; tcp_nodelay on; keepalive_timeout 10; #gzip on; upstream myproject { ..... } server { .... } }
用来设定文件的mime类型,类型在配置文件目录下的mime.type文件定义,来告诉nginx来识别文件类型。
设定了默认的类型为二进制流,也就是当文件类型未定义时使用这种方式
,例如在没有配置asp的locate 环境时,Nginx是不予解析的,此时,用浏览器访问asp文件就会出现下载窗口了。
用于设置日志的格式,和记录哪些参数,
这里设置为main,恰好用于access_log来纪录这种类型
。
main的类型日志以下:也能够增删部分参数。
127.0.0.1 - - [21/Apr/2015:18:09:54 +0800] "GET /index.php HTTP/1.1" 200 87151 "-" "Mozilla/5.0 (Macintosh; Intel Mac OS X 10_10_2) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/41.0.2272.76 Safari/537.36"
用来纪录每次的访问日志的文件地址,后面的main是日志的格式样式,对应于log_format的main。
用于开启高效文件传输模式。将tcp_nopush和tcp_nodelay两个指令设置为on用于防止网络阻塞。
设置客户端链接保持活动的超时时间。在超过这个时间以后,服务器会关闭该链接。
server模块是http的子模块,它用来定一个虚拟主机,咱们先讲最基本的配置,这些在后面再讲。咱们看一下一个简单的server是如何作的?
server { listen 8080; server_name localhost 192.168.12.10 www.yangyi.com; # 全局定义,若是都是这一个目录,这样定义最简单。 root /Users/yangyi/www; index index.php index.html index.htm; charset utf-8; access_log usr/local/var/log/host.access.log main; error_log usr/local/var/log/host.error.log error; .... }
server 标志定义虚拟主机开始。
listen 用于指定虚拟主机的服务端口。
server_name 用来指定IP地址或者域名,多个域名之间用空格分开。
root 表示在这整个server虚拟主机内,所有的root web根目录。注意要和locate {}下面定义的区分开来。
index 全局定义访问的默认首页地址。注意要和locate {}下面定义的区分开来。
charset 用于设置网页的默认编码格式。
access_log 用来指定此虚拟主机的访问日志存放路径,最后的main用于指定访问日志的输出格式。
location模块是nginx中用的最多的,也是最重要的模块了,什么负载均衡啊、反向代理啊、虚拟域名啊都与它相关。
location根据它字面意思就知道是来定位的,定位URL,解析URL,因此,它也提供了强大的正则匹配功能,也支持条件判断匹配,用户能够经过location指令实现Nginx对动、静态网页进行过滤处理。像咱们的php环境搭建就是用到了它。
location / { root /Users/yangyi/www; index index.php index.html index.htm; }
location / 表示匹配访问根目录。
root 指令用于指定访问根目录时,虚拟主机的web目录,
这个目录能够是相对路径(相对路径是相对于nginx的安装目录)。也能够是绝对路径
。index
用于设定咱们只输入域名后访问的默认首页地址
,有个前后顺序:index.php index.html index.htm,若是没有开启目录浏览权限,又找不到这些默认首页,就会报403错误。
下面这个例子是运用正则匹配来连接php。咱们以前搭建环境也是这样作:
location ~ \.php$ { root /Users/yangyi/www; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; }
.php$ 熟悉正则的咱们直到,这是匹配.php结尾的URL,用来解析php文件。
里面的root也是同样,用来表示虚拟主机的根目录
。fastcgi_pass 连接的是php-fpm的地址。其余几个参数咱们之后再说。
location 还有其余用法,等讲到实例的时候,再看吧。
upstream 模块负责负载均衡模块,经过一个简单的调度算法来实现客户端IP到后端服务器的负载均衡。先学习怎么用,具体的使用实例之后再说。
upstream iyangyi.com{ ip_hash; server 192.168.12.1:80; server 192.168.12.2:80 down; server 192.168.12.3:8080 max_fails=3 fail_timeout=20s; server 192.168.12.4:8080; }
在上面的例子中,经过upstream指令指定了一个负载均衡器的名称iyangyi.com
。这个名称能够任意指定,在后面须要的地方直接调用便可。里面是ip_hash这是其中的一种负载均衡调度算法
,下面会着重介绍。紧接着就是各类服务器了。用server关键字表识,后面接ip
。
Nginx的负载均衡模块目前支持4种调度算法:
weight 轮询(默认)。每一个请求按时间顺序逐一分配到不一样的后端服务器,若是后端某台服务器宕机,故障系统被自动剔除,使用户访问不受影响。weight。指定轮询权值,weight值越大,分配到的访问机率越高,主要用于后端每一个服务器性能不均的状况下。
ip_hash。每一个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。
fair(第三方)。比上面两个更加智能的负载均衡算法。此种算法能够依据页面大小和加载时间长短智能地进行负载均衡,也就是根据后端服务器的响应时间来分配请求,响应时间短的优先分配。Nginx自己是不支持fair的,若是须要使用这种调度算法,必须下载Nginx的upstream_fair模块。
url_hash(第三方)。按访问url的hash结果来分配请求,使每一个url定向到同一个后端服务器,能够进一步提升后端缓存服务器的效率。Nginx自己是不支持url_hash的,若是须要使用这种调度算法,必须安装Nginx的hash软件包。
在HTTP Upstream模块中,能够经过server指令指定后端服务器的IP地址和端口,同时还能够设定每一个后端服务器在负载均衡调度中的状态。经常使用的状态有:
down,表示当前的server暂时不参与负载均衡。
backup,预留的备份机器。当其余全部的非backup机器出现故障或者忙的时候,才会请求backup机器,所以这台机器的压力最轻。
max_fails,容许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。
fail_timeout,在经历了max_fails次失败后,暂停服务的时间。max_fails能够和fail_timeout一块儿使用。
注意:当负载调度算法为ip_hash时,后端服务器在负载均衡调度中的状态不能是weight和backup。
假设咱们在本地开发有3个项目,分别在hosts里映射到本地的127.0.0.1上:
127.0.0.1 www.iyangyi.com iyangyi.com 127.0.0.1 api.iyangyi.com 127.0.0.1 admin.iyangyi.com
有这样3个项目,分别对应于web根目录下的3个文件夹,咱们用域名对应文件夹名字,这样子好记:
/Users/yangyi/www/www.iyangyi.com/ /Users/yangyi/www/api.iyangyi.com/ /Users/yangyi/www/admin.iyangyi.com/
每一个目录下都有一个index.php文件,都是简单的输入本身的域名。
下面咱们就来搭建这3个域名的虚拟主机,很显然,咱们要新建3个server来完成。建议将对虚拟主机进行配置的内容写进另一个文件,而后经过include指令包含进来,这样更便于维护和管理。不会使得这个nginx.conf内容太多:
main events { .... } http { .... include vhost/www.iyangyi.conf; include vhost/api.iyangyi.conf; include vhost/admin.iyangyi.conf; # 或者用 *.conf 包含 # include vhost/*.conf }
include:主模块指令,实现对配置文件所包含的文件的设定,能够减小主配置文件的复杂度。
既然每个conf都是一个server,前面已经学习了一个完整的server写的了。下面就开始:
# www.iyangyi.conf server { listen 80; server_name www.iyangyi.com iyangyi.com; root /Users/yangyi/www/www.iyangyi.com/; index index.php index.html index.htm; access_log /usr/local/var/log/nginx/www.iyangyi.access.log main; error_log /usr/local/var/log/nginx/www.iyangyi.error.log error; location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; } }
# api.iyangyi.conf server { listen 80; server_name api.iyangyi.com; root /Users/yangyi/www/api.iyangyi.com/; index index.php index.html index.htm; access_log /usr/local/var/log/nginx/api.iyangyi.access.log main; error_log /usr/local/var/log/nginx/api.iyangyi.error.log error; location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; } }
# admin.iyangyi.conf server { listen 80; server_name admin.iyangyi.com; root /Users/yangyi/www/admin.iyangyi.com/; index index.php index.html index.htm; access_log /usr/local/var/log/nginx/admin.iyangyi.access.log main; error_log /usr/local/var/log/nginx/admin.iyangyi.error.log error; location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; } }
这样3个很精简的虚拟域名就搭建好了。重启下nginx,而后打开浏览器访问一下这3个域名,就能看到对应的域名内容了。
Nginx 使用反向代理,主要是使用location模块下的proxy_pass选项。
来个最简单的。当我访问 mac 上的nginx 的 centos.iyangyi.com 的内容时候, 就反向代理到虚拟机centos上的 apache 192.168.33.10 的index.html页面。
192.168.33.10 中的html 是很简单的一句输出:
centos apache2 index.html
在hosts里新加上这个域名:
#vi /etc/hosts 127.0.0.1 centos.iyangyi.com
在vhost目录中新建一个conf server:
#centos.iyangyi.conf server { listen 80; server_name centos.iyangyi.com; access_log /usr/local/var/log/nginx/centos.iyangyi.access.log main; error_log /usr/local/var/log/nginx/centos.iyangyi.error.log error; location / { proxy_pass http://192.168.33.10; } }
重启下nginx:
sudo nginx -s reload
固然。proxy 还有其余的参数,好比:proxy_set_header 用来设置header头部信息参数转发等,等用了能够仔细看看。
别被这个名字给吓住了,觉得是什么很牛逼的东西的。其实否则。也很简单。
先简单说下负载均衡是干吗的?举个例子:咱们的小网站,刚开始就一台nginx服务器,后来,随着业务量增大,用户增多,一台服务器已经不够用了,咱们就又多加了几台服务器。那么这几台服务器如何调度?如何均匀的提供访问?这就是负载均衡。
负载均衡的好处是能够集群多台机器一块儿工做,而且对外的IP和域名是同样的,外界看起来就好像一台机器同样。
先来一个最简单的,weight权重的:
upstream webservers{ server 192.168.33.11 weight=10; server 192.168.33.12 weight=10; server 192.168.33.13 weight=10; } server { listen 80; server_name upstream.iyangyi.com; access_log /usr/local/var/log/nginx/upstream.iyangyi.access.log main; error_log /usr/local/var/log/nginx/upstream.iyangyi.error.log error; location / { proxy_pass http://webservers; proxy_set_header X-Real-IP $remote_addr; } }
咱们再来继续看几个参数 : max_fails和fail_timeout
**max_fails : **容许请求失败的次数,默认为1。当超过最大次数时,返回proxy_next_upstream 模块定义的错误。
**fail_timeout : **在经历了max_fails次失败后,暂停服务的时间。max_fails能够和fail_timeout一块儿使用,进行健康状态检查。
upstream webservers{ server 192.168.33.11 weight=10 max_fails=2 fail_timeout=30s; server 192.168.33.12 weight=10 max_fails=2 fail_timeout=30s; server 192.168.33.13 weight=10 max_fails=2 fail_timeout=30s; }
down: 表示这台机器暂时不参与负载均衡。至关于注释掉了。
backup: 表示这台机器是备用机器,是其余的机器不能用的时候,这台机器才会被使用,俗称备胎
upstream webservers{ server 192.168.33.11 down; server 192.168.33.12 weight=10 max_fails=2 fail_timeout=30s; server 192.168.33.13 backup; }
这种分配方式,每一个请求按访问IP的hash结果分配,这样来自同一个IP的访客固定访问一个后端服务器,有效解决了动态网页存在的session共享问题。
upstream webservers{ ip_hash; server 192.168.33.11 weight=1 max_fails=2 fail_timeout=30s; server 192.168.33.12 weight=1 max_fails=2 fail_timeout=30s; server 192.168.33.13 down; }
ip_hash 模式下,最好不要设置weight参数,由于你设置了,就至关于手动设置了,将会致使不少的流量分配不均匀。
ip_hash 模式下,backup参数不可用,加了会报错,为啥呢?由于,自己咱们的访问就是固定的了,其实,备用已经无论什么做用了。
页面缓存也是平常web 开发中很重要的一个环节,对于一些页面,咱们能够将其静态化,保存起来,下次请求时候,直接走缓存,而不用去请求反相代理服务器甚至数据库服务了。从而减轻服务器压力。
nginx 也提供了简单而强大的下重定向,反向代理的缓存功能,只须要简单配置下,就能将指定的一个页面缓存起来。它的原理也很简单,就是匹配当前访问的url, hash加密后,去指定的缓存目录找,看有没有,有的话就说明匹配到缓存了。
咱们先来看一下一个简单的页面缓存的配置:
http { proxy_cache_path /data/nginx/cache levels=1:2 keys_zone=cache_zone:10m inactive=1d max_size=100m; upstream myproject { ..... } server { .... location ~ *\.php$ { proxy_cache cache_zone; #keys_zone的名字 proxy_cache_key $host$uri$is_args$args; #缓存规则 proxy_cache_valid any 1d; proxy_pass http://127.0.0.1:8080; } } .... }
下面咱们来一步一步说。用到的配置参数,主要是proxy_*前缀的不少配置。
首先须要在http中加入proxy_cache_path 它用来制定缓存的目录以及缓存目录深度制定等。它的格式以下:
proxy_cache_path path [levels=number] keys_zone=zone_name:zone_size [inactive=time] [max_size=size];
path是用来指定 缓存在磁盘的路径地址。好比:/data/nginx/cache。那之后生存的缓存文件就会存在这个目录下。
levels用来指定缓存文件夹的级数,能够是:levels=1, levels=1:1, levels=1:2, levels=1:2:3 可使用任意的1位或2位数字做为目录结构分割符,如 X, X:X,或 X:X:X 例如: 2, 2:2, 1:1:2,可是最多只能是三级目录。
那这个里面的数字是什么意思呢。表示取hash值的个数。好比:
如今根据请求地址localhost/index.php?a=4 用md5进行哈希,获得e0bd86606797639426a92306b1b98ad9
levels=1:2 表示创建2级目录,把hash最后1位(9)拿出建一个目录,而后再把9前面的2位(ad)拿来建一个目录, 那么缓存文件的路径就是/data/nginx/cache/9/ad/e0bd86606797639426a92306b1b98ad9
以此类推:levels=1:1:2表示创建3级目录,把hash最后1位(9)拿出建一个目录,而后再把9前面的1位(d)建一个目录, 最后把d前面的2位(8a)拿出来建一个目录 那么缓存文件的路径就是/data/nginx/cache/9/d/8a/e0bd86606797639426a92306b1b98ad9
keys_zone 全部活动的key和元数据存储在共享的内存池中,这个区域用keys_zone参数指定。zone_name指的是共享池的名称,zone_size指的是共享池的大小。注意每个定义的内存池必须是不重复的路径,例如:
proxy_cache_path /data/nginx/cache/one levels=1 keys_zone=one:10m; proxy_cache_path /data/nginx/cache/two levels=2:2 keys_zone=two:100m; proxy_cache_path /data/nginx/cache/three levels=1:1:2 keys_zone=three:1000m;
inactive 表示指定的时间内缓存的数据没有被请求则被删除,默认inactive为10分钟。inactive=1d 1天。inactive=30m 30分钟。
max_size 表示单个文件最大不超过的大小。它被用来删除不活动的缓存和控制缓存大小,当目前缓存的值超出max_size指定的值以后,超过其大小后最少使用数据(LRU替换算法)将被删除。max_size=10g表示当缓存池超过10g就会清除不经常使用的缓存文件。
clean_time 表示每间隔自动清除的时间。clean_time=1m 1分钟清除一次缓存。
好。说完了这个很重要的参数。咱们再来讲在server模块里的几个配置参数:
proxy_cache 用来指定用哪一个keys_zone的名字,也就是用哪一个目录下的缓存。上面咱们指定了三个one, two,three 。好比,我如今想用one 这个缓存目录 : proxy_cache one
proxy_cache_key 这个其实蛮重要的,它用来指定生成hash的url地址的格式。根据这个key映射成一个hash值,而后存入到本地文件。
proxy_cache_key $host$uri
表示不管后面跟的什么参数,都会访问一个文件,不会再生成新的文件。 而若是proxy_cache_key $is_args$args
,那么传入的参数 localhost/index.php?a=4 与localhost/index.php?a=44 将映射成两个不一样hash值的文件。proxy_cache_key 默认是
"$scheme$host$request_uri"
。可是通常咱们会把它设置成:$host$uri$is_args$args
一个完整的url路径。
proxy_cache_valid
它是用来为不一样的http响应状态码设置不一样的缓存时间。
proxy_cache_valid 200 302 10m; proxy_cache_valid 404 1m;
表示为http status code 为200和302的设置缓存时间为10分钟,404代码缓存1分钟。 若是只定义时间:
proxy_cache_valid 5m;
那么只对代码为200, 301和302的code进行缓存。 一样可使用any参数任何相响应:
proxy_cache_valid 200 302 10m; proxy_cache_valid 301 1h; proxy_cache_valid any 1m; #全部的状态都缓存1小时
好。缓存的基本一些配置讲完了。也大体知道了怎么使用这些参数。如今开始实战!咱们启动一台vagrant linux 机器 web1 (192.168.33.11) 用做远程代理机器,就不搞复杂的负载均衡了。
先在Mac本地加一个域名cache.iyangyi.com, 而后按照上面的配置在vhost 下新建一个proxy_cache.iyangyi.conf 文件:
proxy_cache_path /usr/local/var/cache levels=1:2 keys_zone=cache_zone:10m inactive=1d max_size=100m; server { listen 80; server_name cache.iyangyi.com; access_log /usr/local/var/log/nginx/cache.iyangyi.access.log main; error_log /usr/local/var/log/nginx/cache.iyangyi.error.log error; add_header X-Via $server_addr; add_header X-Cache $upstream_cache_status; location / { proxy_set_header X-Real-IP $remote_addr; proxy_cache cache_zone; proxy_cache_key $host$uri$is_args$args; proxy_cache_valid 200 304 1m; proxy_pass http://192.168.33.11; } }
打开审核元素或者firebug。看network网络请求选项,咱们能够看到,Response Headers,在这里咱们能够看到:
X-Cache:MISS X-Via:127.0.0.1
X-cache 为 MISS 表示未命中,请求被传送到后端。由于是第一次访问,没有缓存,因此确定是未命中。咱们再刷新下,就发现其变成了HIT, 表示命中。它还有其余几种状态:
MISS 未命中,请求被传送到后端
HIT 缓存命中
EXPIRED 缓存已通过期请求被传送到后端
UPDATING 正在更新缓存,将使用旧的应答
STALE 后端将获得过时的应答
BYPASS 缓存被绕过了
咱们再去看看缓存文件夹 /usr/local/var/cache里面是否有了文件:
cache git:(master) cd a/13 ➜ 13 git:(master) ls 5bd1af99bcb0db45c8bd601d9ee9e13a ➜ 13 git:(master) pwd /usr/local/var/cache/a/13
已经生成了缓存文件。
咱们在url 后面随便加一个什么参数,看会不会新生成一个缓存文件夹及文件:http://cache.iyangyi.com/?w=ww55 。由于咱们使用的生成规则是所有url转换(proxy_cache_key $host$uri$is_args$args;)
查看 X-cache 为 MISS,再刷新 ,变成HIT。再去看一下缓存文件夹 /usr/local/var/cache。
~cache git:(master) ls 4 a
果真又生成了一个4文件夹。
这一小节,主要来学习nginx中的URL重写怎么作。url重写模块,主要是在location模块面来实现,咱们一点一点的看。
首先看下location 正则匹配的使用。还记得以前是如何用location来定位.php文件的吗?
location ~ \.php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; include fastcgi.conf; }
咱们用~来表示location开启正则匹配, 这样:location ~。还能够用这个来匹配静态资源,缓存它们,设置过时时间:
location ~ .*\.(gif|jpg|jpeg|bmp|png|ico|txt|mp3|mp4|swf){ expires 15d; } location ~ .*\.(css|js){ expires 12h; }
expires 用来设置HTTP应答中的Expires和Cache-Control的头标时间,来告诉浏览器访问这个静态文件时,不用再去请求服务器,直接从本地缓存读取就能够了。
语法: expires [time|epoch|max|off] 默认值: expires off 做用域: http, server, location
能够在time值中使用正数或负数。“Expires”头标的值将经过当前系统时间加上您设定的 time 值来得到。能够设置的参数以下:
epoch 指定“Expires”的值为 1 January, 1970, 00:00:01 GMT。
max 指定“Expires”的值为 31 December 2037 23:59:59 GMT,“Cache-Control”的值为10年。
-1 指定“Expires”的值为 服务器当前时间 -1s,即永远过时。
负数:Cache-Control: no-cache。
正数或零:Cache-Control: max-age = #, # 会转换为指定时间的秒数。好比:1d、2h、3m。
off 表示不修改“Expires”和“Cache-Control”的值。
好比再看个例子: 控制图片等过时时间为30天
location ~ \.(gif|jpg|jpeg|png|bmp|ico)$ { expires 30d; }
咱们还能够控制哪个文件目录的时间,好比控制匹配/resource/或者/mediatorModule/里全部的文件缓存设置到最长时间。
location ~ /(resource|mediatorModule)/ { root /opt/demo; expires max; }
重写模块与不少模块一块儿使用。先看一下是怎么用的,看2个例子,而后咱们再一点一点讲每一个的使用方法:
location /download/ { if ($forbidden) { return 403; } if ($slow) { limit_rate 10k; } rewrite ^/(download/.*)/media/(.*)\..*$ /$1/mp3/$2.mp3 break; ...... }
location / { root html; index index.html index.htm; rewrite ^/bbs/(.*)$ http://192.168.18.201/forum/$1; }
上面2个例子就是利用rewrite来完成URL重写的。咱们慢慢来看它的用法。
break和编程语言中的用法同样,就是跳出某个逻辑。
语法:break
默认值:none
使用字段:server, location, if
if (!-f $request_filename) { break; }
上面这个例子就是在if里面使用break,意思是若是访问的文件名不存在,就跳出。后续会有更多的例子。
if 判断一个条件,若是条件成立,则后面的大括号内的语句将执行,相关配置从上级继承。
语法:if (condition) { … }
默认值:none
使用字段:server, location
能够在判断语句中指定下列值:
一个变量的名称;不成立的值为:空字符传”“或者一些用“0”开始的字符串。
一个使用=或者!=运算符的比较语句。
使用符号*和模式匹配的正则表达式:
~为区分大小写的匹配。
~*不区分大小写的匹配(firefox匹配FireFox)。
!和!*意为“不匹配的”。
使用-f和!-f检查一个文件是否存在。
使用-d和!-d检查一个目录是否存在。
使用-e和!-e检查一个文件,目录或者软连接是否存在。
使用-x和!-x检查一个文件是否为可执行文件。
$http_user_agent变量获取浏览器的agent,使用~ 来匹配大小写。
用户若是使用的IE 浏览器,就执行if里面的操做。
if ($http_user_agent ~ MSIE) { rewrite ^(.*)$ /msie/$1 break; }
$request_method变量获取请求的方法,使用=来判断是否等于POST 。
若是复合,就执行if 里面的操做。
if ($request_method = POST ) { return 405; }
$request_filename变量获取请求的文件名
,使用!-f来匹配文件,若是不是一个文件名,就执行if 里面的逻辑。
if (!-f $request_filename) { break; proxy_pass http://127.0.0.1; }
这个指令结束执行配置语句并为客户端返回状态代码,可使用下列的值:204,400,402-406,408,410, 411, 413, 416与500-504。此外,非标准代码444将关闭链接而且不发送任何的头部。
语法:return code
默认值:none
使用字段:server, location, if
语法:rewrite regex replacement flag
默认值:none
使用字段:server, location, if
rewrite用来重写url,有3个位置:
regex 表示用来匹配的正则
replacement 表示用来替换的
flag 是尾部的标记
flag能够是如下的值:
last - url重写后,立刻发起一个新的请求,再次进入server块,重试location匹配,超过10次匹配不到报500错误,地址栏url不变
break - url重写后,直接使用当前资源,再也不执行location里余下的语句,完成本次请求,地址栏url不变
redirect - 返回302临时重定向,url会跳转,爬虫不会更新url。
permanent - 返回301永久重定向。url会跳转。爬虫会更新url。
为空 - URL 不会变,可是内容已经变化,也是永久性的重定向。
上面的正则表达式的一部分能够用圆括号,方便以后按照顺序用$1-$9来引用。
咱们来看几个例子:
须要将/photos/123456重写成/path/to/photos/12/1234/123456.png
能够这样:
rewrite "/photos/([0-9] {2})([0-9] {2})([0-9] {2})" /path/to/photos/$1/$1$2/$1$2$3.png;
下面是一些简单的常见的重写:
rewrite ^/js/base.core.v3.js /js/base.core.v3.dev.js redirect; rewrite ^/js/comment.frame.js /js/comment.frame.dev.js redirect; rewrite ^/live-static/(.*)$ http://live.bilibili.com/public/$1 last;
在此记录下Nginx服务器nginx.conf的配置文件说明, 部分注释收集与网络:
# 运行用户 user www-data; # 启动进程,一般设置成和cpu的数量相等 worker_processes 1; # 全局错误日志及PID文件 error_log /var/log/nginx/error.log; pid /var/run/nginx.pid; # 工做模式及链接数上限 events { use epoll; #epoll是多路复用IO(I/O Multiplexing)中的一种方式,可是仅用于linux2.6以上内核,能够大大提升nginx的性能 worker_connections 1024; #单个后台worker process进程的最大并发连接数 # multi_accept on; } #设定http服务器,利用它的反向代理功能提供负载均衡支持 http { #设定mime类型,类型由mime.type文件定义 include /etc/nginx/mime.types; default_type application/octet-stream; #设定日志格式 access_log /var/log/nginx/access.log; #sendfile 指令指定 nginx 是否调用 sendfile 函数(zero copy 方式)来输出文件,对于普通应用, #必须设为 on,若是用来进行下载等应用磁盘IO重负载应用,可设置为 off,以平衡磁盘与网络I/O处理速度,下降系统的uptime. sendfile on; #将tcp_nopush和tcp_nodelay两个指令设置为on用于防止网络阻塞 tcp_nopush on; tcp_nodelay on; #链接超时时间 keepalive_timeout 65; #开启gzip压缩 gzip on; gzip_disable "MSIE [1-6]\.(?!.*SV1)"; #设定请求缓冲 client_header_buffer_size 1k; large_client_header_buffers 4 4k; include /etc/nginx/conf.d/*.conf; include /etc/nginx/sites-enabled/*; #设定负载均衡的服务器列表 upstream mysvr { #weigth参数表示权值,权值越高被分配到的概率越大 #本机上的Squid开启3128端口 server 192.168.8.1:3128 weight=5; server 192.168.8.2:80 weight=1; server 192.168.8.3:80 weight=6; } server { #侦听80端口 listen 80; #定义使用www.xx.com访问 server_name www.xx.com; #设定本虚拟主机的访问日志 access_log logs/www.xx.com.access.log main; #默认请求 location / { root /root; #定义服务器的默认网站根目录位置 index index.php index.html index.htm; #定义首页索引文件的名称 fastcgi_pass www.xx.com; fastcgi_param SCRIPT_FILENAME $document_root/$fastcgi_script_name; include /etc/nginx/fastcgi_params; } # 定义错误提示页面 error_page 500 502 503 504 /50x.html; location = /50x.html { root /root; } #静态文件,nginx本身处理 location ~ ^/(images|javascript|js|css|flash|media|static)/ { root /var/www/virtual/htdocs; #过时30天,静态文件不怎么更新,过时能够设大一点,若是频繁更新,则能够设置得小一点。 expires 30d; } #PHP 脚本请求所有转发到 FastCGI处理. 使用FastCGI默认配置. location ~ \.php$ { root /root; fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME /home/www/www$fastcgi_script_name; include fastcgi_params; } #设定查看Nginx状态的地址 location /NginxStatus { stub_status on; access_log on; auth_basic "NginxStatus"; auth_basic_user_file conf/htpasswd; } #禁止访问 .htxxx 文件 location ~ /\.ht { deny all; } } #第一个虚拟服务器 server { #侦听192.168.8.x的80端口 listen 80; server_name 192.168.8.x; #对aspx后缀的进行负载均衡请求 location ~ .*\.aspx$ { root /root;#定义服务器的默认网站根目录位置 index index.php index.html index.htm;#定义首页索引文件的名称 proxy_pass http://mysvr;#请求转向mysvr 定义的服务器列表 #如下是一些反向代理的配置可删除. proxy_redirect off; #后端的Web服务器能够经过X-Forwarded-For获取用户真实IP proxy_set_header Host $host; proxy_set_header X-Real-IP $remote_addr; proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; client_max_body_size 10m; #容许客户端请求的最大单文件字节数 client_body_buffer_size 128k; #缓冲区代理缓冲用户端请求的最大字节数, proxy_connect_timeout 90; #nginx跟后端服务器链接超时时间(代理链接超时) proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时) proxy_read_timeout 90; #链接成功后,后端服务器响应时间(代理接收超时) proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小 proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k如下的话,这样设置 proxy_busy_buffers_size 64k; #高负荷下缓冲大小(proxy_buffers*2) proxy_temp_file_write_size 64k; #设定缓存文件夹大小,大于这个值,将从upstream服务器传 } } }
上面咱们已经详细讲解了Nginx经常使用配置,从中咱们已经体会到了,Nginx模块化配置的优势。其中,模块化设计相似于面向对象中的接口类,它加强了nginx源码的可读性、可扩充性和可维护性。
因此,Nginx有五大优势:模块化、事件驱动、异步、非阻塞、多进程单线程。
由内核和模块组成的,其中内核完成的工做比较简单,仅仅经过查找配置文件将客户端请求映射到一个location block,而后又将这个location block中所配置的每一个指令将会启动不一样的模块去完成相应的工做。
Nginx的模块从结构上分为核心模块、基础模块和第三方模块:
核心模块:HTTP模块、EVENT模块和MAIL模块
基础模块:HTTP Access模块、HTTP FastCGI模块、HTTP Proxy模块和HTTP Rewrite模块,
第三方模块:HTTP Upstream Request Hash模块、Notice模块和HTTP Access Key模块。
Nginx的模块从功能上分为以下三类:
Core(核心模块):构建nginx基础服务、管理其余模块。
Handlers(处理器模块):此类模块直接处理请求,并进行输出内容和修改headers信息等操做。Handlers处理器模块通常只能有一个。
Filters (过滤器模块):此类模块主要对其余处理器模块输出的内容进行修改操做,最后由Nginx输出。
Proxies (代理类模块):此类模块是Nginx的HTTP Upstream之类的模块,这些模块主要与后端一些服务好比FastCGI等进行交互,实现服务代理和负载均衡等功能。
Nginx的核心模块主要负责创建nginx服务模型、管理网络层和应用层协议、以及启动针对特定应用的一系列候选模块。其余模块负责分配给web服务器的实际工做:
(1) 当Nginx发送文件或者转发请求到其余服务器,由Handlers(处理模块)或Proxies(代理类模块)提供服务;
(2) 当须要Nginx把输出压缩或者在服务端加一些东西,由Filters(过滤模块)提供服务。
每一个handlers(处理模块)都有机会映射到配置文件中定义的特定位置(location)
;若是有多个handlers(处理模块)映射到特定位置时,只有一个会“赢”(说明配置文件有冲突项,应该避免发生)。处理模块以三种形式返回:
OK
ERROR
或者放弃处理这个请求而让默认处理模块来处理(主要是用来处理一些静态文件,事实上若是是位置正确而真实的静态文件,默认的处理模块会抢先处理)。
若是handlers(处理模块)把请求反向代理到后端的服务器,就变成另一类的模块:load-balancers(负载均衡模块)
。负载均衡模块的配置中有一组后端服务器,当一个HTTP请求过来时,它决定哪台服务器应当得到这个请求。Nginx的负载均衡模块采用两种方法:
轮转法,它处理请求就像纸牌游戏同样从头至尾分发;
IP哈希法,在众多请求的状况下,它确保来自同一个IP的请求会分发到相同的后端服务器。
若是handlers(处理模块)没有产生错误,filters(过滤模块)将被调用
。多个filters(过滤模块)能映射到每一个位置,因此(好比)每一个请求均可以被压缩成块。它们的执行顺序在编译时决定。filters(过滤模块)是经典的“接力链表(CHAIN OF RESPONSIBILITY)”模型
:一个filters(过滤模块)被调用,完成其工做,而后调用下一个filters(过滤模块),直到最后一个filters(过滤模块)。
过滤模块链的特别之处在于:
每一个filters(过滤模块)不会等上一个filters(过滤模块)所有完成;
它能把前一个过滤模块的输出做为其处理内容;有点像Unix中的流水线;
过滤模块能以buffer(缓冲区)为单位进行操做,这些buffer通常都是一页(4K)大小,固然你也能够在nginx.conf文件中进行配置
。这意味着,好比,模块能够压缩来自后端服务器的响应,而后像流同样的到达客户端,直到整个响应发送完成。
总之,过滤模块链以流水线的方式高效率地向客户端发送响应信息。
客户端发送HTTP请求 –>
Nginx基于配置文件中的位置选择一个合适的处理模块 ->
(若是有)负载均衡模块选择一台后端服务器 –>
处理模块进行处理并把输出缓冲放到第一个过滤模块上 –>
第一个过滤模块处理后输出给第二个过滤模块 –>
而后第二个过滤模块又到第三个 –>
依此类推 –> 最后把响应发给客户端。
下图展现了Nginx模块处理流程:
Nginx模块处理流程
Nginx自己作的工做实际不多,当它接到一个HTTP请求时,它仅仅是经过查找配置文件将这次请求映射到一个location block,而此location中所配置的各个指令则会启动不一样的模块去完成工做,所以模块能够看作Nginx真正的劳动工做者。一般一个location中的指令会涉及一个handler模块和多个filter模块(固然,多个location能够复用同一个模块)。handler模块负责处理请求,完成响应内容的生成,而filter模块对响应内容进行处理
。
Nginx在启动时会以daemon形式在后台运行
,采用多进程+异步非阻塞IO事件模型
来处理各类链接请求。多进程模型包括一个master进程,多个worker进程,通常worker进程个数是根据服务器CPU核数来决定的
。master进程负责管理Nginx自己和其余worker进程
。以下图:
Master进程负责管理Nginx自己和其余worker进程
从上图中能够很明显地看到,4个worker进程的父进程都是master进程,代表worker进程都是从父进程fork出来的,而且父进程的ppid为1,表示其为daemon进程。
须要说明的是,在nginx多进程中,每一个worker都是平等的,所以每一个进程处理外部请求的机会权重都是一致的。
Nginx架构及工做流程图:
Nginx架构及工做流程图
Nginx的每个Worker进程都管理着大量的线程,真正处理请求业务的是Worker之下的线程。worker进程中有一个ngx_worker_process_cycle()函数,执行无限循环,不断处理收到的来自客户端的请求,并进行处理
,直到整个Nginx服务被中止。
worker 进程中,ngx_worker_process_cycle()函数就是这个无限循环的处理函数。在这个函数中,一个请求的简单处理流程以下:
操做系统提供的机制(例如 epoll, kqueue 等)产生相关的事件。
接收和处理这些事件,如是接收到数据,则产生更高层的 request 对象。
处理 request 的 header 和 body。
产生响应,并发送回客户端。
完成 request 的处理。
从新初始化定时器及其余事件。
下面来介绍一个请求进来,多进程模型的处理方式:
首先,master进程一开始就会根据咱们的配置,
来创建须要listen的网络socket fd
,而后fork出多个worker进程。其次,根据进程的特性,新创建的worker进程,也会和master进程同样,具备相同的设置。所以,
其也会去监听相同ip端口的套接字socket fd
。而后,这个时候有多个worker进程都在监听一样设置的socket fd,
意味着当有一个请求进来的时候,全部的worker都会感知到。这样就会产生所谓的“惊群现象”
。为了保证只会有一个进程成功注册到listenfd的读事件,nginx中实现了一个“accept_mutex”相似互斥锁,只有获取到这个锁的进程,才能够去注册读事件
。其余进程所有accept 失败。最后,监听成功的worker进程,读取请求,解析处理,响应数据返回给客户端,断开链接,结束。所以,一个request请求,只须要worker进程就能够完成。
进程模型的处理方式带来的一些好处就是:进程之间是独立的
,也就是一个worker进程出现异常退出,其余worker进程是不会受到影响的;此外,独立进程也会避免一些不须要的锁操做,这样子会提升处理效率,而且开发调试也更容易。
如前文所述,多进程模型+异步非阻塞模型
才是胜出的方案。单纯的多进程模型会致使链接并发数量的下降,而采用异步非阻塞IO模型很好的解决了这个问题
;而且还所以避免的多线程的上下文切换致使的性能损失。
worker进程会竞争监听客户端的链接请求:这种方式可能会带来一个问题,就是可能全部的请求都被一个worker进程给竞争获取了,致使其余进程都比较空闲,而某一个进程会处于忙碌的状态,这种状态可能还会致使没法及时响应链接而丢弃discard掉本有能力处理的请求
。这种不公平的现象,是须要避免的,尤为是在高可靠web服务器环境下。
针对这种现象,Nginx采用了一个是否打开accept_mutex选项的值,ngx_accept_disabled标识控制一个worker进程是否须要去竞争获取accept_mutex选项,进而获取accept事件
。
ngx_accept_disabled值,nginx单进程的全部链接总数的八分之一,减去剩下的空闲链接数量,获得的这个ngx_accept_disabled。
当ngx_accept_disabled大于0时,不会去尝试获取accept_mutex锁,而且将ngx_accept_disabled减1,因而,每次执行到此处时,都会去减1,直到小于0。不去获取accept_mutex锁,就是等于让出获取链接的机会,很显然能够看出,
当空闲链接越少时,ngx_accept_disable越大,因而让出的机会就越多,这样其它进程获取锁的机会也就越大
。不去accept,本身的链接就控制下来了,其它进程的链接池就会获得利用,这样,nginx就控制了多进程间链接的平衡了。
从 Nginx 的内部来看,一个 HTTP Request 的处理过程涉及到如下几个阶段:
初始化 HTTP Request(读取来自客户端的数据,生成 HTTP Request 对象,该对象含有该请求全部的信息)。
处理请求头。
处理请求体。
若是有的话,调用与此请求(URL 或者 Location)关联的 handler。
依次调用各 phase handler 进行处理。
在创建链接过程当中,对于nginx监听到的每一个客户端链接,都会将它的读事件的handler设置为ngx_http_init_request函数,这个函数就是请求处理的入口
。在处理请求时,主要就是要解析http请求,好比:uri,请求行等,而后再根据请求生成响应。下面看一下nginx处理的具体过程:
Nginx处理的具体过程
在这里,咱们须要了解一下 phase handler 这个概念。phase 字面的意思,就是阶段。因此 phase handlers 也就好理解了,就是包含若干个处理阶段的一些 handler
。
在每个阶段,包含有若干个 handler
,再处理到某个阶段的时候,依次调用该阶段的 handler 对 HTTP Request 进行处理。
一般状况下,一个 phase handler 对这个 request 进行处理,并产生一些输出。一般 phase handler 是与定义在配置文件中的某个 location 相关联的
。
一个 phase handler 一般执行如下几项任务:
获取 location 配置。
产生适当的响应。
发送 response header。
发送 response body。
当 Nginx 读取到一个 HTTP Request 的 header 的时候,Nginx 首先查找与这个请求关联的虚拟主机的配置
。若是找到了这个虚拟主机的配置,那么一般状况下,这个 HTTP Request 将会通过如下几个阶段的处理(phase handlers):
NGX_HTTP_POST_READ_PHASE: 读取请求内容阶段
NGX_HTTP_SERVER_REWRITE_PHASE: Server 请求地址重写阶段
NGX_HTTP_FIND_CONFIG_PHASE: 配置查找阶段
NGX_HTTP_REWRITE_PHASE: Location请求地址重写阶段
NGX_HTTP_POST_REWRITE_PHASE: 请求地址重写提交阶段
NGX_HTTP_PREACCESS_PHASE: 访问权限检查准备阶段
NGX_HTTP_ACCESS_PHASE: 访问权限检查阶段
NGX_HTTP_POST_ACCESS_PHASE: 访问权限检查提交阶段
NGX_HTTP_TRY_FILES_PHASE: 配置项 try_files 处理阶段
NGX_HTTP_CONTENT_PHASE: 内容产生阶段
NGX_HTTP_LOG_PHASE: 日志模块处理阶段
在内容产生阶段,为了给一个 request 产生正确的响应,Nginx 必须把这个 request 交给一个合适的 content handler 去处理
。若是这个 request 对应的 location 在配置文件中被明确指定了一个 content handler,那么Nginx 就能够经过对 location 的匹配,直接找到这个对应的 handler,并把这个 request 交给这个 content handler 去处理。这样的配置指令包括像,perl,flv,proxy_pass,mp4等。
若是一个 request 对应的 location 并无直接有配置的 content handler,那么 Nginx 依次尝试:
若是一个 location 里面有配置 random_index on,那么随机选择一个文件,发送给客户端。
若是一个 location 里面有配置 index 指令,那么发送 index 指令指明的文件,给客户端。
若是一个 location 里面有配置 autoindex on,那么就发送请求地址对应的服务端路径下的文件列表给客户端。
若是这个 request 对应的 location 上有设置 gzip_static on,那么就查找是否有对应的.gz文件存在,有的话,就发送这个给客户端(客户端支持 gzip 的状况下)。
请求的 URI 若是对应一个静态文件,static module 就发送静态文件的内容到客户端。
内容产生阶段完成之后,生成的输出会被传递到 filter 模块去进行处理
。filter 模块也是与 location 相关的。全部的 fiter 模块都被组织成一条链。输出会依次穿越全部的 filter,直到有一个 filter 模块的返回值代表已经处理完成。
这里列举几个常见的 filter 模块,例如:
server-side includes。
XSLT filtering。
图像缩放之类的。
gzip 压缩。
在全部的 filter 中,有几个 filter 模块须要关注一下。按照调用的顺序依次说明以下:
copy: 将一些须要复制的 buf(文件或者内存)从新复制一份而后交给剩余的 body filter 处理。
postpone: 这个 filter 是负责 subrequest 的,也就是子请求的。
write: 写输出到客户端,其实是写到链接对应的 socket 上。
根据以上请求步骤所述,请求完整的处理过程以下图所示: