文章目录
一、NGINX简介
Nginx 是一个高性能的 Web 和反向代理服务器, 它具有有很多非常优越的特性:
- 作为 Web 服务器:相比 Apache,Nginx 使用更少的资源,支持更多的并发连接,体现更高的效率, 这点使 Nginx 尤其受到虚拟主机提供商的欢迎。能够支持高达 50,000 个并发连接数的响应
- 作为负载均衡服务器:Nginx 既可以在内部直接支持 Rails 和 PHP,也可以支持作为 HTTP代理服务器 对外进行服务。Nginx 用 C 编写, 不论是系统资源开销还是 CPU 使用效率都比 Perlbal 要好的多。
- 作为邮件代理服务器: Nginx 同时也是一个非常优秀的邮件代理服务器(最早开发这个产品的目的之一也是作为邮件代理服务器)
Nginx 安装非常的简单,配置文件 非常简洁(还能够支持perl语法),Bugs非常少,Nginx 启动特别容易,并且几乎可以做到7*24不间断运行,即使运行数个月也不需要重新启动。你还能够在 不间断服务的情况下进行软件版本的升级。占有内存少,并发能力强,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等.
安装源码
root@centos-03 ~]# cd /usr/local/src/
[root@centos-03 src]# wget http://nginx.org/download/nginx-1.14.0.tar.gz
[root@centos-03 src]# tar zxvf nginx-1.14.0.tar.gz
### 编译安装可以查看配置参数
[root@centos-03 nginx-1.14.0]# ./configure --help
配置文件结构
- 全局变量(user、worker_proccesses、error_log、pid)
user nobody;
定义运行nginx服务的用户,还可以加上组,如 user nobody nobody;
worker_processes 1;
定义nginx子进程数量,即提供服务的进程数量,该数值建议和服务cpu核数保持一致。
除了可以定义数字外,还可以定义为auto,表示让系统自动调整。
error_log logs/error.log;
定义错误日志的路径,可以是相对路径(相对prefix路径的),也可以是绝对路径。
该配置可以在此处定义,也可以定义到http、server、location里
error_log logs/error.log notice;
定义错误日志路径以及日志级别.
错误日志级别:常见的错误日志级别有[debug|info|notice|warn|error|crit|alert|emerg],级别越高记录的信息越少。
如果不定义默认是error
pid logs/nginx.pid;
定义nginx进程pid文件所在路径,可以是相对路径,也可以是绝对路径。
worker_rlimit_nofile 100000;
定义nginx最多打开文件数限制。如果没设置的话,这个值为操作系统(ulimit -n)的限制保持一致。
把这个值设高,nginx就不会有“too many open files”问题了。
- events(网络连接相关、worker_connections)
worker_connections 1024;
定义每个work_process同时开启的最大连接数,即允许最多只能有这么多连接。
accept_mutex on;
当某一个时刻只有一个网络连接请求服务器时,服务器上有多个睡眠的进程会被同时叫醒,这样会损耗一定的服务器性能。
Nginx中的accept_mutex设置为on,将会对多个Nginx进程(worker processer)接收连接时进行序列化,防止多个进程争抢资源。
默认就是on。
multi_accept on;
nginx worker processer可以做到同时接收多个新到达的网络连接,前提是把该参数设置为on。
默认为off,即每个worker process一次只能接收一个新到达的网络连接。
use epoll;
Nginx服务器提供了多个事件驱动器模型来处理网络消息。
其支持的类型有:select、poll、kqueue、epoll、rtsing、/dev/poll以及eventport。
* select:只能在Windows下使用,这个事件模型不建议在高负载的系统使用
* poll:Nginx默认首选,但不是在所有系统下都可用
* kqueue:这种方式在FreeBSD 4.1+, OpenBSD2.9+, NetBSD 2.0, 和 MacOS X系统中是最高效的
* epoll: 这种方式是在Linux 2.6+内核中最高效的方式
* rtsig:实时信号,可用在Linux 2.2.19的内核中,但不适用在高流量的系统中
* /dev/poll: Solaris 7 11/99+,HP/UX 11.22+, IRIX 6.5.15+, and Tru64 UNIX 5.1A+操作系统最高效的方式
* eventport: Solaris 10最高效的方式
- http
官方文档 http://nginx.org/en/docs/
参考链接: https://segmentfault.com/a/1190000012672431
参考链接: https://segmentfault.com/a/1190000002797601
参考链接:http的header https://kb.cnblogs.com/page/92320/
MIME-Type
include mime.types; //cat conf/mime.types
定义nginx能识别的网络资源媒体类型(如,文本、html、js、css、流媒体等)
default_type application/octet-stream;
定义默认的type,如果不定义改行,默认为text/plain.
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" $http_host'
'"$http_user_agent" "$http_x_forwarded_for"';
log_format elklog '{"timestamp":"$time_iso8601",'
'"host":"$server_addr",'
'"client":"$remote_addr",'
'"size":$body_bytes_sent,'
'"responsetime": $request_time,'
'"upstreamtime":"$upstream_response_time",'
'"domain":"$http_host",'
'"url":"$request_uri",'
'"referer": "$http_referer",'
'"agent": "$http_user_agent",'
'"status": "$status",'
'"body":"$request_body"}';
其中main为日志格式的名字,后面的为nginx的内部变量组成的一串字符串。
access_log logs/access.log main;
定义日志的路径以及采用的日志格式,该参数可以在server配置块中定义。
sendfile on;
是否调用sendfile函数传输文件,默认为off,使用sendfile函数传输,可以减少user mode和kernel mode的切换,从而提升服务器性能。
对于普通应用设为 on,如果用来进行下载等应用磁盘IO重负载应用,可设置为off,以平衡磁盘与网络I/O处理速度,降低系统的负载。
sendfile_max_chunk 128k;
该参数限定Nginx worker process每次调用sendfile()函数传输数据的最大值,默认值为0,如果设置为0则无限制。
tcp_nopush on;
当tcp_nopush设置为on时,会调用tcp_cork方法进行数据传输。
使用该方法会产生这样的效果:当应用程序产生数据时,内核不会立马封装包,而是当数据量积累到一定量时才会封装,然后传输。这样有助于解决网络堵塞问题。
默认值为on。举例:快递员收快递、发快递,包裹累积到一定量才会发,节省运输成本。
keepalive_timeout 65 60;
该参数有两个值,第一个值设置nginx服务器与客户端会话结束后仍旧保持连接的最长时间,单位是秒,默认为75s。
第二个值可以省略,它是针对客户端的浏览器来设置的,可以通过curl -I看到header信息中有一项Keep-Alive: timeout=60,如果不设置就没有这一项。
第二个数值设置后,浏览器就会根据这个数值决定何时主动关闭连接,Nginx服务器就不操心了。但有的浏览器并不认可该参数。
send_timeout
这个超时时间是发送响应的超时时间,即Nginx服务器向客户端发送了数据包,但客户端一直没有去接收这个数据包。
如果某个连接超过send_timeout定义的超时时间,那么Nginx将会关闭这个连接。
client_max_body_size 10m;
浏览器在发送含有较大HTTP包体的请求时,其头部会有一个Content-Length字段,
client_max_body_size是用来限制Content-Length所示值的大小的。
这个限制包体的配置不用等Nginx接收完所有的HTTP包体,就可以告诉用户请求过大不被接受。会返回413状态码。
例如,用户试图上传一个1GB的文件,Nginx在收完包头后,发现Content-Length超过client_max_body_size定义的值,
就直接发送413(Request Entity Too Large)响应给客户端。
gzip on;
是否开启gzip压缩。
gzip_min_length 1k;
设置允许压缩的页面最小字节数,页面字节数从header头得content-length中进行获取。默认值是20。建议设置成大于1k的字节数,小于1k可能会越压越大。
gzip_buffers 4 16k;
设置系统获取几个单位的buffer用于存储gzip的压缩结果数据流。4 16k代表分配4个16k的buffer。
gzip_http_version 1.1;
用于识别 http 协议的版本,早期的浏览器不支持 Gzip 压缩,用户会看到乱码,所以为了支持前期版本加上了这个选项。
如果你用了Nginx反向代理并期望也启用Gzip压缩的话,由于末端通信是http/1.1,故请设置为 1.1。
gzip_comp_level 6;
gzip压缩比,1压缩比最小处理速度最快,9压缩比最大但处理速度最慢(传输快但比较消耗cpu)
gzip_types mime-type ... ;
匹配mime类型进行压缩,无论是否指定,”text/html”类型总是会被压缩的。
在conf/mime.conf里查看对应的type。
示例:gzip_types text/plain application/x-javascript text/css text/html application/xml;
gzip_proxied any;
Nginx作为反向代理的时候启用,决定开启或者关闭后端服务器返回的结果是否压缩,匹配的前提是后端服务器必须要返回包含”Via”的 header头。
以下为可用的值:
off - 关闭所有的代理结果数据的压缩
expired - 启用压缩,如果header头中包含 "Expires" 头信息
no-cache - 启用压缩,如果header头中包含 "Cache-Control:no-cache" 头信息
no-store - 启用压缩,如果header头中包含 "Cache-Control:no-store" 头信息
private - 启用压缩,如果header头中包含 "Cache-Control:private" 头信息
no_last_modified - 启用压缩,如果header头中不包含 "Last-Modified" 头信息
no_etag - 启用压缩 ,如果header头中不包含 "ETag" 头信息
auth - 启用压缩 , 如果header头中包含 "Authorization" 头信息
any - 无条件启用压缩
gzip_vary on;
和http头有关系,会在响应头加个 Vary: Accept-Encoding ,可以让前端的缓存服务器缓存经过gzip压缩的页面,例如,用Squid缓存经过Nginx压缩的数据。
gzip on;
#该指令用于开启或关闭gzip模块(on/off)
gzip_buffers 16 8k;
#设置系统获取几个单位的缓存用于存储gzip的压缩结果数据流。16 8k代表以8k为单位,安装原始数据大小以8k为单位的16倍申请内存
gzip_comp_level 6;
#gzip压缩比,数值范围是1-9,1压缩比最小但处理速度最快,9压缩比最大但处理速度最慢
gzip_http_version 1.1;
#识别http的协议版本
gzip_min_length 256;
#设置允许压缩的页面最小字节数,页面字节数从header头得content-length中进行获取。默认值是0,不管页面多大都压缩。这里我设置了为256
gzip_proxied any;
#这里设置无论header头是怎么样,都是无条件启用压缩
gzip_vary on;
#在http header中添加Vary: Accept-Encoding ,给代理服务器用的
gzip_types
text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
text/javascript application/javascript application/x-javascript
text/x-json application/json application/x-web-app-manifest+json
text/css text/plain text/x-component
font/opentype font/ttf application/x-font-ttf application/vnd.ms-fontobject
image/x-icon;
#进行压缩的文件类型,这里特别添加了对字体的文件类型
gzip_disable "MSIE [1-6]\.(?!.*SV1)";
#禁用IE 6 gzip
- server(虚拟主机)
server{} 包含在http{}内部,每一个server{}都是一个虚拟主机(站点)。
以下为nginx.conf配置文件中server{}部分的内容。
server {
listen 80; //监听端口为80,可以自定义其他端口,也可以加上IP地址,如,listen 127.0.0.1:8080;
server_name localhost; //定义网站域名,可以写多个,用空格分隔。
#charset koi8-r; //定义网站的字符集,一般不设置,而是在网页代码中设置。
#access_log logs/host.access.log main; //定义访问日志,可以针对每一个server(即每一个站点)设置它们自己的访问日志。
##在server{}里有很多location配置段
location / {
root html; //定义网站根目录,目录可以是相对路径也可以是绝对路径。
index index.html index.htm; //定义站点的默认页。
}
#error_page 404 /404.html; //定义404页面
# redirect server error pages to the static page /50x.html
#
error_page 500 502 503 504 /50x.html; //当状态码为500、502、503、504时,则访问50x.html
location = /50x.html {
root html; //定义50x.html所在路径
}
# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
#定义访问php脚本时,将会执行本location{}部分指令
#location ~ \.php$ {
# proxy_pass http://127.0.0.1; //proxy_pass后面指定要访问的url链接,用proxy_pass实现代理。
#}
# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
#location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000; //定义FastCGI服务器监听端口与地址,支持两种形式,1 IP:Port, 2 unix:/path/to/sockt
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; //定义SCRIPT_FILENAME变量,后面的路径/scripts为上面的root指定的目录
# include fastcgi_params; //引用prefix/conf/fastcgi_params文件,该文件定义了fastcgi相关的变量
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht { //访问的url中,以/.ht开头的,如,www.example.com/.htaccess,会被拒绝,返回403状态码。
# deny all; //这里的all指的是所有的请求。
#}
}
# another virtual host using mix of IP-, name-, and port-based configuration
#
#server {
# listen 8000; //监听8000端口
# listen somename:8080; //指定ip:port
# server_name somename alias another.alias; //指定多个server_name
# location / {
# root html;
# index index.html index.htm;
# }
#}
# HTTPS server
#
#server {
# listen 443 ssl; //监听443端口,即ssl
# server_name localhost;
### 以下为ssl相关配置
# ssl_certificate cert.pem; //指定pem文件路径
# ssl_certificate_key cert.key; //指定key文件路径
# ssl_session_cache shared:SSL:1m; //指定session cache大小
# ssl_session_timeout 5m; //指定session超时时间
# ssl_protocols TLSv1 TLSv1.1 TLSv1.2; //指定ssl协议
# ssl_ciphers HIGH:!aNULL:!MD5; //指定ssl算法
# ssl_prefer_server_ciphers on; //优先采取服务器算法
# location / {
# root html;
# index index.html index.htm;
# }
#}
架构分析
Nginx涉及到的模块分为核心模块、标准HTTP模块、可选HTTP模块、邮件服务模块以及第三方模块等五大类。
核心模块
核心模块是指Nginx服务器正常运行时必不可少的模块,它们提供了Nginx最基本最核心的服务,如进程管理、权限控制、错误日志记录等。
主要包含对两类功能的支持,一类是主体功能,包括进程管理、权限控制、错误日志记录、配置解析等,
另一类是用于响应请求事件必需的功能,包括事件驱动机制、正则表达式解析等。
ngx_core_module
ngx_errlog_module
ngx_conf_module
ngx_regex_module
ngx_events_module
ngx_event_core_module
ngx_epoll_module
- 标准HTTP模块
标准HTTP模块是编译Nginx后包含的模块,其支持Nginx服务器的标准HTTP功能。
模块 | 功能 |
---|---|
ngx_http_core | 配置端口,URI分析,服务器响应错误处理,别名控制以及其他HTTP核心事务 |
ngx_http_access_module | 基于IP地址的访问控制(允许/拒绝) |
ngx_http_auth_basic_module | 基于HTTP的身份认证 |
ngx_http_autoindex_module | 处理以“/”结尾的请求并自动生成目录列表 |
ngx_http_browser_module | 解析HTTP请求头中的“User-Agent”域的值 |
ngx_http_charset_module | 指定网页编码 |
ngx_http_empty_gif_module | 从内存创建一个1 x 1的透明gif图片,可以快速调用 |
ngx_http_fastcgi_module | 对FastCGI的支持 |
ngx_http_geo_module | 将客户端的IP转化为键值对变量,该模块主要用来针对客户的的IP来定义变量 |
ngx_http_gzip_module | 压缩请求响应,可以减少数据传输 |
ngx_http_headers_filter_module | 设置HTTP响应头 |
ngx_http_index_module | 处理以“/”结尾的请求,如果没有找到该目录下的index页,就将请求转给ngx_http_autoindex_module模块处理 |
ngx_http_limit_req_module | 限制来自客户端的请求的响应和处理速率 |
ngx_http_limit_conn_module | 限制来自客户端的连接的响应和处理速率 |
ngx_http_log_module | 自定义access日志 |
ngx_http_map_module | 创建任意键值对变量 |
ngx_http_memcached_module | 对Memcached的支持 |
ngx_http_proxy_module | 支持代理事务 |
ngx_http_referer_module | 对HTTP头中的"referer"进行过滤处理,比如,实现防盗链功能 |
ngx_http_rewrite_module | 实现nginx的rewrite功能 |
ngx_http_scgi_module | 对SCGI的支持 |
ngx_http_upstream_module | 定义一组服务器,可以接收来自代理、Fastcgi、Memcached的中重定向,主要用于负载均衡 |
- 可选HTTP模块
可选HTTP模块主要用于扩展标准的HTTP功能,使其能够处理一些特殊的HTTP请求。在编译Nginx时,如果不指定这些模块,默认是不会安装的。
模块 | 功能 |
---|---|
ngx_http_addition_module | 在响应请求的页面开始或者结尾添加文本信息 |
ngx_http_degradation_module | 在低内存的情形下允许Nginx服务器返回444错误或204错误 |
ngx_http_perl_module | 在Nginx的配置文件中可以使用Perl脚本 |
ngx_http_flv_module | 支持将Flash多媒体信息按照流文件传输,可以根据客户端指定的开始位置返回Flash |
ngx_http_geoip_module | 支持解析基于GeoIP数据库的客户端请求 |
ngx_google_perflools_module | 支持Google Performance Tools的一套用于C++Profile的工具集 |
ngx_http_image_filter_module | 支持将H.264/AAC编码的多媒体信息(后缀名通常为mp4、m4v或m4a)按照流文件传输,常与ngx_http_flv_module模块一起使用 |
ngx_http_random_index_module | Nginx接收到以“/”结尾的请求时,在对应的目录下随机选择一个文件作为index文件 |
ngx_http_secure_link_module | 支持对请求链接的有效性检查 |
ngx_http_ssl_module | 对HTTPS/SSL支持 |
ngx_http_stub_status_module | 支持返回Nginx服务器的统计信息,一般包括处理连接的数量、连接成功的数量、处理的请求数、读取和返回的Header信息数等信息 |
ngx_http_sub_module | 使用指定的字符串替换响应信息中的信息 |
ngx_http_dav_module | 支持HTTP协议和WebDAV协议中PUT、DELETE、MKCOL、COPY和MOVE方法 |
ngx_http_xslt_module | 将XML响应信息使用XSLT(拓展样式表转换语言)进行转换 |
- 邮件服务模块
主要用于支持Ningx的邮件服务。
- 第三方模块
并非有Nginx官方提供,而是由第三方机构或者个人开发的模块,用于实现某种特殊功能。
echo-nginx-module 支持在Nginx配置文件中使用echo、sleep、time以及exec等类shell命令
lua-nginx-module 使Nginx支持lua脚本语言
WEB请求机制
- 同步机制
同步、异步发生在当客户端发起请求后,服务端处理客户端的请求时。
同步机制,是指客户端发送请求后,需要等待服务端(内核)返回信息后,再继续发送下一个请求。
在同步机制中,所有的请求在服务器端得到同步,即发送方和接收方对请求的处理步调是一致的。
- 异步机制
异步机制,是指客户端发出一个请求后,不等待服务端(内核)返回信息,就继续发送下一个请求。
在异步机制中,所有来自发送方的请求形成一个队列,接收方处理完后再通知发送方。
举例:一家酒店前台,在旺季的高峰时间段会接很多预定酒席的电话。
如果是同步机制情况下,前台每接一个电话后先不挂掉电话,而是去查询有无剩余酒席,查到结果后,告诉客户。
如果是异步机制情况下,前台每接一个预定电话直接回复客户,一会回复,此时前台把查询这件事情交给了另外的同事,
该前台挂掉电话后,继续处理其他客户的事情,当另外的同事查询到结果后再通知给前台,前台再通知客户。
- 阻塞
阻塞与非阻塞发生在IO调度中,比如内核到磁盘IO。
阻塞方式下,进程/线程在获取最终结果之前,被系统挂起了,也就是所谓的阻塞了,在阻塞过程中该进程什么都干不了,
直到最终结果反馈给它时,它才恢复运行状态。
- 非阻塞
非阻塞方式和阻塞相反,进程/线程在获取最终结果之前,并没有进入被挂起的状态,而是该进程可以继续执行新的任务。
当有最终结果反馈给该进程时,它再把结果交给客户端。
举例:依然是酒店前台接待预定酒席电话的案例。
此时角色不再是前台,而是她的查询有无剩余酒席的同事。如果是阻塞方式,该同事在查询有无剩余酒席的过程中,需要傻傻地
等待酒店管理系统给他返回结果,在此期间不能做其他事情。
如果是非阻塞,该同事在等待酒店管理系统给他返回结果这段时间,可以做其他事情,比如可以通知前台剩余酒席的情况。
- Nginx的请求机制
Nginx之所以可以支持高并发,是因为Nginx用的是异步非阻塞的机制,而Nginx是靠事件驱动模型来实现这种机制的。
在Nginx的事件驱动模型下,客户端发起的所有请求在服务端都会被标记为一个事件,Nginx会把这些事件收集到“事件收集器”里,
然后再把这些事件交给内核去处理。
-
线程是进程的一个子单元,线程比进程好的地方是可以节省更多的资源,假如我开一个进程耗费的资源是20兆,那我开10个线程也是占用20兆,这样我同样的内存可以开更多的线程出来,每个线程也是处理一个请求,线程也有弊端,需要和其他的线程共享内存,稳定性不是很好
-
同步:同步、异步发生在当客户端发起请求后,服务端处理客户端的请求时。 同步机制,是指客户端发送请求后,需要等待服务端(内核)返回信息后,再继续发送下一个请求。 在同步机制中,所有的请求在服务器端得到同步,即发送方和接收方对请求的处理步调是一致的。
-
异步:异步机制,是指客户端发出一个请求后,不等待服务端(内核)返回信息,就继续发送下一个请求。 在异步机制中,所有来自发送方的请求形成一个队列,接收方处理完后再通知发送方。
-
阻塞:阻塞与非阻塞发生在IO调度中,比如内核到磁盘IO。 阻塞方式下,进程/线程在获取最终结果之前,被系统挂起了,也就是所谓的阻塞了,在阻塞过程中该进程什么都干不了, 直到最终结果反馈给它时,它才恢复运行状态。
-
非阻塞:非阻塞方式和阻塞相反,进程/线程在获取最终结果之前,并没有进入被挂起的状态,而是该进程可以继续执行新的任务。 当有最终结果反馈给该进程时,它再把结果交给客户端。
事件驱动模型
- 事件驱动模型
事件驱动模型是实现异步非阻塞的一个手段。事件驱动模型中,一个进程(线程)就可以了。
对于web服务器来说,客户端A的请求连接到服务端时,服务端的某个进程(Nginx worker process)会处理该请求,
此进程在没有返回给客户端A结果时,它又去处理了客户端B的请求。
服务端把客户端A以及客户端B发来的请求作为事件交给了“事件收集器”,
而“事件收集器”再把收集到的事件交由“事件发送器”发送给“事件处理器”进行处理。
最后“事件处理器”处理完该事件后,通知服务端进程,服务端进程再把结果返回给客户端A、客户端B。
在这个过程中,服务端进程做的事情属于用户级别的,而事件处理这部分工作属于内核级别的。
也就是说这个事件驱动模型是需要操作系统内核来作为支撑的。
- Nginx的事件驱动模型
Nginx的事件驱动模型,支持select、poll、epoll、rtsig、kqueue、/dev/poll、eventport等。
最常用的是前三种,其中kqueue模型用于支持BSD系列平台的事件驱动模型。kqueue是poll模型的一个变种,本质上和epoll一样。
/dev/poll是Unix平台的事件驱动模型,其主要在Solaris7及以上版本、HP/UX11.22及以上版本、IRIX6.5.15及以上版本、
Tru64 Unix 5.1A及以上版本的平台使用。
eventport是用于支持Solaris10及以上版本的事件驱动模型。
- select模型
Linux和Windows都支持,使用select模型的步骤是:
1. 创建所关注事件的描述符集合,对于一个描述符,可以关注其上面的读(Read)事件、写(Write)事件以及异常发生(Exception)事件。
在select模型中,要创建这3类事件描述符集合。
2. 调用底层提供的select()函数,等待事件发生。
3. 轮询所有事件描述符集合中的每一个事件描述符,检查是否有相应的事件发生,如果有就进行处理。
- poll模型
poll模型是Linux平台上的事件驱动模型,在Linux2.1.23中引入的,Windows平台不支持该模型。
poll模型和select模型工作方式基本相同,区别在于,select模型创建了3个描述符集合,而poll模型只创建一个描述符集合。
- epoll模型
epoll模型属于poll模型的变种,在Linux2.5.44中引入。epoll比poll更加高效,原因在于它不需要轮询整个描述符集合,
而是Linux内核会关注事件集合,当有变动时,内核会发来通知。
Nginx架构
Nginx服务器使用 master/worker 多进程模式。
主进程(Master process)启动后,会接收和处理外部信号;
主进程启动后通过fork() 函数产生一个或多个子进程(work process),每个子进程会进行进程初始化、
模块调用以及对事件的接收和处理等工作。
- 主进程
主要功能是和外界通信和对内部其他进程进行管理,具体来说有以下几点:
-
读取Nginx配置文件并验证其有效性和正确性
-
建立、绑定和关闭socket
-
按照配置生成、管理工作进程
-
接收外界指令,比如重启、关闭、重载服务等指令
-
日志文件管理
- 子进程(worker process)
是由主进程生成,生成数量可以在配置文件中定义。该进程主要工作有:
-
接收客户端请求
-
将请求依次送入各个功能模块进行过滤处理
-
IO调用,获取响应数据
-
与后端服务器通信,接收后端服务器处理结果
-
数据缓存,访问缓存索引,查询和调用缓存数据
-
发送请求结果,响应客户端请求
-
接收主进程指令,如重启、重载、退出等
rewrite用法
rewrite配置-if
- if指令
格式:if (条件判断) { 具体的rewrite规则 }
- 条件举例
条件判断语句由Nginx内置变量、逻辑判断符号和目标字符串三部分组成。
其中,内置变量是Nginx固定的非自定义的变量,如,$request_method, $request_uri等。
逻辑判断符号,有=, !=, ~, ~*, !~, !~*
!表示相反的意思,~为匹配符号,它右侧为正则表达式,区分大小写,而~*为不区分大小写匹配。
目标字符串可以是正则表达式,通常不用加引号,但表达式中有特殊符号时,比如空格、花括号、分号等,需要用单引号引起来。
示例1
if ($request_method = POST) //当请求的方法为POST时,直接返回405状态码
{
return 405; //在该示例中并未用到rewrite规则,if中支持用return指令。
}
示例2
if ($http_user_agent ~ MSIE) //user_agent带有MSIE字符的请求,直接返回403状态码
{
return 403;
}
如果想同时限制多个user_agent,还可以写成这样
if ($http_user_agent ~ "MSIE|firefox|spider")
{
return 403;
}
示例3
if(!-f $request_filename) //当请求的文件不存在,将会执行下面的rewrite规则
{
rewrite 语句;
}
示例4
if($request_uri ~* 'gid=\d{9,12}/') //\d表示数字,{9,12}表示数字出现的次数是9到12次,如gid=123456789/就是符合条件的。
{
rewrite 语句;
}
示例5
if ($request_filename ~* ^.*?.(txt|doc|pdf|rar|gz|zip|docx|exe|xlsx|ppt|pptx|jpg|png)$){
add_header Content-Disposition attachment;
}
即对所有的访问,响应头中增加Content-Disposition。
chrome的表现:直接下载login.jpg。
ie9表现:提示“您是要打开还是保存来自file.test.com的login.jpg”
为文件换个名字
有时你希望用户侧使用另外的名字保存文件,这时只要增加filename字段即可。
Content-Disposition:attachment;filename=NewnName
location /50018/ {
alias /AECDN/appcdn/50018/;
add_header Content-MD5 $file_md5; //给HTTP头部增加文件md5标识;
expires 30d;
add_header Content-Disposition “attachment;filename=xfz.apk”; //重定向文件名为指定名称;
}
}
rewrite中的break和last
两个指令用法相同,但含义不同,需要放到rewrite规则的末尾,用来控制重写后的链接是否继续被nginx配置执行(主要是rewrite、return指令)。
示例1(连续两条rewrite规则):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html ;
rewrite /2.html /3.html ;
}
当我们请求1.html时,最终访问到的是3.html,两条rewrite规则先后执行。
break和last在location {}外部
格式:rewrite xxxxx break;
示例2(增加break):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
当我们请求1.html时,最终访问到的是2.html
说明break在此示例中,作用是不再执行break以下的rewrite规则。
但,当配置文件中有location时,它还会去执行location{}段的配置(请求要匹配该location)。
示例3(break后面还有location段):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
location /2.html {
return 403;
}
}
当请求1.html时,最终会返回403状态码,说明它去匹配了break后面的location{}配置。
以上2个示例中,可以把break替换为last,它们两者起到的效果一模一样。
当break和last在location{}里面
示例4(什么都不加):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
当请求/1.html,最终将会访问/b.html,连续执行location /下的两次rewrite,跳转到了/3.html,然后又匹配location /3.html
示例5(增加break):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html break;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
当请求/1.html,最终会访问/2.html
在location{}内部,遇到break,本location{}内以及后面的所有location{}内的所有指令都不再执行。
示例6(增加last):
server{
listen 80;
server_name test.com;
root /tmp/123.com;
location / {
rewrite /1.html /2.html last;
rewrite /2.html /3.html;
}
location /2.html
{
rewrite /2.html /a.html;
}
location /3.html
{
rewrite /3.html /b.html;
}
}
当请求/1.html,最终会访问/a.html
在location{}内部,遇到last,本location{}内后续指令不再执行,而重写后的url再次从头开始,从头到尾匹配一遍规则。
结论
- 当rewrite规则在location{}外,break和last作用一样,遇到break或last后,其后续的rewrite/return语句不再执行。但后续有location{}的话,还会近一步执行location{}里面的语句,当然前提是请求必须要匹配该location。
- 当rewrite规则在location{}里,遇到break后,本location{}与其他location{}的所有rewrite/return规则都不再执行。
- 当rewrite规则在location{}里,遇到last后,本location{}里后续rewrite/return规则不执行,但重写后的url再次从头开始执行所有规则,哪个匹配执行哪个。
nginx中的return用法
1.直接返回状态码·
[root@centos-03 conf]# cat vhost/default.conf
server {
listen 80 default_server;
return 403;
}
[root@centos-03 conf]# curl -x127.0.0.1:80 fjldsfjdsajfl
<html>
<head><title>403 Forbidden</title></head>
<body bgcolor="white">
<center><h1>403 Forbidden</h1></center>
<hr><center>nginx/1.14.0</center>
</body>
</html>
curl -x127.0.0.1:80 fjldsfjdsajfl -I
HTTP/1.1 403 Forbidden
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 12:42:18 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
2.在if中使用return直接返回404
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 404;
rewrite /(.*) /aaa.txt; #该行配置不会被执行。
}
}
curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 404 Not Found
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 12:50:11 GMT
Content-Type: text/html
Content-Length: 169
Connection: keep-alive
3.返回字符串
[root@centos-03 conf]# vim vhost/1.conf ^C
[root@centos-03 conf]# cat vhost/1.conf
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 200 "ok";
rewrite /(.*) /aaa.txt; #该行配置不会被执行。
}
}
curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 12:53:53 GMT
Content-Type: application/octet-stream
Content-Length: 2
Connection: keep-alive
curl -x127.0.0.1:80 www.1.com/12/.htpasswd
ok
4.返回url
[root@centos-03 conf]# vim vhost/1.conf ^C
[root@centos-03 conf]# cat vhost/1.conf
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 200 "<html><script>window.location.href='//$host$request_uri';</script></html>";
rewrite /(.*) /aaa.txt; #该行配置不会被执行。
}
}
[root@centos-03 conf]# curl -x127.0.0.1:80 www.1.com/12/.htpasswd
<html><script>window.location.href='//www.1.com/12/.htpasswd';</script></html>[root@centos-03 conf]# curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 200 OK
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 13:07:28 GMT
Content-Type: application/octet-stream
Content-Length: 78
Connection: keep-alive
5.301跳转
server {
listen 80;
server_name www.1.com;
index index.html;
root /data/wwwroot/www.1.com;
rewrite_log on;
if ($request_uri ~ "\.htpasswd|\.bak")
{
return 301 http://www.baidu.com;
rewrite /(.*) /aaa.txt; #该行配置不会被执行。
}
}
curl -x127.0.0.1:80 www.1.com/12/.htpasswd -I
HTTP/1.1 301 Moved Permanently
Server: nginx/1.14.0
Date: Thu, 26 Jul 2018 13:12:38 GMT
Content-Type: text/html
Content-Length: 185
Connection: keep-alive
Location: http://www.baidu.com
rewrite规则语法
rewrite规则
格式:rewrite regex replacement [flag]
* rewrite配置可以在server、location以及if配置段内生效
* regex是用于匹配URI的正则表达式,其不会匹配到$host(域名)
* replacement是目标跳转的URI,可以以http://或者https://开头,也可以省略掉$host,直接写$request_uri部分(即请求的链接)
* flag,用来设置rewrite对URI的处理行为,其中有break、last、rediect、permanent,其中break和last在前面已经介绍过,
rediect和permanent的区别在于,前者为临时重定向(302),而后者是永久重定向(301),对于用户通过浏览器访问,这两者的效果是一致的。
但是,对于搜索引擎蜘蛛爬虫来说就有区别了,使用301更有利于SEO。所以,建议replacemnet是以http://或者https://开头的flag使用permanent。
示例1
location / {
rewrite /(.*) http://www.test.com/$1 permanent;
}
说明:.*为正则表达式,用()括起来,在后面的URI中可以调用它,第一次出现的()用$1调用,第二次出现的()用$2调用,以此类推。
示例2
location / {
rewrite /.* http://www.test.com$request_uri permanent;
}
说明:在replacement中,支持变量,这里的$request_uri就是客户端请求的链接
示例3
server{
listen 80;
server_name www.123.com;
root /tmp/123.com;
index index.html;
rewrite /(.*) /abc/$1 redirect;
}
说明:本例中的rewrite规则有问题,会造连续循环,最终会失败,解决该问题有两个方案。
关于循环次数,经测试发现,curl 会循环50次,chrome会循环80次,IE会循环120次,firefox会循环20次。
示例4
server{
listen 80;
server_name www.123.com;
root /tmp/123.com;
index index.html;
rewrite /(.*) /abc/$1 break;
}
说明:在rewrite中使用break,会避免循环。
示例5
server{
listen 80;
server_name www.123.com;
root /tmp/123.com;
index index.html;
if ($request_uri !~ '^/abc/')
{
rewrite /(.*) /abc/$1 redirect;
}
}
说明:加一个条件限制,也可以避免产生循环
rewrite实例
域名跳转(域名重定向)
示例1(不带条件的):
server{
listen 80;
server_name www.testlinux.com;
rewrite /(.*) http://www.test.com/$1 permanent;
.......
}
示例2(带条件的):
server{
listen 80;
server_name www.testlinux.com testlinux.com;
if ($host != 'www.testlinux.com')
{
rewrite /(.*) http://www.testlinux.com/$1 permanent;
}
.......
}
示例3(http跳转到https):
server{
listen 80;
server_name www.testlinux.com;
rewrite /(.*) https://www.testlinux.com/$1 permanent;
.......
}
示例4(域名访问二级目录)
server{
listen 80;
server_name bbs.testlinux.com;
rewrite /(.*) http://www.testlinux.com/bbs/$1 last;
.......
}
示例5(静态请求分离)
server{
listen 80;
server_name www.testlinux.com;
location ~* ^.+.(jpg|jpeg|gif|css|png|js)$
{
rewrite /(.*) http://img.testlinux.com/$1 permanent;
}
.......
}
或者:
server{
listen 80;
server_name www.testlinux.com;
if ( $uri ~* 'jpg|jpeg|gif|css|png|js$')
{
rewrite /(.*) http://img.testlinux.com/$1 permanent;
}
.......
}
示例6防盗链
server{
listen 80;
server_name www.testlinux.com;
location ~* ^.+.(jpg|jpeg|gif|css|png|js|rar|zip|flv)$
{
valid_referers none blocked server_names *.testlinux.com testlinux.com *.test.com test.com;
if ($invalid_referer)
{
rewrite /(.*) http://img.testlinux.com/images/forbidden.png;
}
}
.......
}
说明:*这里是通配,跟正则里面的*不是一个意思,none指的是referer不存在的情况(curl -e 测试),
blocked指的是referer头部的值被防火墙或者代理服务器删除或者伪装的情况,
该情况下,referer头部的值不以http://或者https://开头(curl -e 后面跟的referer不以http://或者https://开头)。
或者:
location ~* ^.+.(jpg|jpeg|gif|css|png|js|rar|zip|flv)$
{
valid_referers none blocked server_names *.testlinux.com *.test.com testlinux.com test.com;
if ($invalid_referer)
{
return 403;
}
}
rewrite多个条件的并且
示例7:
location /{
set $rule 0;
if ($document_uri !~ '^/abc')
{
set $rule "${rule}1";
}
if ($http_user_agent ~* 'ie6|firefox')
{
set $rule "${rule}2";
}
if ($rule = "012")
{
rewrite /(.*) /abc/$1 redirect;
}
}
指定安卓设备匹配URL跳转
set $flag 0;
if ( $host = "appcdn.haowanyou.com" ){
set $flag "1";
}
if ( $request_uri = "/60018/1.0.0/xxx.apk" ){
set $flag "${flag}1";
}
if ( $http_user_agent ~* "mobile" ){
set $flag "${flag}1";
}
if ( $http_user_agent ~* "android" ){
set $flag "${flag}1";
}
if ( $flag = "1111" ){
rewrite ^/(.*)$ https://appcdn.haowanyou.com/1.0.1/default.apk redirect;
}