nginx

Nginx基本功能

1) 静态资源的web服务器;

2) http协议的反向代理服务器;

3) pop3, smpt,imap4等邮件协议的反向代理;

4) 能缓存打开的文件(元数据:文件的描述符等等信息)

5) 支持FastCGI(php-fpm), uWSGI(Python WebFramwork)等协议机制,实现代理后端应用程序交互

6) 高度模块化(非DSO机制)

core module

核心公用模块

Standard HTTP  modules

标准(核心)HTTP模块;自动编译进程序不止一个

Optional HTTP  modules

可选HTTP模块

Mail modules

邮件模块

3rd party modules

第三方模块,在编译时需手动指明加载方式加载

7) 支持过滤器,例如zip,SSI

8) 支持SSL加密机制;

9) web服务相关的功能:虚拟主机(server)、keepalive、访问日志(支持基于日志缓冲提高其性能)、urlrewirte、路径别名、基于IP及用户的访问控制、支持速率限制及并发数限制;

Nginx的基本架构:master/worker

master/worker模型:一个master进程可生成一个或多个worker进程;每个worker基于时间驱动机制可以并行响应多个请求

master:加载配置文件、管理worker进程、平滑升级,...

worker:http服务,http代理,fastcgi代理,...

事件驱动:epoll(Linux),kqueue(FreeBSD), /dev/poll(Solaris)

消息通知:select,poll, rt signals,支持sendfile,sendfile64,支持AIO,mmap


nginx是以多进程的方式来工作的,当然nginx也是支持多线程的方式的,只是我们主流的方式还是多进程的方式,也是nginx的默认方式。nginx采用多进程的方式有诸多好处 .

(1) nginx在启动后,会有一个master进程和多个worker进程。master进程主要用来管理worker进程,包含:接收来自外界的信号,向各worker进程发送信号,监控 worker进程的运行状态,当worker进程退出后(异常情况下),会自动重新启动新的worker进程。而基本的网络事件,则是放在worker进程中来处理了 。多个worker进程之间是对等的,他们同等竞争来自客户端的请求,各进程互相之间是独立的 。一个请求,只可能在一个worker进程中处理,一个worker进程,不可能处理其它进程的请求。

worker进程的个数是可以设置的,一般我们会设置与机器cpu核数一致,这里面的原因与nginx的进程模型以及事件处理模型是分不开的 。

(2)Master接收到信号以后怎样进行处理(./nginx -s reload )?首先master进程在接到信号后,会先重新加载配置文件,然后再启动新的进程,并向所有老的进程发送信号,告诉他们可以光荣退休了。新的进程在启动后,就开始接收新的请求,而老的进程在收到来自master的信号后,就不再接收新的请求,并且在当前进程中的所有未处理完的请求处理完成后,再退出 .

(3) worker进程又是如何处理请求的呢?我们前面有提到,worker进程之间是平等的,每个进程,处理请求的机会也是一样的。当我们提供80端口的http服务时,一个连接请求过来,每个进程都有可能处理这个连接,怎么做到的呢?

首先,每个worker进程都是从master进程fork过来,在master进程里面,先建立好需要listen的socket之后,然后再fork出多个worker进程,这样每个worker进程都可以去accept这个socket(当然不是同一个socket,只是每个进程的这个socket会监控在同一个ip地址与端口,这个在网络协议里面是允许的)。

一般来说,当一个连接进来后,所有在accept在这个socket上面的进程,都会收到通知,而只有一个进程可以accept这个连接,其它的则accept失败,这是所谓的惊群现象。

当然,nginx也不会视而不见,所以nginx提供了一个accept_mutex这个东西,从名字上,我们可以看这是一个加在accept上的一把共享锁。有了这把锁之后,同一时刻,就只会有一个进程在accpet连接,这样就不会有惊群问题了。

accept_mutex是一个可控选项,我们可以显示地关掉,默认是打开的。当一个worker进程在accept这个连接之后,就开始读取请求,解析请求,处理请求,产生数据后,再返回给客户端,最后才断开连接,这样一个完整的请求就是这样的了。我们可以看到,一个请求,完全由worker进程来处理,而且只在一个worker进程中处理。

(4)nginx采用这种进程模型有什么好处呢?采用独立的进程,可以让互相之间不会影响,一个进程退出后,其它进程还在工作,服务不会中断,master进程则很快重新启动新的worker进程。当然,worker进程的异常退出,肯定是程序有bug了,异常退出,会导致当前worker上的所有请求失败,不过不会影响到所有请求,所以降低了风险。当然,好处还有很多,大家可以慢慢体会。

(5).有人可能要问了,nginx采用多worker的方式来处理请求,每个worker里面只有一个主线程,那能够处理的并发数很有限啊,多少个worker就能处理多少个并发,何来高并发呢?非也,这就是nginx的高明之处,nginx采用了异步非阻塞的方式来处理请求,也就是说,nginx是可以同时处理成千上万个请求的 .对于IIS服务器每个请求会独占一个工作线程,当并发数上到几千时,就同时有几千的线程在处理请求了。这对操作系统来说,是个不小的挑战,线程带来的内存占用非常大,线程的上下文切换带来的cpu开销很大,自然性能就上不去了,而这些开销完全是没有意义的。我们之前说过,推荐设置worker的个数为cpu的核数,在这里就很容易理解了,更多的worker数,只会导致进程来竞争cpu资源了,从而带来不必要的上下文切换。而且,nginx为了更好的利用多核特性,提供了cpu亲缘性的绑定选项,我们可以将某一个进程绑定在某一个核上,这样就不会因为进程的切换带来cache的失效


首先,nginx在启动时,会解析配置文件,得到需要监听的端口与ip地址,然后在nginx的master进程里面,先初始化好这个监控的socket(创建socket,设置addrreuse等选项,绑定到指定的ip地址端口,再listen),然后再fork(一个现有进程可以调用fork函数创建一个新进程。由fork创建的新进程被称为子进程 )出多个子进程出来,然后子进程会竞争accept新的连接。此时,客户端就可以向nginx发起连接了。当客户端与nginx进行三次握手,与nginx建立好一个连接后,此时,某一个子进程会accept成功,得到这个建立好的连接的socket,然后创建nginx对连接的封装,即ngx_connection_t结构体。接着,设置读写事件处理函数并添加读写事件来与客户端进行数据的交换。最后,nginx或客户端来主动关掉连接,到此,一个连接就寿终正寝了。

当然,nginx也是可以作为客户端来请求其它server的数据的(如upstream模块),此时,与其它server创建的连接,也封装在ngx_connection_t中。作为客户端,nginx先获取一个ngx_connection_t结构体,然后创建socket,并设置socket的属性( 比如非阻塞)。然后再通过添加读写事件,调用connect/read/write来调用连接,最后关掉连接,并释放ngx_connection_t。


Nginx配置upstream实现负载均衡

如果Nginx没有仅仅只能代理一台服务器的话,那它也不可能像今天这么火,Nginx可以配置代理多台服务器,当一台服务器宕机之后,仍能保持系统可用。具体配置过程如下:

  1. 在http节点下,添加upstream节点。
    upstream linuxidc { 
       server 10.0.6.108:7080; 
       server 10.0.0.85:8980; 
    }
    
  2. 将server节点下的location节点中的proxy_pass配置为:
    http:// + upstream名称,即“
    http://linuxidc”.
    
    location / { 
            root  html; 
            index  index.html index.htm; 
            proxy_pass http://linuxidc; 
    }
    
  3. 现在负载均衡初步完成了。upstream按照轮询(默认)方式进行负载,每个请求按时间顺序逐一分配到不同的后端服务器,如果后端服务器down掉,能自动剔除。虽然这种方式简便、成本低廉。但缺点是:可靠性低和负载分配不均衡。适用于图片服务器集群和纯静态页面服务器集群。

除此之外,upstream还有其它的分配策略,分别如下:

  • weight(权重)

指定轮询几率,weight和访问比率成正比,用于后端服务器性能不均的情况。如下所示,10.0.0.88的访问比率要比10.0.0.77的访问比率高一倍。

upstream linuxidc{ 
      server 10.0.0.77 weight=5; 
      server 10.0.0.88 weight=10; 
}
  • ip_hash(访问ip)

每个请求按访问ip的hash结果分配,这样每个访客固定访问一个后端服务器,可以解决session的问题。

upstream favresin{ 
      ip_hash; 
      server 10.0.0.10:8080; 
      server 10.0.0.11:8080; 
}
  • fair(第三方)

按后端服务器的响应时间来分配请求,响应时间短的优先分配。与weight分配策略类似。

upstream favresin{      
      server 10.0.0.10:8080; 
      server 10.0.0.11:8080; 
      fair; 
}
  • url_hash(第三方)

按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,后端服务器为缓存时比较有效。

注意:在upstream中加入hash语句,server语句中不能写入weight等其他的参数,hash_method是使用的hash算法。

 upstream resinserver{ 
      server 10.0.0.10:7777; 
      server 10.0.0.11:8888; 
      hash $request_uri; 
      hash_method crc32; 
}

upstream还可以为每个设备设置状态值,这些状态值的含义分别如下:

down 表示单前的server暂时不参与负载.

weight 默认为1.weight越大,负载的权重就越大。

max_fails :允许请求失败的次数默认为1.当超过最大次数时,返回proxy_next_upstream 模块定义的错误.

fail_timeout : max_fails次失败后,暂停的时间。

backup: 其它所有的非backup机器down或者忙的时候,请求backup机器。所以这台机器压力会最轻。

upstream bakend{ #定义负载均衡设备的Ip及设备状态 
      ip_hash; 
      server 10.0.0.11:9090 down; 
      server 10.0.0.11:8080 weight=2; 
      server 10.0.0.11:6060; 
      server 10.0.0.11:7070 backup; 
}

nginx处理静态资源的配置

主要配置如下(在server配置中加入location配置即可):

#css|js|ico|gif|jpg|jpeg|png|txt|html|htm|xml|swf|wav这些都是静态文件,但应分辨,js、css可能经常会变,过期时间应小一些,图片、html基本不变,过期时间可以设长一些  
location ~* ^.+\.(ico|gif|jpg|jpeg|png|html|htm)$ {  
    root         /var/www/poseidon/root/static;  
    access_log   off;  
    expires      30d;  
}  
location ~* ^.+\.(css|js|txt|xml|swf|wav)$ {  
    root         /var/www/poseidon/root/static;  
    access_log   off;  
    expires      24h;  
}  
#注:location不包括?后面带的参数,所以以上正则可以匹配http://192.168.1.16/image/sxxx.jpg?a=xxx

打开gzip,压缩传输

gzip on;  
gzip_comp_level 7;  
gzip_min_length  1100; #需要压缩的最小长度  
gzip_buffers    4 8k;  
gzip_types      text/plain application/javascript text/css text/xml application/x-httpd-php; #指定需要压缩的文件类型  
output_buffers  1 32k;  
postpone_output  1460;

Linux nginx 配置 location 语法 正则表达式

location:不同资源请求的路径

语法:location [=|~|~*|^~] /uri/ { … }

= : 精确匹配
   比如 /bbs 
   不加 = ,则表示bbs下所有文件目录,包括子目录
   加 = ,仅表示bbs下的文件,不包括子目录
^~ : 禁止匹配正则表达式
~ : 正则表达式,模式匹配,区分字符大小写
~*:正则表达式,模式匹配,不区分字符大小写

location / {
RA : 匹配 / 下面所有文件,包括子目录
}

例子:

location = / {
# 只匹配 / 查询。
[ configuration A ]
}
location / {
# 匹配任何查询,因为所有请求都已 / 开头。但是正则表达式规则和长的块规则将被优先和查询匹配。
[ configuration B ]
}
location ^~ /images/ {
# 匹配任何已 /images/ 开头的任何查询并且停止搜索。任何正则表达式将不会被测试。
[ configuration C ]
}
location ~* \.(gif|jpg|jpeg)$ {
# 匹配任何已 gif、jpg 或 jpeg 结尾的请求。然而所有 /images/ 目录的请求将使用 Configuration C。
[ configuration D ]
}

root:

在server中:表示全局的,对所有有的location都有意义。

在location:局部的资源定义

location /a/ {
root /var/www/;
}
location / #就代表 location 里面的 root,若是location里的 root 未定义,就从 server 里面继承!

/a/1.html = /var/www/a/1.html


alias:

location ~ ^/images/(.*)${
alias /var/www/files/$1
}

URI: /images/1.txt ==>> /var/www/files/1.txt

location /i/ {
alias /var/www/
}

URI : /i/1.html ==>> /var/www/1.html


nginx-404与fastcgi_intercept_errors指令

  • 错误 静态文件不存在会返回404页面,但是php页面则返回空白页!!

  • 配置文档 error_page 404 /404.html;

解决

nginx.conf 配置文件 添加以下指令! fastcgi_intercept_errors on;

[root@slave nginx]# nginx -s reload

总结

fastcgi_intercept_errors

语法:fastcgi_intercept_errors on|off

默认值:fastcgi_intercept_errors off

使用字段:http, server, location

这个指令指定是否传递4xx和5xx错误信息到客户端,或者允许nginx使用error_page处理错误信息。

你必须明确的在error_page中指定处理方法使这个参数有效,正如Igor所说“如果没有适当的处理方法,nginx不会拦截一个错误,这个错误不会显示自己的默认页面,这里允许通过某些方法拦截错误。


网站有时候会出现页面不存在或者是其他的错误状态,用error_page,把403,404,503,504的错误跳转到错误的页面上,可以给用户更好的体验 配置

error_page 403 = http://www.lynn.cn/403.html; 
error_page 404 = http://www.lynn.cn/404.html; 
error_page 503 = http://www.lynn.cn/503.html; 
error_page 504 = http://www.lynn.cn/504.html;

error_page可以使用的范围有http/server/location/if in location 配置好之后/etc/init.d/nginxd reload 测试,普通的静态文件不存在,能够返回到错误页面,但是php页面不行 google之,是因为fastcgi没有把错误给nginx,参考

http://bbs.linuxtone.org/thread-6848-1-1.html

还需要设置

fastcgi_intercept_errors on;

设置好之后,重新加载配置文件,测试,ok


nginx.conf同目录下创建blockips.conf,加入需要阻止的ip(deny 192.168.1.3) 在nginx中

http
        {
。。。。。。。。         
                include blockips.conf;
        }

导入阻止ip 建立403 error页面

<html>
<head><title>Error 403 - IP Address Blocked</title></head>
<body>
Your IP Address is blocked. If you this an error, please contact webmaster with your IP at 拉瓦锡@qq.com
</body>
</html>

在nginx中加入

server
        {
                listen       80;
                server_name 192.168.1.3;

                error_page 403 /test403.html;
                location = /test403.html {
                  root /home/www/test;
                  charset utf-8;
              }

重启nginx: /usr/local/nginx/sbin/nginx -s reload

结果只出现nginx默认的403页面,自定义的403页面不显示

             error_page 403 /test403.html;
                location = /test403.html {
                  root /home/www/test;
                  charset utf-8;
              allow all;
              }

火狐下面可以显示自定义403页面,可IE下死活出不来! 经过一番排查,发现html页面有问题,需要加上:

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">

之后,IE下面就可以显示自定义403页面了