nginx处理HttpRequest流程

最近在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。我们来一个一个地看:

  1. 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就不知道请求 是由谁真正发过来的,所以需要把请求的源地址进行恢复处理,这样代理服务器就跟不存在一样。

  1. Server-rewrite阶段

    server {
      listen 8080;
      location /test {
      set $b "$a, world";
      echo $b;
      }
     set $a hello;
     }
    

这个阶段会对server的set变量进行赋值,所以$a要比$b先执行,在localtion里面set是按顺序来执行的, 但是echo写到哪里都无所谓,因为它根本不会在本阶段执行。

  1. findconfig

       location /hello {
          echo "hello world";
         }
    

这一块很简单,就是匹配相应的localtion,但是在此之前所有写在localtion里面的配置变量都不会 生效,这就是为什么前面的set_real_ip_from的模块要放在server域里面。

  1. rewrite

    location /test {
       set $a "hello%20world";
       set_unescape_uri $b $a;
       set $c "$b!";
       echo $c;
     }
    

所有的localtion中的set指令会在这个阶段按顺序执行,其中set_unescape_uri将%20转化为了空格, 其执行优先级与set一样。

  1. post-rewrite

    location /foo {
      rewrite ^ /bar;
      rewrite ^ /baz;
      echo foo;
      }
      location /bar {
      echo bar;
      }
      location /baz {
      echo baz;
     }
    

    这个阶段会执行localtion中的rewrite指令,这个时候会发生页面的跳转,但是post-rewrite阶段只会 发生一次跳转。即上面的代码中/foo只会跳转到/baz。

  2. 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 就运行在此阶段,前者可以控制请求的访 问频度,而后者可以限制访问的并发度。

  3. 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)的网段。

  4. 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 方式。

  5. 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/对应的是目录

  6. 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 等响应头,并最终发送出去。

  7. log

    error_log logs/error.log debug;

即是将log的等级调为debug,由于打日志是影响性能的,所以nginx默认禁用调试日志的。

nginx是使用C语言开发的,所以学好C语言是王道啊!



Previous     Next
zhing /
Published under (CC) BY-NC-SA in categories nginx  tagged with nginx