一、Nginx安装部署
docker安装
docker的nginx镜像: https://hub.docker.com/_/nginx
1 | # docker安装nginx,本地新建nginx/conf/nginx.conf文件,文件参考下方第3小结-基础配置 |
mac安装
1 | $ brew install nginx |
linux编译安装
1 | # rewrite模块,http模块的正则解析,需要pcre库 |
添加模块并重新编译
所属 | 模块名 | 作用 | 安装命令 |
---|---|---|---|
官方 | http_stub_status_module | nginx状态 | –with-http_stub_status_module |
官方 | http_ssl_module | ssl | –with-http_ssl_module |
官方 | http_realip_module | 真实ip | –with-http_realip_module |
三方 | nginx-rtmp-module | rtmp流媒体 | –add-module=/usr/local/src/nginx-rtmp-module/ |
1 | # 使用参数重新配置,更改安装位置,增加3个官方模块和一个三方模块 |
服务启停控制
1 | $ ./nginx -h #查看命令帮助 |
平滑升级nginx版本
nginx支持的信号(不要使用kill -9)
信号 | 作用 | 是否支持worker进程 |
---|---|---|
TERM(15),INT(2) | 快速停止 | 支持 |
QUIT(3) | 平缓停止 | 支持 |
HUP(1) | 重载配置平滑重启,开新的worker进程,平滑关旧worker进程 | 不支持 |
USER1(10) | 重新打日志文件 | 支持 |
USER2(12) | 重新打开nginx二进制文件 | 不支持 |
WINCH(28) | 平滑停止工作进程 | 支持 |
1 | $ kill -TERM|INT|QUIT|HUP `cat /opt/nginx/logs/nginx.pid` #也可通过kill命令,kill SIGNAL PID. |
nginx平滑升级过程
1 | # 调整参数重新编译,并替换sbin/nginx的二进制文件。最好备份下之前的nginx二进制文件。 |
二、Nginx架构
简介
- Nginx(engine x)是一个高性能的Web服务器和反向代理服务器,也是一个IMAP/POP3/SMTP服务器,由俄罗斯程序员Igor Sysoev于2002年开发。『Nginx官网』。
- Nginx常见的社区分支有:官方版本,Tengine(淘宝团队开发),Openresty(章宜春开发)。类似的服务器还有Caddy。
- Nginx源码是大约11万行的C代码,目录结构:Core-主干和基础设置,Event-事件驱动模型和不同的 IO 复用模块,HTTP-HTTP 服务器和模块,Mail-邮件代理服务器和模块,OS-操作系统相关的实现,Misc-杂项。
- Nginx的应用场景有,静态文件服务器、反向代理(区别于正向代理,如VPN或内网上网代理)、动静分离(静态资源和动态资源)、负载均衡(集群压力分摊)、安全防御、智能路由、流媒体服务等。
特点
Nginx特点有,高性能、高可靠性、高度模块化、高可扩展性、低内存消耗、热部署
- 高性能。它采用事件驱动,不使用传统的进程或线程服务器模型,能够非阻塞地处理10K乃至100K的海量连接。
- 高可靠性。内存池避免了常见的资源泄漏,独特的进程池机制则实现了自我监控和管理、快速恢复。
- 高度模块化。高内聚、低耦合,设计模式理论中的迪米特原则,支持分布式、支持应用拓展和升级。分为核心模块(ngx_core、ngx_conf、ngx_events、ngx_epoll、ngx_regex等),标准http模块(ngx_http_core、ngx_http_charset)、可选http模块(ngx_http_zip、ngx_http_ssl)、邮件服务模块和第三方模块等五大类。
- 高可扩展性。支持第三方模块。
web请求处理机制,使用多进程和异步非阻塞方式。
- 1、多进程方式,服务器主进程生成子进程来交互,相对独立,但资源消耗大,导致性能下降,如早期的Apache服务器。
- 2、多线程方式,服务器主进程派生一个线程来交互,线程的开销远小于进程,减轻web服务器对系统资源的消耗。但多线程位于同一进程,可以访问同样的内存空间,相互影响,需要自己对内存进行管理,引起死锁同步等问题,可能还需定期重启服务器。
- 3、异步方式,网络通信中的同步和异步是描述通信模式的概念,发送方发送请求后需等待收到接收方响应后,才发送下一个请求。异步中所有请求形成一个队列,接收方处理完成后通知发送方。
有一个主进程和多个工作进程,所有工作进程都可用于接收和处理客户端请求。每个工作进程都使用异步非阻塞方式处理。那么,nginx工作进程调用IO后,就去进行其他工作了,当IO调用返回后,IO怎么把状态通知工作进程?有两种方式,一是工作进程主动检查,二是IO完成后主动通知。基于第二种方式,形成了事件驱动模型,放弃创建进程和线程方法,采用如下实现办法,“事件发送器”每传递过来一个请求,“目标对象”就将其放入一个待处理事件的列表,使用非阻塞I/O方式调用。select/poll/epoll等事件驱动处理库(即多路IO复用方法)就是来支持这种方案的。
select有1024链接数限制,非线程安全,事件描述符集合有3组需要分别轮询。poll去除链接数限制,非线程安全,可同时检查3组事件。epoll线程安全,是高效的。
nginx服务器架构
- 1、主进程(Master),配置文件、Socket管理、管理和监控工作进程、平滑升级重启等
- 2、工作进程(Worker),接收客户端请求、请求过滤处理、IO调用获取响应数据、与后端服务器通信、响应客户端请求,接收主进程指令。
- 3、Cache Loader和Cache Manager进程,缓存索引重建及管理。
进程交互,Master-Worker交互是单向管道,Worker-Worker相互隔离的,如需要交互,可从单向管道获取其他Worker信息。
配置文件重载原理:
- 向 master 进程发送 HUP 信号( reload 命令)。
- master 进程检查配置语法是否正确。
- master 进程打开监听端口。
- master 进程使用新的配置文件启动新的 worker 子进程。
- master 进程向老的 worker 子进程发送 QUIT 信号。
- 老的 worker 进程关闭监听句柄,处理完当前连接后关闭进程。
- 整个过程 Nginx 始终处于平稳运行中,实现了平滑升级,用户无感知。
nginx高可用
- keepalived
- LVS
- nginx集群
nginx高并发
1、linux内核
1 | $ ulimit -n 20480 #系统打开文件描述符的最大值 |
2、nginx配置
1 | worker_processes 4; #worker数,推荐等于cpu数 |
三、Nginx配置
基础
包含5个块:全局块、events块、http块、server块、location块
1 | #运行worker进程的归属用户和组,其中组可以不指定 |
/opt/nginx/conf.d/xxx.conf文件如下(只需要server块):
1 | #server块,虚拟主机相关参数 |
内置变量
常用的nginx内置变量以$符号起始
变量名 | 含义 |
---|---|
$remote_addr和$http_x_forwarded_for | 远程客户端的IP地址,请求者IP |
$remote_port | 客户端端口 |
$server_protocol、$server_addr、$server_port | 服务器协议、ip地址、端口 |
$server_name | 虚拟主机名称 |
$uri | 请求url,不包含参数 |
$request_uri | 请求url,包含参数 |
$request | 请求的URI和HTTP协议,这是整个PV日志记录中最有用的信息,记录服务器收到一个什么样的请求 |
$request_length | 请求的长度,包含请求行、请求头和请求体 |
$request_time | 整个请求的总时间 |
$request_method | 请求方法 |
$request_length | 请求的长度 |
$request_filename | 磁盘文件系统待访问文件的完整路径 |
$args | 全部参数字符串 |
$arg_xxx | 特定参数值 |
$is_args | 是否有参数,有返回?,否则返回空 |
$host | 请求信息中的host |
$status | 记录请求返回的http状态码,比如成功是200 |
$uptream_status | upstream状态,比如成功是200 |
$upstream_addr | upstream的地址,即真正提供服务的主机地址 |
$upstream_response_time | 请求过程中,upstream的响应时间 |
$body_bytes_sent | 发送给客户端的文件主体内容的大小 |
$bytes_sent | 发送给客户端的总大小,可以将日志每条记录中的这个值累加起来以粗略估计服务器吞吐量。 |
$http_user_agent | 用户ua |
$http_referer | http的referer字段 |
$http_via | 经过一层代理服务器添加的信息 |
$http_cookie | 用户cookie |
$document_root | 由URI和root、alias规则生成的文件夹路径 |
$request_time和$upstream_response_time等时间,经常用于日志打印,定义如下图
location相关
针对location的匹配,有以下几类:
- 精确匹配: location = /img
- 普通匹配(前缀匹配): location /img/ 或 location ^~ /img/,两者的区别在于匹配优先级。另需要注意尽量后面加上/,如果只有/img,那么/img1也能匹配到
- 大小写敏感正则匹配:location ~ .(gif|jpg|png|jpeg)$
- 大小写不敏感正则匹配: location ~* .(gif|jpg|png|jpeg)$
- 任意匹配: /
匹配优先级: (=) > (^) > () >(~*) > (/path) >(/)
- 匹配到全等匹配时,终止后续所有匹配,直接返回;
- 步骤一未匹配上时,然后遍历所有的普通匹配,按照最长匹配原则找到最满足的匹配项,
如果匹配项前面有^~符号,则终止后续正则匹配,采用该匹配项;反之则继续后续的正则匹配。 - 步骤一二都未匹配上时,此时进行正则匹配,找到第一个满足的正则匹配项,直接返回,若都不满足,则返回步骤二中的最长匹配项。
1 | #location普通匹配 |
反向代理
1 | #upstream在http块里配置。用于定义上游服务器的相关信息 |
SSL配置
1 | { |
限流
- 漏桶流算法,Nginx的限流都是基于漏桶流算法。我们把水比作是请求,漏桶比作是系统处理能力极限,水先进入到漏桶里,漏桶里的水按一定速率流出,当流出的速率小于流入的速率时,由于漏桶容量有限,后续进入的水直接溢出(拒绝请求),以此实现限流。
- 令牌桶算法,可以理解成医院的挂号看病,只有拿到号以后才可以进行诊病。系统会维护一个令牌桶,以一个恒定的速度往桶里放入令牌,这时如果有请求进来想要被处理,则需要先从桶里获取一个令牌,当桶里没有令牌可取时,则该请求将被拒绝服务。令牌桶算法通过控制桶的容量、发放令牌的速率,来达到对请求的限制。
1 | # 限流返回状态码设置 |
静态资源配置
1 | # vue配置 |
TCP/UDP
1 | #配置TCP/UDP相关功能,平行于http块 |
常用
1 | #除了常用请求类型,限制其他类型,如OPTIONS等 |
相关工具
『在线Nginx配置生成』,nginxconfig.io是github开源项目
四、Nginx的三方模块
RTMP流媒体
https://github.com/arut/nginx-rtmp-module
verynginx
https://github.com/alexazhou/VeryNginx/
ngx_http_geoip2
https://github.com/leev/ngx_http_geoip2_module.git,通过ip获取地理信息
echo-nginx-module,memc-nginx-module、rds-json-nginx-module,lua-nginx-module
agentzh的开源
ngx_http_accesskey_module
nginx防盗链
五、Nginx进阶
变量
Nginx 变量的值只有一种类型,那就是字符串。使用$来命名变量,使用标准ngx_rewrite模块的set配置指令来赋值,可使用$name或${name}来进行变量插值。
1 | location /test1{ |
Nginx 变量的创建和赋值操作发生在全然不同的时间阶段。创建只能发生在 Nginx 配置加载的时候,而赋值操作则只会发生在请求实际处理的时候。这意味着不创建而直接使用变量会导致启动失败,同时也意味着我们无法在请求处理时动态地创建新的 Nginx 变量。
Nginx 变量一旦创建,其变量名的可见范围就是整个 Nginx 配置,甚至可以跨越不同虚拟主机的 server 配置块。Nginx 变量名的可见范围虽然是整个配置,但每个请求都有所有变量的独立副本。
按照上方的配置,请求结果如下:
1 | $ curl 'http://localhost:8080/test2' |
变量容器的生命期,也不是与 location 配置块绑定的,而是与当前正在处理的请求绑定的。一个请求在其处理过程中,即使经历多个不同的 location 配置块,它使用的还是同一套 Nginx 变量的副本。比如下面的“内部跳转”,访问/test3,还是会输出”a = [hello]”。
1 | location /test3 { |
nginx提供了大量的内建变量,用于获取关于请求或响应的各种信息。例如由 ngx_http_core 模块提供的内建变量 $uri和$request_uri。其中有个特别的$arg_XXX变量群。其中很多变量如$uri和$request_uri都是只读的,但是$args是可写的,意味着可以改变请求的参数值。
1 | location /test5 { |
1 | $ curl 'http://localhost:8090/test5?a=1 |
可以修改内建变量的值。
1 | location /test6 { |
1 | $ curl 'http://localhost:8080/test6' |
使用了“取处理程序”,变量都会缓存结果。
1 | # 标准 ngx_map 模块的 map 配置指令,映射规则:当 $args 的值等于 debug 的时候,$foo 变量的值就是 1,否则 $foo 的值就为 0. |
类似 ngx_map 模块,标准的 ngx_geo 等模块也一样使用了变量值的缓存机制。因为缓存的存在,变量 $foo 在第一次被读取时就被缓存了,只在请求生命期中的第一次读取中才被执行
1 | $ curl 'http://localhost/test7' |
配置指令的执行顺序
Nginx 处理每一个用户请求时,都是按照若干个不同阶段(phase)依次处理的。Nginx 的请求处理阶段共有 11 个,按照执行顺序依次是 post-read、server-rewrite、find-config、rewrite、post-rewrite、preaccess、access、post-access、try-files、content 以及 log。
1 | typedef enum { |
1 | location /test6 { |
1 | ## 运行结果 |
按照上例,会先在 rewrite 阶段执行完这里的两条 set 赋值语句,然后再在后面的 content 阶段依次执行那两条 echo 语句。分属两个不同处理阶段的配置指令之间是不能穿插着运行的。
主要关注以下几个阶段:
- rewrite阶段:Nginx 已经在 find-config 阶段完成了当前请求与 location 的配对,所以从 rewrite 阶段开始,location 配置块中的指令便可以产生作用。ngx_rewrite 模块中的几乎全部指令,如set。set_by_lua也属于该阶段。rewrite_by_lua运行在阶段末尾。
- access阶段:标准模块 ngx_access、第三方模块 ngx_auth_request 以及第三方模块 ngx_lua 的 access_by_lua 指令就运行在这个阶段。
- content阶段:是所有请求处理阶段中最为重要的一个,因为运行在这个阶段的配置指令一般都肩负着生成“内容”(content)并输出 HTTP 响应的使命。echo、echo_exec、proxy_pass、echo_location等指令,以及 content_by_lua都是在该阶段运行。