最近在cloudfoundry的部署过程中需要对router进行负载均衡,最容易想到的是使用反向代理服务器nginx 来做访问的负载均衡。最开始的时候觉得这个东西比较复杂,通过阅读了章亦春的nginx教程之后,觉得 nginx对http的处理是多么的简洁和清晰,在这里我想理一理自己的思路。
nginx
对nginx的绝大部分配置是通过配置文件来完成的,完全可以这么说nginx配置文件的书写可以作为一门 编程语言了,我们这里不对nginx具体的配置文件怎么书写来做详细介绍,只对其处理http请求的流程做 一个解剖。
- nginx对HttpRequest的处理分为几个阶段?
nginx处理请求一共可以划分为11个阶段,依次是:post-read、server-rewrite、findconfig、rewrite、 post-rewrite、preaccess、access、postaccess、try-files、content 以及 log。我们来一个一个地看:
post-read
server { listen 8080; set_real_ip_from 127.0.0.1; real_ip_header X-My-IP; location /test { set $addr $remote_addr; echo "from: $addr"; } }
这段配置的意思就是将来自127.0.0.1请求的源地址转换为X-My-Ip的值。那么我们为什么要修改请求 的源IP地址呢,有可能这个请求是通过代理服务器转发过来的,如果不经过这步处理,nginx就不知道请求 是由谁真正发过来的,所以需要把请求的源地址进行恢复处理,这样代理服务器就跟不存在一样。
Server-rewrite阶段
server { listen 8080; location /test { set $b "$a, world"; echo $b; } set $a hello; }
这个阶段会对server的set变量进行赋值,所以$a要比$b先执行,在localtion里面set是按顺序来执行的, 但是echo写到哪里都无所谓,因为它根本不会在本阶段执行。
findconfig
location /hello { echo "hello world"; }
这一块很简单,就是匹配相应的localtion,但是在此之前所有写在localtion里面的配置变量都不会 生效,这就是为什么前面的set_real_ip_from的模块要放在server域里面。
rewrite
location /test { set $a "hello%20world"; set_unescape_uri $b $a; set $c "$b!"; echo $c; }
所有的localtion中的set指令会在这个阶段按顺序执行,其中set_unescape_uri将%20转化为了空格, 其执行优先级与set一样。
post-rewrite
location /foo { rewrite ^ /bar; rewrite ^ /baz; echo foo; } location /bar { echo bar; } location /baz { echo baz; }
这个阶段会执行localtion中的rewrite指令,这个时候会发生页面的跳转,但是post-rewrite阶段只会 发生一次跳转。即上面的代码中/foo只会跳转到/baz。
preaccess
server { listen 8080; location /test { set_real_ip_from 127.0.0.1; real_ip_header X-Real-IP; echo "from: $remote_addr"; } }
上面的set_real_ip_from因为在localtion里面所以没有机会在pre-read阶段执行,所以这里是一个 补救措施。同时标准模块 ngx_limit_req 和 ngx_limit_zone 就运行在此阶段,前者可以控制请求的访 问频度,而后者可以限制访问的并发度。
access
location /hello { allow 127.0.0.1; deny all; echo "hello world"; }
在这里控制对请求是接受(allow)还是拒绝(deny),模块还支持所谓的“CIDR 记法”来表示一个网段, 例如 169.200.179.4/24 则表示路由前缀是169.200.179.0(或者说子网掩码是 255.255.255.0)的网段。
post-access
location /test { satisfy all; deny all; access_by_lua 'ngx.exit(ngx.OK)'; echo something important; }
这里通过satisfy来控制允许访问的策略,。比如模块 A 和 B 都在 access 阶段注册了与访问控制相关的处理程序,那就有两种 协作方式,一是模块 A 和模块 B 都得通过验证才算通过,二是模 块 A 和模块 B 只要其中任一个通过验证就算通过。第一种协作方 式称为 all 方式(或者说“与关系”),第二种方式则被称为 any 方 式(或者说“或关系”)。默认情况下,Nginx 使用的是 all 方式。
try-files
root /var/www/; location /test { try_files /foo /bar/ /baz; echo "uri: $uri"; } location /foo { echo foo; } location /bar/ { echo bar; } location /baz { echo baz; }
它会在 try-files 阶段依次检查前两个参数 /foo 和 /bar/ 所对应的文件系统对象是否存在,然后 执行最后一个匹配的跳转。注意在URI中/foo对应是文件,/foo/对应的是目录
content
location /test { set $age 1;# rewrite phase rewrite_by_lua "ngx.var.age = ngx.var.age + 1"; deny 10.32.168.49;# access phase access_by_lua "ngx.var.age = ngx.var.age * 3"; echo "age = $age";# content phase }
content 阶段是所有请求处理阶段中最为重要的一个,因为运行在这个阶段的配置指令一般都肩负 着生成“内容”(content)并输出 HTTP 响应的使命。确认响应文件存在后,自动设置 Content-Type、Content- Length 以及 Last-Modified 等响应头,并最终发送出去。
log
error_log logs/error.log debug;
即是将log的等级调为debug,由于打日志是影响性能的,所以nginx默认禁用调试日志的。
nginx是使用C语言开发的,所以学好C语言是王道啊!