Nginx总结

1. 了解I/O模型

x86类型的CPU把指令分成了四类:环0-环3,最内层为核心指令即内核指令,其他指令在环3上,历史原因环1,2没有使用。

分类:阻塞型、非阻塞型、复用型、信号驱动型、异步。

同步/异步:

  关注消息通知机制:

    消息通知:
      同步:等待对方返回消息;

      异步:被调用者通过状态、通知或回调机制通知调用者有关被调用者的运行状态。

阻塞/非阻塞:

  关注调用者在等待结果返回之前所处的状态:

    阻塞:blocking,调用结果返回之前,调用者被挂起;

    非阻塞:nonblocking,调用结果返回之前,调用者不被挂起。

一次文件I/O请求,都会由两阶段组成:

  第一步:等待数据,即数据从磁盘到内核内存;

  第二步:复制数据,即数据从内核内存到进程内存。

已上的I/O分类就是给予上述两步来划分的:

  阻塞型:已上两步均被阻塞;

  非阻塞型:第一步是非阻塞的,第二步是阻塞的;

  复用型:一个进程能监视多路I/O,此时需要一个特殊的内核函数(I/O复用器)去监视iI/O;

  信号驱动型:属于异步I/O,第一步不再阻塞,调用后不再等待结果,继续执行其他任务;

  异步:属于异步I/O,无论第一步和第二步均不参与,由内核去完成。

复用型I/O调用:

  Linux:

    select:1024(一个进程最多监视1024个I/O,多次测试的结果,是由BSD发明)

    poll():没有限制,是由UNIX

信号驱动型(event-driven):

  epoll(Linux):由 libevent包来实现

  Kqueue(BSD):

  /dev/poll(Solaris)

 

 

2 . HTTPD MPM(多处理模块)

多处理模块(MPM),用来绑定到网络端口上,接受请求, 以及调度子进程处理请求。在更高伸缩性的站点可以选择使用线程的 MPM,即 worker 或 event; 需要可靠性或者与旧软件兼容的站点可以使用 prefork

prefork模型:进程模型,二级结构,主进程master负责生成子进程,每个子进程负责响应一个请求;

worker模型:线程模型,三级结构,主进程master负责生成子进程,每个子进程负责生成多个线程,每个线程响应一个请求;

event模型:二级结构,主进程master负责生成子进程,每个子进程响应多个请求(类似线程模型)。

 

3. Nginx程序架构

 

nginx有三种功能:web服务(静态的web资源服务器),web反向代理,邮件反向代理

3.1 master/worker

  一个master进程:

    负责加载和分配配置文件、管理worker进程、平滑升级

  一个或者多个worker进程:

    处理并响应用户请求

  缓存相关的进程:

    cache loader: 载入缓存对象

    cache manager: 管理缓存对象

特性:异步,事件驱动和非阻塞

  并发请求处理:通过kevent/epoll/select,/dev/poll实现,此处需要注意epoll和select,优先使用epoll;

  文件I/O: 高级I/O、sendfile、异步、mmap

3.2 Nginx模块

Nginx高度模块化机制,但其模块早期不支持DSO机制,近期支持模块动态装载和卸载。

模块分类:核心模块和标准模块、第三方模块。

3.2.1 核心模块(core module)

3.2.2 标准模块

HTTP modules:
  standard HTTP modules:
  Optional HTTP modules(可选的http模块) :

Mail modules
Stream modules(流模块):实现传输层代理(属于四层代理)

3.2.2 第三方模块

 

4. Nginx配置文件

配置指令:

4.1 性能优化相关的配置

worker_processes number | auto

通常对应cpu核心数;

worker_cpu_affinity auto | cpumask

将进程绑定在cpu上;

worker_priority number | cpumask

指定worker进程的nice值,设定worker进程的优先级[-20,20];

worker_rlimit_nofile_number

worker进程所能够打开的文件数量上限;

 

sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率很高,被称之为零拷贝。但是只适用于静态资源服务器,对于反向代理则无用。

原理解释
read/write
在传统的文件传输方式(read、write/send方式),具体流程细节如下:

调用read函数,文件数据拷贝到内核缓冲区
read函数返回,数据从内核缓冲区拷贝到用户缓冲区
调用write/send函数,将数据从用户缓冲区拷贝到内核socket缓冲区
数据从内核socket缓冲区拷贝到协议引擎中

在这个过程当中,文件数据实际上是经过了四次拷贝操作:
硬盘—>内核缓冲区—>用户缓冲区—>内核socket缓冲区—>协议引擎
sendfile
sendfile系统调用则提供了一种减少拷贝次数,提升文件传输性能的方法。

sendfile系统调用利用DMA引擎将文件数据拷贝到内核缓冲区,之后数据被拷贝到内核socket缓冲区中
DMA引擎将数据从内核socket缓冲区拷贝到协议引擎中

这里没有用户态和内核态之间的切换,也没有内核缓冲区和用户缓冲区之间的拷贝,大大提升了传输性能。
这个过程数据经历的拷贝操作如下:
硬盘—>内核缓冲区—>内核socket缓冲区—>协议引擎
带有DMA收集拷贝功能的sendfile
对于带有DMA收集拷贝功能的sendfile系统调用,还可以再减少一次内核缓冲区之间的拷贝。具体流程如下:

sendfile系统调用利用DMA引擎将文件数据拷贝到内核缓冲区,之后,将带有文件位置和长度信息的缓冲区描述符添加到内核socket缓冲区中
DMA引擎会将数据直接从内核缓冲区拷贝到协议引擎中

这个过程数据经历的拷贝操作如下:
硬盘—>内核缓冲区—>协议引擎
sendfile原理解释

4.2 定义客户端请求的相关配置

keepalive_timeout

keepalive_timeout   timeout  [header_timeout];

设定保持连接超时时长,0表示禁止长连接,默认为75s,可以调整短点。

keepalive_requests

keepalive_requests  number;

在一次长连接上所允许请求的资源的最大数量,默认为100

keepalive_disable

keepalive_disable   none | browser ...

对哪种浏览器禁用长连接

send_timeout

send_timeout   time;

向客户端发送响应报文的超时时长,此处是指两次写操作之间的间隔时长,而非整个响应过程的传输时长 

client_body_buffer_size 

client_body_buffer_size  size;

用于接收每个客户端请求报文的body部分的缓冲区大小;默认为16k;超出此大小时,其将被暂存到磁盘上的由client_body_temp_path指令所定义的位置。注意,这里是接收,不是下发数据,上传文件到该网站,才看得到效果

client_body_temp_path 

client_body_temp_path  path [level1 [level2 [level3]]];

设定用于存储客户端请求报文的body部分的临时存储路径及子目录结构和数量

这里指定的路径下存放文件,目录名为16进制的数字,是经过hash运算后,利用hash的值的最后几位数,依次分层,如三层目录进行存放文件

例子

  client_body_temp_path   /var/tmp/client_body  1 2 2

  1 1级目录占1位16进制,即2^4=16个目录0-f

  2 2级目录占2位16进制,即2^8=256个目录00-ff

  2 3级目录占2位16进制,即2^8=256个目录00-ff

4.3 对客户端进行限制的相关配置

.19、limit_rate

limit_rate   rate;

限制响应给客户端的传输速率,单位是bytes/second,默认值0表示无限制

.20、limit_except  

limit_except  method ... { ... },仅用于location

限制客户端使用除了指定的请求方法之外的其它方法

method:GET, HEAD, POST, PUT, DELETE,MKCOL, COPY, MOVE,OPTIONS, PROPFIND,PROPPATCH, LOCK, UNLOCK, PATCH

location {
limit_except   GET {
allow 192.168.1.0/24;
deny all;
} 
}

除了GET和HEAD 之外其它方法仅允许192.168.1.0/24网段主机使用

4.4 文件操作优化的配置

aio 

对于大文件采用aio,节省cpu,而对于小文件,采用sendfile,减少拷贝;并且对于大文件aio采用directio,避免挤占文件系统缓存,让文件系统缓存更多的小文件。

aio   on | off | threads[=pool];是否启用aio功能,默认是on

directio 

directio  size | off;一般是异步

是否同步(直接)写磁盘,而非写缓存,在Linux主机启用O_DIRECT标记,则文件大于等于给定大小时使用,例如directio  4m

open_file_cache

open_file_cache  off;

open_file_cache  max=N  [inactive=time];

nginx可以缓存以下三种信息:缓存的是元数据,不是数据本身

(1) 文件元数据:文件的描述符、文件大小和最近一次的修改时间

(2) 打开的目录结构

(3) 没有找到的或者没有权限访问的文件的相关信息

max=N:可缓存的缓存项上限;达到上限后会使用LRU算法实现管理

inactive=time:缓存项的非活动时长,在此处指定的时长内未被命中的或命中的次数少于open_file_cache_min_uses指令所指定的次数的缓存项即为非活动项,将被删除

inactive=time 为10分钟;open_file_cache_min_uses为3,表示在10分钟内访问低于3次表示非活动的,该文件就不会被缓存,会被清空

open_file_cache_errors 

open_file_cache_errors on | off;

是否缓存查找时发生错误的文件一类的信息,默认值为off

open_file_cache_min_uses

open_file_cache_min_uses  number;

open_file_cache指令的inactive参数指定的时长内,至少被命中此处指定的次数方可被归类为活动项,默认值为1

open_file_cache_valid

open_file_cache_valid  time;

缓存项有效性的检查频率,默认值为60s

4.5 Nginx 之 实现https

要利用nginx软件实现https的页面,用到ngx_http_ssl_module模块

1、ssl

ssl  on | off;

为指定虚拟机启用HTTPS  protocol,建议用listen指令代替

2、ssl_certificate

ssl_certificate  file;

当前虚拟主机使用PEM格式的证书文件

创建自签名的证书文件

cd /etc/pki/tls/certs/
make nginx6.crt

将生成的私钥文件解密

openssl rsa -in nginx6.key-out nginx66.key

将这两个文件复制到配置文件里指定的路径即可

在客户的上查看生成的证书信息,命令如下

openssl s_client -connect  www.e.com:443

3、ssl_certificate_key

ssl_certificate_key  file;

当前虚拟主机上与其证书匹配的私钥文件

4、ssl_protocols

ssl_protocols [SSLv2] [SSLv3] [TLSv1] [TLSv1.1] [TLSv1.2];

 支持ssl协议版本,默认为后三个,主流版本是[TLSv1.2]

5、ssl_session_cache 

ssl_session_cache off | none | [builtin[:size]] [shared:name:size];

builtin[:size]:使用OpenSSL内建缓存,为每worker进程私有,开启多大的空间来作为缓存空间

[shared:name:size]:在各worker之间使用一个共享的缓存,这样会提高缓存的命中率,提高性能。

6、ssl_session_timeout

ssl_session_timeout  time;

客户端连接可以复用sslsession cache中缓存的ssl参数的有效时长,默认5m

示例:

vim  /etc/nginx/conf.d/https.conf
server{
    listen 443 ssl;
    server_name www.e.com;
    root /app/website5;
    ssl_certificate /etc/nginx/ssl/nginx5.crt;
    ssl_certificate_key/etc/nginx/ssl/nginx5.key;
    ssl_session_cache shared:sslcache:20m;
    ssl_session_timeout 10m;
}
server{
    listen 443 ssl;
    server_name www.f.com;
    root /app/website6;
    ssl_certificate /etc/nginx/ssl/nginx6.crt;
    ssl_certificate_key/etc/nginx/ssl/nginx6.key;
    ssl_session_cache shared:sslcache:20m;
    ssl_session_timeout 10m;
}
示例配置

4.6 ngx_http_rewrite_module模块:

将用户请求的URI基于PCRE regex所描述的模式进行检查,而后完成重定向替换

当旧的业务和新的业务不一样,网站更名了,如网站名原来是www.dianping.com/zz后面改名为www.dianping.com/zhengzhou 

1、rewrite 

rewrite regex  replacement  [flag]

将用户请求的URI基于regex所描述的模式进行检查,匹配到时将其替换为replacement指定的新的URI

注意:如果在同一级配置块中存在多个rewrite规则,那么会自上而下逐个检查;被某条件规则替换完成后,会重新一轮的替换检查

 隐含有循环机制,但不超过10次;如果超过,提示500响应码,[flag]所表示的标志位用于控制此循环机制

如果replacement是以http://或https://开头,则替换结果会直接以重向返回给客户端

.[flag]:有以下几个关键字

last:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后对新的URI启动新一轮重写检查;提前重启新一轮循环

break:重写完成后停止对当前URI在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环,建议在location中使用

last 和break是在服务器内部操作的,客户端不知道,客户端访问的url不会发生变化。但是服务器端会返回替换过的新的内容。

redirect和permanent是服务器端给客户端发一个301或者302的请求,客户端需要重新发起请求,因此最终客户端看到的浏览器是url和原始的url是不一样的,url会被转换,如将fj改成成fujian,那么当用户访问的是http://172.18.50.73/fj/ 最终返回结果的时候,浏览器上的url会变更改为http://172.18.50.73/fujian/

redirect:临时重定向,重写完成后以临时重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求;不能以http://或https://开头,使用相对路径,状态码:302

permanent:重写完成后以永久重定向方式直接返回重写后生成的新URI给客户端,由客户端重新发起请求,状态码:301

例子

以下是正则表达式,$1表示前面的(.*).写在server段或者location段里,以下四个选项只需四选一。

    rewrite ^/fj/(.*)$  /fujian/$1 last | break |redirect |permanent;
 

测试

curl  -L http://172.18.50.73/fj/

 或者直接在浏览器里输入http://172.18.50.73/fj/查看结果

2、return

return 返回一个具体的地址

return code [text];

return  code URL;

停止处理并将指定的代码返回给客户端。非标准代码444关闭连接而不发送响应头。

从版本0.8.42开始,可以指定重定向URL(代码301,302,303,307和308)或响应正文文本(对于其他代码)。响应正文文本和重定向网址可以包含变量。作为特殊情况,重定向URL可以指定为此服务器本地的URI,在这种情况下,根据请求方案($ scheme)和server_name_in_redirect和port_in_redirect指令形成完整的重定向URL。

此外,可以将用于代码302的临时重定向的URL指定为唯一的参数。这样的参数应以“http://”,“https://”或“$ scheme”字符串开头。 URL可以包含变量。

测试,能正常返回text,

return URL;

停止处理,并返回给客户端指定的响应码。但是实际测试都是多次循环重定向,导致url不能打开,用curl命令测试,出现了报错:curl: (47) Maximum(50) redirects followed导致实验不成功

3、rewrite_log

rewrite_log  on | off;

是否开启重写日志, 发送至error_log(notice level)

4、set 

set  $variable value;

用户自定义变量,注意:变量定义和调用都要以$开头 

5、if

if  (condition) { ... }

引入新的上下文,条件满足时,执行配置块中的配置指令;server,location

condition:比较操作符:

== 相同

!= 不同

~:模式匹配,区分字符大小写

~*:模式匹配,不区分字符大小写

!~:模式不匹配,区分字符大小写

!~*:模式不匹配,不区分字符大小写

文件及目录存在性判断:

-e, !-e  存在(包括文件,目录,软链接)

-f, !-f  文件

-d, !-d 目录

-x, !-x 执行

4.7 ngx_http_referer_module模块

 referer是指从哪儿跳转,该字段可以用来防止盗链。

比如网页内有代码<a href=http://172.18.50.73/m.txt>test referer </a>那么再网页上test referer就会有链接到资源http://172.18.50.73/m.txt上,点击该链接,log就会有对应的referer在code之后,如这里是http://172.18.50.73/a.html,如果没有referer,则code后的字段显示“-”

.1、valid_referers  none|blocked|server_names|string...;

定义referer首部的合法可用值,不能匹配的将是非法值

none:请求报文首部没有referer首部

blocked:请求报文有referer首部,但无有效值

server_names:参数,其可以有值作为主机名或主机名模式

arbitrary_string:任意字符串,但可使用*作通配符

regular expression:被指定的正则表达式模式匹配到的字符串,要使用~开头,例如:~.*\.magedu\.com

.示例:

valid_referers  none blocked  server_names  *.sunny.com   ~\.sunny\.~\.baidu\.;

 #定义有效的referers

if ($invalid_referer) {
return 403;
}

 #定义如果出现无效的referer将返回code 403

posted @ 2019-06-24 01:26  324小熊  阅读(571)  评论(0编辑  收藏  举报