Nginx 介绍、部署及性能优化
本文主要内容:
一、Nginx 简介
正向代理和反向代理
正向代理: 客户端非常明确要访问的服务器地址,它代理客户 端,替客户端发出请求。比如:FQ (警告⚠️:FQ操作违反相关法律规定,本篇文章仅供 学习参考,切勿盲目FQ)。
反向代理: 均衡分工,控制流量,避免出现局部节点负载过大。 反向代理隐藏了服务器的信息,它代理的是服务器端, 代其接收请求。 换句话说,反向代理的过程中,客户端并不知道具体是 哪台服务器处理了自己的请求。如此一来,既提高了访 问速度,又为安全性提供了保证。
什么是 Nginx ?
Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,在BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力在同类型的网页服务器中表现较好。
注:Nginx 实际代表的是 engine-x,其音标为:/'endʒɪneks/
Nginx 与Apache性能对比
160 million 个站点在 NGINX 上运行
51% 前 10,000 个访问量最大的网站Nginx 占比
36% 的亚马逊网络服务运行着Nginx
二、Nginx 搭建及生产环境配置
Nginx 编译安装
$ yum -y install gcc gcc-c++ # 下载gcc编译器 $ yum -y install pcre-devel openssl-devel # 下载PCRE $ wget http://nginx.org/download/nginx-1.21.1.zip # 下载nginx,官方网站是 http://nginx.org ,自己 # 找到需要的版本,右键复制下载链接 $ tar -zxvf nginx-1.19.2.tar.gz # 解压 $ cd nginx-1.19.2 $ ./configure --prefix=/opt/nginx \ # 生成makefile --with-http_stub_status_module \ # 使用./configure --help查看各个模块的使用情况 --with-http_ssl_module # 使用--with-http_perl_modules方式安装需要的模块 $ make && make install # 编译安装 $ cd /opt/nginx # 进入到安装目录 $ ln -s /usr/local/nginx/sbin/nginx /usr/local/sbin/ # 将 /usr/local/nginx/sbin/nginx 软连接 # 到 /usr/local/sbin 下,就可以在任意地方使用nginx命令
Nginx 系统服务配置
cat > /usr/lib/systemd/system/nginx.service << NGINX_CONF [Unit] Description=The NGINX HTTP and reverse proxy server Documentation=http://nginx.org/en/docs/ Wants=network-online.target [Service] Type=forking PIDFile=${nginx_dir}/logs/nginx.pid ExecStartPre=${nginx_dir}/sbin/nginx -t ExecStart=${nginx_dir}/sbin/nginx ExecReload=${nginx_dir}/sbin/nginx -s reload ExecStop=/bin/kill -s QUIT \$MAINPID [Install] WantedBy=multi-user.target NGINX_CONF
# Nginx system 配置
$ systemctl daemon-reload $ systemctl enable nginx $ systemctl stop nginx $ systemctl start nginx $ systemctl reload nginx
Nginx 动态负载配置
Nginx 动态负载及高可用
Nginx高并发处理原理
进程模型采用Master/Worker 方式。当 nginx 启动的时候,会创建一个 Master 进程,Master进程会根据nginx.conf配置文件中相应的配置项来fork出多个worker子进程去处理请求(怎么处理也是根据配置文件中相应的配置文件)。
Master进程负责管理Worker进程的生命周期、处理网络事件、接收外界信号等。由于Master进程可以fork出多个Worker进程,所以说Nginx是多进程的。
异步处理模式是指nginx处理请求的时候是采用I/O多路复用技术(select | poll | epoll 模型),即多个 I/O 可以复用一个进程。当 worker 进程接收到客户端的请求后,会调用服务端对其请求进行处理,如果没有立即得到响应结果, worker 进程没有阻塞,而是去处理其他请求,知道有请求被服务端处理完成并返回响应结果。
这里的 worker 进程默认就是采用 epoll 多路复用机制来对服务端进行处理的。当服务端返回响应结果时,回调 epoll 多路复用器,epoll 告知 worker 进程,worker 会挂起当前正在处理的线程,去获取响应结果返回客户端,完成后再去执行被挂起的线程。整个过程中不会出现等待的情况,所以理论上Ngnix的一个进程就可以处理无限数量的连接,而且无需轮询。
注:worker 进程接收客户端请求不是采用的 epoll 模型,而是互斥锁机制;只有对服务端的请求和响应采用的是 epoll 模型。
Nginx的特点 – 高并发
$ ulimit –a # 查看当前会话中的linux核心配置,只需要关注open file $ ulimit –n # 查看系统的“进程最大可打开文件数的设置”,默认时1024 vim /etc/security/limit.conf # 修改“进程最大可打开文件数的设置” #添加下面两行 soft nofile 65535 # 应用软件级别限制的最大可打开文件数的限制 hard nofile 65535 # 操作系统级别限制的最大可打开文件数的限制 $ ulimit -n 65535 # 文件保存后不会马上生效,所以还得更改当前会话级别的配置 # 修改nginx配置文件(下面两行) $ vim /src/local/nginx/conf/nginx.conf worker_rlimit_nofile 65535; # 这行,看这里看这里 events { use epoll; worker_connections 65535; # 这行,看这里看这里 } $ nginx -s reload # 热部署重新加载配置文件
要知道,在linux的世界里,一切皆文件.因此要实现大的并发量的第一步,修改linux系统的文件标识符限制数,,也就是文件打开数量的限制
Nginx的特点 – 热部署
$ nginx -s reload
1、当上面的命令一执行,如果发现配置文件已更改,会创建一个新的主进程
2、当前所有的worker进程不会再接收新的请求并把当前正在处理的请求执行完就关闭
3、master主进程会创建新的worker进程来接收并处理新的请求
Nginx的特点 – 模块化
$ git clone https://github.com/agentzh/echo-nginx-module #放入指定位置 $ mv echo-nginx-module-master /opt/nginx/echo-nginx-module $ ./configure #就用这个命令生成新的makefile --prefix=/opt/nginx \ --with-http_stub_status_module \ --with-http_ssl_module \ --add-module=/opt/nginx/echo-nginx-module $ make # 编译(这里只需要make,一定不要执行make install, # 不然会被覆盖) $ cp /opt/nginx/sbin/nginx /opt/nginx/sbin/nginx.bak #备份原文件 $ cp /opt/nginx/objs/nginx /opt/nginx/sbin/nginx $ ln -s /opt/nginx/sbin/nginx /opt/bin/nginx #重新建立软连接,检测配置文件并平滑启动 $ nginx -t $ nginx -s reload
Nginx的特点
低消耗
一万个非活跃性链接,消耗内存仅暂用2.5M
高可用
woker都一个一个的进程,就算其中某个进程挂掉了,也对其他的进程没得影响,而且其他的进程会接替出问题的进程。
Nginx 的整体结构
全局块
配置影响nginx的全局指令。包括:
- 配置运行nginx的服务器用户组
- worker process数
- nginx进程
- pid存放路径
- 错误日志存放路径
- 配置文件的引入
events块
配置影响nginx服务器或与用户的网络连接。包括:
- 设置网络连接的序列化(惊群)
- 是否允许同时接收多个网络连接
- 选择事件驱动模型
- 设置最大连接数
http块
可以嵌套多个server模块,配置代理、缓存、日志定义等和第三方模块的配置。包括:
- 定义MIMI-Type
- 自定义服务日志格式
- 允许sendfile方式传输文件
- 连接超时时间
- 单连接请求数上限
server块
配置虚拟主机相关参数。包括:
- 配置网络监听
- 配置基于名称的虚拟主机
- 配置基于IP的虚拟主机
location块
配置请求的路由,以及页面和其他静态资源的处理。包括:
- Location配置
- 请求根目录配置更改
- URL
- 网站默认首页配置
Nginx Location 匹配
匹配顺序:
1. 首先精确匹配 =
2. 其次最佳匹配 ^~
3. 其次是按文件中顺序的正则匹配
4. 然后匹配不带任何修饰的前缀匹配
5. 最后是交给 / 通用匹配
6. 当有匹配成功时候,停止匹配,按当前匹配规则处理请求
注意:前缀匹配,如果有包含关系时,按最大匹配原则进行匹配。
访问根目录 /,比如 http://localhost/ 将匹配 规则A
访问 http://localhost/login 将匹配 规则B,http://localhost/register 则匹配 规则F
访问 http://localhost/static/a.html 将匹配 规则C
访问 http://localhost/static/files/a.exe 将匹配 规则X,虽然 规则C 也能匹配到,但因为最大匹配原则,最终选中了 规则X。你可以测试下,去掉规则 X ,则当前 URL 会匹配上 规则C。
访问 http://localhost/a.gif, http://localhost/b.jpg 将匹配 规则D 和 规则 E ,但是 规则 D 顺序优先,规则 E 不起作用,而 http://localhost/static/c.png 则优先匹配到 规则 C
访问 http://localhost/a.PNG 则匹配 规则 E ,而不会匹配 规则 D ,因为 规则 E 不区分大小写。
访问 http://localhost/img/a.gif 会匹配上 规则D,虽然 规则Y 也可以匹配上,但是因为正则匹配优先,而忽略了 规则Y。
访问 http://localhost/img/a.tiff 会匹配上 规则Y。
访问 http://localhost/category/id/1111 则最终匹配到规则 F ,因为以上规则都不匹配,这个时候应该是 Nginx 转发请求给后端应用服务器,比如 FastCGI(php),tomcat(jsp),Nginx 作为反向代理服务器存在。
三、Nginx 性能调优
全局模块下的 调优
max_clients = worker_processes * worker_connections;
$ vim /opt/nginx/conf/nginx.conf worker_processes auto; # 设置进程数 worker_cpu_affinity 01 10; # 设置内核数量以及进程使用内核情况 worker_rlimit_nofile 65535; # 可打开最大文件数
worker_processes
worker的进程数,该值一般设置为CPU内核数,或者内核的整倍数。不仅仅取决于CPU内核数,也与磁盘数量和负载均衡模式有关系,在不知道怎么做的时候可以设置为auto。
worker_cpu_affinity
将worker进程与cpu内核进行绑定,该配置是以多位二进制数进行设置。
worker_rlimit_nofile
设置一个worker进程最大能打开的文件数。默认值与当前linux系统的设置的最大能打开的文件描述符一致。
events模块下的调优
I/O 多路复用
select vs. poll vs. epoll/kqueue
其中select和poll都是标准的工作模式,
kqueue和epoll是高效的工作模式,
不同的是epoll用在Linux平台上,kqueue用在BSD系统中。
对于Linux系统Linux2.6+的内核,推荐选择epoll工作模式,这是高性能高并发的设置
https://programmer.help/blogs/introduction-to-select-poll-epoll-i-o-multiplexing.html
https://nima101.github.io/io_multiplexing
$ vim /opt/nginx/conf/nginx.conf events { use epoll; # 设置worker进程与客户端的连接方式(事件驱动模型) worker_connections 1024; # 设置worker进程最大的连接数,默认为512 accept_mutex on; # 设置网路连接序列化,防止惊群现象发生,默认为on accept_mutex_delay 500ms; multi_accept on; # 设置一个进程是否同时接受多个网络连接,默认为off }
https 模块下的调优
http { ... sendfile on; # 开启高效文件传输模式 tcp_nopush on; # 防止网络阻塞 tcp_nodelay on; # 设置数据发送缓存 client_max_body_size 10m; # 最大允许上传的文件大小根据业务需求来设置 keepalive_timeout 65; # 设置客户端与Nginx间所建立的长连接 # 的生命超时时间,时间到达, # 则连接将自动关闭。单位秒 keepalive_requests 2000; # 设置一个长连接发出最多的请求数 ... }
Sendfile
参数用于开启文件的高效传输模式。同时将tcp_nopush和tcp_nodelay两个指令设置为on,可防止网络及磁盘I/O阻塞,提升Nginx工作效率。
tcp_nopush
设置是否将nginx的响应头单独发送,如果开启:则会以单独的形式发送nginx的响应头,而真实的响应体数据会在单独以数据包的形式发送;如果关闭:则会将nginx的响应头和真实的响应体数据一起发送,每个响应都会包含。
tcp_nodelay
设置数据发送缓存,如果开启:不设置数据发送缓存,适合传输小数据;如果关闭:开启数据发送缓存,如果传输图片等大文件,建议设置为 off。
client_body_timeout
设置客户端获取响应的超时时间。如果超时了 ,则认为请求失败。可根据此值得设置来做接口的简单小压测,并且 可设置一个合理的值,保证请求响应的最优。
keepalive_timeout
设置连接活跃时间,如果超时则断开连接。若设置为0,则表示禁止keepalive连接。如果传输的数据量小,且系统运算量很小,则可以将该值设置小一些;反之。
keepalive_requests
设置一个长连接发送请求的最大数。如果当前系统并发量大,而如果 该值 设置很小,则会导致keepalive_timeout 时间还未到,但是keepalive_requests上限已经到了。故我们需要根据真实系统并发量和连接活跃时间来设置该值。
http { ... # 将当前目录(conf 目录)中的 mime.types 文件包含进来 include mime.types; include /opt/nginx/vhost.d/*.conf; server { ... include /opt/nginx/upstream/*.conf; ... upstream { ... include /opt/nginx/upstream/xxx.1hai.cn.conf; ... } ... }
http { ... charset utf-8; # 设置请求与响应的字符编码 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; log_format postdata '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' ' $request_body' '"$http_user_agent" "$http_x_forwarded_for"'; # 开启访问日志,存放路径为logs/access.log,以log_format main格式存放 access_log logs/access.log main; ... }
Nginx 日志切割
$ cat /etc/logrotate-daily /opt/nginx/logs/*.log { rotate 4 daily dateext dateformat .%Y%m%d compress missingok notifempty copytruncate sharedscripts }
为什么要做日志切割?
因为随时系统访问量的增长,访问日志里会出现越来越多的数据,如果不去按照时间去做合理的日志切割,访问日志里的数据多到无法打开的地步,所以需要做日志切割。
logrotate的行为也是受crontab控制,在/etc/cron.daily目录下。
而crontab任务是受anacron控制,在/etc/anacron文件中配置。
Nginx Gzip 压缩
http { ... gzip on; # 开启gzip压缩 # 用来指定压缩的类型 gzip_types application/atom+xml application/javascript application/json application/ld+json application/manifest+json application/rss+xml xxx...; gzip_comp_level 2; ... }
gzip_comp_level
压缩比率。
用来指定gzip压缩比,1压缩比最小,处理速度最快;
9压缩比最大,传输速度快,但处理最慢,也比较消耗CPU资源
Nginx gzip压缩功能介绍
Nginx gzip压缩模块提供了压缩文件内容的功能,用户请求的内容在发送到用户客户端之前,Nginx服务器会根据一些具体的策略实施压缩策略,以节约网站出口带宽,同时加快数据传输效率,来提升用户访问体验。
Nginx gzip压缩的优点
提升网站用户体验:发送给用户的内容小了,用户访问单位大小的页面就加快了,用户体验提升了,网站口碑就好了
节约网站带宽成本:数据是压缩传输的,因此节省了网站的带宽流量成本,不过压缩时会稍微消耗一些CPU资源,这个一般可以忽略不计
需要和不需要压缩的对象
纯文本内容压缩比很高,因此,纯文本的内容最好进行压缩,例如:html、css、js、xml、shtml等格式的文件
被压缩的纯文本文件必须大于1KB,由于压缩算法的特殊原因,极小的文件压缩后可能反而变大
图片、视频(流媒体)等文件尽量不要压缩,因为这些文件大多都是经过压缩的,如果再压缩很可能不会减小或减小很少,或者可能增大,同事压缩时还会消耗大量的CPU、内存资源
Nginx 提供两种限流方式
一、是控制速率
http { ... limit_req_zone $binary_remote_addr zone=myRateLimit:10m rate=10r/s; server { location / { limit_req zone=myRateLimit; proxy_pass http://my_upstream; } } ... }
key:定义限流对象,binary_remote_addr 是一种key,表示基于 remote_addr(客户端IP) 来做限流,binary_ 的目的是压缩内存占用量。
zone:定义共享内存区来存储访问信息, myRateLimit:10m 表示一个大小为10M,名字为myRateLimit的内存区域。1M能存储16000 IP地址的访问信息,10M可以存储16W IP地址访问信息。
rate:用于设置最大访问速率,rate=10r/s 表示每秒最多处理10个请求。Nginx 实际上以毫秒为粒度来跟踪请求信息,因此 10r/s 实际上是限制:每100毫秒处理一个请求。这意味着,自上一个请求处理完后,若后续100毫秒内又有请求到达,将拒绝处理该请求。
http { ... limit_req_zone $binary_remote_addr zone=myRateLimit:10m rate=10r/s; server { location / { # limit_req zone=myRateLimit burst=20; limit_req zone=myRateLimit burst=20 nodelay; proxy_pass http://my_upstream; } } ... }
burst 译为突发、爆发,表示在超过设定的处理速率后能额外处理的请求数。当 rate=10r/s 时,将1s拆成10份,即每100ms可处理1个请求。
此处,burst=20 ,若同时有21个请求到达,Nginx 会处理第一个请求,剩余20个请求将放入队列,然后每隔100ms从队列中获取一个请求进行处理。若请求数大于21,将拒绝处理多余的请求,直接返回503.
不过,单独使用 burst 参数并不实用。假设 burst=50 ,rate依然为10r/s,排队中的50个请求虽然每100ms会处理一个,但第50个请求却需要等待 50 * 100ms即 5s,这么长的处理时间自然难以接受。
nodelay 针对的是 burst 参数,burst=20 nodelay 表示这20个请求立马处理,不能延迟,相当于特事特办。不过,即使这20个突发请求立马处理结束,后续来了请求也不会立马处理。burst=20 相当于缓存队列中占了20个坑,即使请求被处理了,这20个位置这只能按 100ms一个来释放。
这就达到了速率稳定,但突然流量也能正常处理的效果。
二、是控制并发连接数
http { ... limit_conn_zone $binary_remote_addr zone=perip:10m; limit_conn_zone $server_name zone=perserver:10m; server { ... limit_conn perip 10; limit_conn perserver 100; } ... }
设置白名单
http { ... geo $limit { default 1; 10.0.0.0/8 0; 192.168.0.0/24 0; 172.20.0.35 0; } map $limit $limit_key { 0 ""; 1 $binary_remote_addr; } limit_req_zone $limit_key zone=myRateLimit:10m rate=10r/s; ... }
geo 对于白名单(子网或IP都可以) 将返回0,其他IP将返回1。
map 将 $limit 转换为 $limit_key,如果是 $limit 是0(白名单),则返回空字符串;如果是1,则返回客户端实际IP。
limit_req_zone 限流的key不再使用 $binary_remote_addr,而是 $limit_key 来动态获取值。
如果是白名单,limit_req_zone 的限流key则为空字符串,将不会限流;
若不是白名单,将会对客户端真实IP进行限流。
设置黑名单
$ cat blockip.conf deny 165.91.122.67; deny 180.169.22.135; #在nginx的安装目录下面,新建屏蔽ip文件,命名为blockip.conf deny 219.220.141.2; $ vim nginx.conf http{ ... include blocksip.conf; #nginx.conf文件将该配置加入http{}标签末尾 ... } $ nginx -s reload $ cat blockip.conf #如果你想实现这样的应用,除了几个IP外,其他全部拒绝,那需要你在blockip.conf中这样写 allow 1.1.1.1; allow 1.1.1.2; deny all;
限制数据传输速度
http { ... location /flv/ { flv; limit_rate_after 20m; limit_rate 100k; } ... }
除限流外,ngx_http_core_module 还提供了限制数据传输速度的能力(即常说的下载速度)。
这个限制是针对每个请求的,
表示客户端下载前20M时不限速,后续限制100kb/s。