资讯 > 行业资讯

程序猿成长日记 | 浅谈nginx使用之道
2017/3/23 14:04:54

  作者:孔宁宁 BoCloud博云 测试工程师

  What Nginx

  nginx一个轻量级的web服务器,同时可以作为反向代理服务器,将web请求分发至后端的realserver。作为webserver 和proxy-server nginx本身来说有什么优点呢?

  1、作为web服务器

  并发量高,在30000的并发量下,在nginx+PHP(fastcgi)架构下,开启10个nginx进程会消耗10*15=150M内存,开启64个CGI进程会消耗64*20=1280M内存。在内存、cpu的消耗量不是很大的情况下,实现了高并发量。

  nginx选用的是epoll网络I/O模型,而apace则采用的是select,所以nginx的处理速度更快。

  在10000个非活动连接的长连接的情况下。只需要消耗2.5M内存。

  在启动nginx后,主进程master会生成相应数量的worker进程。worker进程用来接收client的请求。如果我们的服务器配置发生了改变。原来已经生成的并且正在为client提供请求的进程继续使用原来的配置。当服务结束之后,master进程将老配置的worker进程终止,在重新生成新的进程。

  2、作为proxy服务器

  比较流行的反向代理服务器有nginx,haproxy,lvs等,三种反向代理服务器有各自的优缺点:

  lvs是基于四层实现的负载均衡,所谓的四层基于的是socket实现反向代理,比如说把A的80请求全部代理到B的80上去。lvs工作四层之上,仅做分发之用,不会产生额外的开销,这就决定的了lvs的性能超强,其对内存和cpu的开销都是很小的。

  lvs在代理的过程中,DR单纯的把请求分发到后面的realserver,realserver直接对client端进行响应。所以lvs的分发性能是其他的反向代理服务器不能比较的。

lvs请求图示(DR模型)

  nginx对于lvs而言,首先nginx是工作在七层之上的反向代理服务器。所谓的七层表示nginx可以针对某个目录,某个url来进行反向代理。比如说把A服务器的 /app01/这个目录代理到B服务器的/app01/目录。这样就实现了基于应用的反向代理。同时nginx可以实现对后端服务器的健康检查,当分发的请求发生错误之后,会将请求重新分发到另外的realserver,同样nginx可以承受大并发请求,且采用异步请求处理,减轻了后端服务器的压力。

  haproxy本身作为作为反向代理服务器,会比nginx具有更好的处理速度,而且可以解决nginx出现的session共享问题。(nginx可以采用ip_hash的调度方法解决)。haproxy同样可以对后端服务器进行健康检查。但是haproxy的配置相对于nginx来说略微复杂(个人意见仅供参考)。

nginx反向代理图示

  How nginx

  nginx无论作为web服务器还是反向代理服务器,location和正则表达式的灵活应用都是他的一大优势,可以任意的匹配我们想进行操作的url,并对相应的url做rewrite和proxy_pass处理。对于nginx的location匹配我有话说。

  1.location分类:正则location和普通的location

  正则location:以”~”和”~*”开头,其中*好表示后面的正则表达式忽略大小写

  普通location:“=”,“^~ ”和“@ ”和无任何前缀的都属于普通location 。

  2.如果nginx配置中两种location都存在,先匹配那个呢,另外location的匹配和书写的顺序有关系吗?

  在正则location和普通location都存在的时候,location的匹配规则是先匹配普通的location,在匹配普通location的过程中,遵循的是最长匹配原则。然后再匹配正则的location,但后边匹配到的正则location会覆盖掉先前的普通的location,除非普通的location是完全匹配的。

  下面用几个简单的例子加以说明:

  1、location =/ {
  }
  2、location / {
  }
  3、location ^~ /image/ {
  }
  4、 location ~* \.(gif|jpg|jpeg)${
  }

  请求:
   /                                                ----->匹配第一个url
  /documents/docment.html        ----->匹配第二个url
  /images/boc.gif                         ----->匹配第三个url
  /documents/boc.jpg                  ----->匹配第四个url

  解析
  1:如果location中明确标注了=号,那么此location只能精确的匹配根/这个location,因为此时是精确匹配,所以不再进行正则的匹配。

  2:/documents/docment.html只有/这个location会匹配。虽然前缀不是那么长,但是在这四个location中算是最长的匹配。

  3:/images/boc.gif,这个url首先会匹配到/这个普通的location和/image/这两个,但是/image/的匹配前缀更长,所以选择后面的进行匹配。~^表示的是在匹配的过程中,匹配完普通location之后不在进行正则的匹配。

  4:/documents/boc.jpg  首先会匹配到/,然后进行正则的匹配,会匹配到最后的~* \.(gif|jpg|jpeg)$,匹配到之后,正则的会把普通的覆盖掉。

  location匹配进阶

  server {
       listen       80;
       server_name  www.bocloud.com.cn;
                  location /boc/test.html {
           allow all;
       }
                 location ^~ / {
           root   html;
           index  index.html index.htm;
           deny all;
       }
       location ~ \.html$ {
           allow all;
       }
  }

  分别访问 curl -I  http://www.bocloud.com.cn/,http://www.bocloud.com.cn/a.html,http://www.bocloud.com.cn/boc/test.html,http://www.bocloud.com.cn/test.html。

  总结:为什么除了/boc/test.html之外,其余根开始匹配的全部都返回的是403呢,因为在匹配的过程中,匹配到根路径之后,前面^~说明就不在进行正则的匹配。所以直接执行了里面的deny all请求,所以返回了403。

  如果我们把前面的^~符号去掉会发生什么呢?

  server {
       listen       80;
       server_name  www.bocloud.com.cn;
                  location /boc/test.html {
           allow all;
       }
                 location / {
           root   html;
           index  index.html index.htm;
           deny all;
       }
       location ~ \.html$ {
           allow all;
       }
  }

  总结:除了根的匹配之外,其余的全部是404的返回值,为什么呢,因为location /之后还会进行正则的匹配,结果匹配到了 ~ \.html$ 就会把最开始的进行覆盖,执行里面的allow all(虽然是allow all,但是没有文件,返回是404,404表明请求已经到达了服务器,但是服务器不存在客户端请求的文件,而403表示的是请求根本没有到达服务器直接被拒绝掉,这两个存在本质的区别)就返回了404状态吗。

  location的匹配规则你懂了吗?

  总结

  普通location会先于正则location进行匹配。

  如果匹配到普通之后,想结束不再匹配正则的location,那么可以在普通的location前面加上 ^~这两个符号。

  如果继续匹配正则location,正则匹配到的结果会覆盖普通匹配到的结果。

  匹配正则的过程中,和正则的为上下位置有关系,因为正则location是匹配到一个随即停止匹配。并不存在普通location的最大前缀匹配原则。

  3.nginx rewrite规则

  在nginx中rewrite可以使我们将url进行灵活的转换,但是nginx在转换的过程中会有许多的坑,下面的这些坑你踩过吗?

  rewrite既可以写在server中又可以写在location中,但是写在这两个中有什么区别呢,会有什么先后的顺序吗?

  答案是肯定的,如果两者同时出现的话,首先会匹配的是server中的rewrite规则,然后进行匹配的是location中的rewrite规则,那server中location匹配完了之后会在继续匹配location中的rewrite规则吗?如果想解决这个问题,需要简要的说下rewrite的4个flag位。

  1、last:匹配完当前的uri之后不在进行匹配。
2、break:匹配完当前的uri之后不在进行匹配。
3、permanent:永久重定向,http请求返回码301。
4、redirect:临时重定向,http请求返回码302。

  没看错吧,为什么last和break的定义一样呢?解析的是从官方翻译过来的,下面看几个例子。

  server {
        listen       80;
        server_name  www.bocloud.com.cn ;
        root html;

          rewrite_log on;   # 打开 URL 重写模块的日志开关,以便写入 error_log
        location  /aaa.html {
            rewrite "^/aaa\.html$"  /bbb.html;
            rewrite "^/bbb\.html$"  /ddd.html;
        }
        location  /bbb.html {
            rewrite "^/bbb\.html$" /ccc.html;
        }
  }

  请求:
  curl http://www.bocloud.com.cn/aaa.html

  返回bbb.html,这个应该不难理解,先是匹配自己location中的uri规则,之后生成的新的uri也会继续匹配,直到匹配完成。

  server {
        listen       80;
        server_name  www.bocloud.com.cn ;
        root html;

          rewrite_log on;   # 打开 URL 重写模块的日志开关,以便写入 error_log
        location  /aaa.html {
            rewrite "^/aaa\.html$"  /bbb.html last;
            rewrite "^/bbb\.html$"  /ddd.html;
        }
        location  /bbb.html {
            rewrite "^/bbb\.html$" /ccc.html;
        }
  }

  curl http://www.bocloud.com.cn/aaa.html
  返回ccc.html

  为什么会返回的是ccc.html呢,因为rewrite "^/aaa\.html$"  /bbb.html last;后面的flag位last,这表明停止当前location中的uri的继续的匹配,但是重新生成的uri不会停止匹配,会继续匹配其他的location,所以匹配到了 /bbb.html,所以返回的是ccc.html。

  erver {
        listen       80;
        server_name  www.bocloud.com.cn ;
        root html;

          rewrite_log on;   # 打开 URL 重写模块的日志开关,以便写入 error_log
        location  /aaa.html {
            rewrite "^/aaa\.html$"  /bbb.html break;
            rewrite "^/bbb\.html$"  /ddd.html;
        }
        location  /bbb.html {
            rewrite "^/bbb\.html$" /ccc.html;
        }
  }
  请求:
  curl http://www.bocloud.com.cn/aaa.html

  返回bbb.html,当遇到break之后,停止一切的匹配。从此处结束。所以说break比last的力度会更大些。就好像是程序里面的循环一样,遇到break之后,结束所有的循环。

  同样,如果rewrite规则同时在server和location中出现,server的总是先于location的匹配,直到匹配结束。

  nginx的rewrite规则你了解了吗?
 
  Use nginx

  核心配置
  upstream  boc  {
  server 192.168.1.1  weight=1 max_fails=2 fail_timeout=2;
  server 192.168.1.2  weight=1 max_fails=2 fail_timeout=2;
  }
  server
        {
        listen       80;
        server_name www.bocloud.com.cn;
        location / {
                proxy_pass        http://boc;
                proxy_set_header   Host             $host;
                proxy_set_header   X-Real-IP        $remote_addr;
                proxy_set_header   X-Forwarded-For  $proxy_add_x_forwarded_for;
        }
       access_log  off;
    }

  在这里,定义了一个web的集群,包含两台realserver,并定义了健康检查的规则。当client访问www.bocloud.com.cn这个域名的时候,前方的nginx就会把请求发送到192.168.1.1和192.168.1.2这两台服务器,默认的分配规则是roundrobin。

  上面简单的对nginx作为反向代理服务器和其他的几款反向代理软件简单的做了些比较,也介绍了些nginx中的location和反向代理集群的配置。无论是nginx还是haproxy亦或是lvs。都有自己的优势和不足的地方。没有那个好那个坏的区分。在应用中,选择自己合适的才是最好的。正所谓无论白猫还是黑猫,能抓到老鼠的都是好猫。

版权所有:苏州博纳讯动软件有限公司     苏ICP备13004761号