一、简介
Nginx 是一款轻量级的套接字服务,他的特点如下
一、可以充当很多角色或用途:
(1)如过web应用只是一堆简单的html、css、js等静态页面,那根本都用不到nginx的反向代理功能,可以直接用nginx对外提供服务,此时可以将其称之为静态文件服务
(2)在集群架构中,web层服务器上可以用nginx充当web服务用,其作用是反向代理web应用(fastcgi_pass、proxy_pass、uwsgi_pass)
(3)七层负载均衡(反向代理功能+upstream模块)
(4)四层负载均衡(反向代理功能+upstream模块)
(5)还可以基于location来区分动静态请求,实现动静分离,分离集群压力
二、性能非常强大(官方测试能够支撑 5 万并发连接,在实际生产环境中可以支撑 2 到 4 万并发连接。)
(1)内存消耗少,启动极快
(2)nginx采用多进程模型,每个进程都是单线程的,每个worker内的那唯一一个线程采用的是异步非阻塞事件驱动模型(epoll网络IO模型),从而使其非常轻量级且高并发能力极强,在互联网项目中广泛应用。
(3)开源软件,免费
(4)稳定性高,用于反向代理(负载均衡),宕机的概率微乎其微。
(5)支持热部署。在不间断服务的情况下,对软件版本升级。
三、nginx包含的应用场景
四、了解nginx的同类
1.apache:httpd,最早期使用的web服务,性能不高,操作难 2.nginx tengine:Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性 openresty-nginx:OpenResty 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。 3.IIS:windows下的web服务 4.lighttpd:是一个德国人领导的开源 Web 服务器软件,其根本的目的是提供一个专门针对高性能网站,安全、快速、兼容性好并且灵活的 Web Server 环境。具有非常低的内存开销,CPU 占用率低,效能好,以及丰富的模块等特点。 5.GWS:google web server 6.BWS:baidu web server
二、安装nginx
2.1 yum安装
yum安装见:https://egonlin.com/?p=9386
yum安装后会产生一些文件,为了让大家更清晰的了解Nginx软件的全貌,可使用rpm -ql nginx查看整体的目录结构及对应的功能,
了解即可
1.Nginx主配置文件
路径 | 类型 | 作用 |
---|---|---|
/etc/nginx/nginx.conf | 配置文件 | nginx主配置文件 |
/etc/nginx/conf.d/default.conf | 配置文件 | 默认网站配置文件 |
2.Nginx代理相关参数文件
路径 | 类型 | 作用 |
---|---|---|
/etc/nginx/fastcgi_params | 配置文件 | Fastcgi代理配置文件 |
/etc/nginx/scgi_params | 配置文件 | scgi代理配置文件 |
/etc/nginx/uwsgi_params | 配置文件 | uwsgi代理配置文件 |
3.Nginx编码相关配置文件
路径 | 类型 | 作用 |
---|---|---|
/etc/nginx/win-utf | 配置文件 | Nginx编码转换映射文件 |
/etc/nginx/koi-utf | 配置文件 | Nginx编码转换映射文件 |
/etc/nginx/koi-win | 配置文件 | Nginx编码转换映射文件 |
/etc/nginx/mime.types | 配置文件 | Content-Type与扩展名 |
4.Nginx管理相关命令
路径 | 类型 | 作用 |
---|---|---|
/usr/sbin/nginx | 命令 | Nginx命令行管理终端工具 |
/usr/sbin/nginx-debug | 命令 | Nginx命令行与终端调试工具 |
5.Nginx日志相关目录与文件
路径 | 类型 | 作用 |
---|---|---|
/var/log/nginx | 目录 | Nginx默认存放日志目录 |
/etc/logrotate.d/nginx | 配置文件 | Nginx的日志切割配置,由lograte工具管理详见https://egonlin.com/?p=9568第二小节,你也可以直接去/var/log/nginx目录下查看切割的结果 |
2.2 源安装
安装nginx依赖包
yum install gcc* glibc* -y yum install -y pcre pcre-devel yum install -y zlib zlib-devel yum install -y openssl openssl-devel gcc:安装 nginx 需要先将官网下载的源码进行编译,编译依赖 gcc 环境 pcre pcre-devel:pcre 是一个 perl 库,包括 perl 兼容的正则表达式库,nginx 的 http 模块使用 pcre 来解析正则表达式 zlib zlib-devel:zlib 库提供了很多种压缩和解压缩方式,nginx 使用 zlib 对 http 包的内容进行 gzip openssl openssl-devel:OpenSSL 是一个强大的安全套接字层密码库,囊括主要的密码算法、常用的密钥和证书封装管理功能及 SSL 协议,并提供丰富的应用程序供测试或其它目的使用。nginx 不仅支持 http 协议,还支持 https(即在ssl协议上传输http),所以需要在 Centos 安装 OpenSSL 库
下载压缩包
wget https://nginx.org/download/nginx-1.22.1.tar.gz
安装 Nginx 步骤
# 解压到当前目录 [root@localhost opt]# tar -zxvf /opt/nginx-1.12.2.tar.gz # 进入解压目录 [root@localhost opt]# cd nginx-1.22.1/ # 配置参数(具体参数可以./configure --help命令查看) [root@localhost nginx-1.22.1]# ./configure --prefix=/usr/local/nginx1.12.2 --with-http_stub_status_module --with-http_ssl_module # 编译 [root@localhost nginx-1.22.1]# make # 安装 [root@localhost nginx-1.22.1]# make install # 验证 [root@localhost nginx-1.22.1]# /usr/local/nginx1.12.2/sbin/nginx -v nginx version: nginx/1.22.1 # 制作软连接:一般./configure指定的prefix目录都会具体到nginx的版本,这样方便区分,但是在用的时候一般都会做软连接,这样既方便用也方便后续升级 [root@localhost nginx-1.22.1]# ln -s /usr/local/nginx1.12.2 /usr/local/nginx # 配置环境变量/etc/profile.d/nginx.sh export PATH=$PATH:/usr/local/nginx/sbin # system管理配置 #源码包安装后没有办法使用system管理,需要我们自己配置 [root@localhost /usr/lib/systemd/system]# vim /usr/lib/systemd/system/nginx.service [Unit] Description=nginx - high performance web server Documentation=http://nginx.org/en/docs/ After=network-online.target remote-fs.target nss-lookup.target Wants=network-online.target [Service] Type=forking PIDFile=/usr/local/nginx/logs/nginx.pid ExecStart=/usr/local/nginx/sbin/nginx -c /usr/local/nginx/conf/nginx.conf ExecReload=/usr/local/nginx/sbin/nginx -s reload ExecStop=/usr/local/nginx/sbin/nginx -s stop [Install] WantedBy=multi-user.target [root@localhost ~]# systemctl daemon-reload #加载新的unit (*.service)配置文件
三、nginx常用命令
例如:/usr/local/nginx/sbin/nginx Nginx 默认端口号为 80。-v、-t、-s 参数最为常用 参数: -h:命令帮助 -v :打印版本号 -V:打印已经安装的插件等信息 -t:测试配置正确性并退出 -q:测试配置时只显示错误 -s:向主进程发送信号(reload 重新加载配置文件、stop 停止服务) -p:指定Nginx服务器路径前缀 -c: 指定Nginx配置文件路径 -g: 指定Nginx附加配置文件路径\
示例
nginx #启动nginx。 等价于systemctl start nginx nginx -s reopen #重启Nginx。 等价于systemctl restart nginx nginx -s reload #重新加载Nginx配置文件,然后以优雅的方式重启Nginx。 等价于systemctl reload nginx nginx -s stop #强制停止Nginx服务。 等价于systemctl stop nginx nginx -s quit #优雅地停止Nginx服务(即处理完所有请求后再停止服务) nginx -t #检测配置文件是否有语法错误,然后退出 nginx -?,-h #打开帮助信息 nginx -v #显示版本信息并退出 nginx -V #显示版本和配置选项信息,然后退出 nginx -V 2>&1 | sed "s/\s\+--/\n --/g" #模块分行输出,格式化输出 killall nginx #杀死所有nginx进程 systemctl enable nginx #加入开机自启 Centos6: 启动:nginx service nginx start /etc/init.d/nginx start 加入开机自启: chkconfig nginx on nginx -T #检测配置文件是否有语法错误,转储并退出 nginx -q #在检测配置文件期间屏蔽非错误信息 nginx -p prefix #设置前缀路径(默认是:/usr/share/nginx/) nginx -c filename #设置配置文件(默认是:/etc/nginx/nginx.conf) nginx -g directives #设置配置文件外的全局指令
nginx -V # 大写子字母V可以看到nginx的更加详细的信息,包括加载的模块,具体内容解析如下
[root@web02 ~]# nginx -V #显示版本和配置选项信息,然后退出 。。。。 ###########################内容解析####################################### nginx version: nginx/1.18.0 #nginx版本 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) # built with OpenSSL 1.0.2k-fips 26 Jan 2017 TLS SNI support enabled configure arguments: --prefix=/etc/nginx #nginx文件的安装路径,其它选项如果使用相对路径,那么以此路径为基础路径 --sbin-path=/usr/sbin/nginx #二进制程序的安装目录 --modules-path=/usr/lib64/nginx/modules #模块安装路径 --conf-path=/etc/nginx/nginx.conf #设置nginx的conf文件路径 --error-log-path=/var/log/nginx/error.log #设置nginx错误日志路径 --http-log-path=/var/log/nginx/access.log #设置nginx的访问日志路径 --pid-path=/var/run/nginx.pid #设置nginx的pid文件路径 --lock-path=/var/run/nginx.lock #设置lock文件临时存放的路径 --http-client-body-temp-path=/var/cache/nginx/client_temp #设置http客户端主体临时缓存路径 --http-proxy-temp-path=/var/cache/nginx/proxy_temp #设置http反向代理临时路径 --http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp #设置http的fastcgi临时缓存路径 --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp #设置uwsgi临时目录 --http-scgi-temp-path=/var/cache/nginx/scgi_temp #scgi临时存放目录 --user=nginx #设置启动 worker 进程时所使用的非特权用户名 --group=nginx #设置启动 worker 进程时所使用的非特权用户组名 。。。。。。
四、nginx平滑升级方法
1.下载新版本的包 [root@web02 ~]# wget http://nginx.org/download/nginx-1.18.0.tar.gz 2.解压 [root@web02 ~]# tar xf nginx-1.18.0.tar.gz 3.生成 [root@web02 nginx-1.18.0]# cd nginx-1.18.0 [root@web02 nginx-1.18.0]# ./configure --prefix=/usr/local/nginx-1.18.0 --user=www --group=www --without-http_gzip_module --with-compat --with-file-aio --with-threads --with-http_addition_module --with-http_auth_request_module --with-http_dav_module --with-http_flv_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_mp4_module --with-http_random_index_module --with-http_realip_module 4.编译安装 [root@web02 nginx-1.18.0]# make && make install 5.重做软连接:删除磁盘上的nginx包相关文件并不会影响已运行的nginx进程,因为代码文件都已经加载到内存中了 [root@web02 ~]# rm -rf /usr/local/nginx && ln -s /usr/local/nginx-1.18.0 /usr/local/nginx 6.重启服务 [root@web02 ~]# systemctl restart nginx
五、Nginx 配置文件
5.1 配置文件主体结构
一些全局配置 # 1、全局配置 events { ... } # 2、事件驱动配置 http{ # 3、http模块配置 upstream {} server{ location { ... } } } stream{ # 4、stream模块配置 upstream {} server{ location { ... } } }
说明如下:
- main:全局配置,所有其他配置块都包含在其内部
- events 块:控制Nginx处理连接的方式。
- http 块:Nginx处理http请求的主要配置块。
- upstream块:配置负载均衡
- server块:Nginx中主机配置块,可用于配置多个虚拟主机。
- localtion块:可以有多个,用于匹配uri路径
- stream块:stream做四层负载均衡并不常用
- upstream块:配置负载均衡
- server块:四层的server块内部必然是不能写location匹配uri地址
补充:
1、http块与stream块内的upstream及server块均可以有多个
2、在 Nginx 配置文件中,每一条指令配置都必须以 “;” 分号结束。
留存作图地址:https://www.processon.com/diagraming/5889ff28e4b049e795ec6c62
5.2 全局配置介绍
events块、http块、stream块内部都可以包含很多配置,但就这三大块本身来说他们都是属于全局块的,这仨内部的东西暂且不说,先来说一下与他们并列处于顶级的一些配置
# 1、运行用户 user nobody; # 2、工作的worker进程数,通过与cpu核数保持一致,设置为auto,nginx会自动配置为核数。 # 注意:nginx启动后还有一个master进程负责与用户交互响应会话,具体干活的是worker进程 worker_processes auto; # # 3、全局错误日志:日志级别有:debug、info、notice、warn、error、crit、alert #error_log logs/error.log; error_log logs/error.log notice; error_log logs/error.log info; # 4、PID文件 pid logs/nginx.pid; # 5、include用来引入配置文件 # 下述指令的含义:nginx启动时会加载动态dynamic模块的所有配置,确保动态模块都加载到nginx中 include /usr/share/nginx/modules/*.conf;
我们在用nginx配置四层stream负载均衡时,yum安装stream动态模块会产生两个动作
1、下载的模块放到/usr/lib64/nginx/modules目录下
2、加载该插件模块的配置文件放到/usr/share/nginx/modules下
[root@lb ~]# ls /usr/lib64/nginx/modules/ ngx_stream_module.so [root@lb ~]# ls /usr/share/nginx/modules/ mod-stream.conf [root@lb ~]# cat /usr/share/nginx/modules/mod-stream.conf load_module "/usr/lib64/nginx/modules/ngx_stream_module.so";
worker_processes 是 Nginx 服务器实现并发处理的关键所在。worker processes 值越大,可以支持的并发处理量也越多(并不是越大越好,因为会受到硬件、软件等设备的制约)
5.3 events块
储备知识:
什么是 TCP 半连接队列和全连接队列?
在 TCP 三次握手的时候,Linux 内核会维护两个队列,分别是:
1、半连接队列,也称 SYN 队列。
2、全连接队列,也称 accepet 队列。
服务端收到客户端发起的 SYN 请求后,内核会把该连接存储到半连接队列,并向客户端响应 SYN+ACK,接着客户端会返回 ACK,服务端收到第三次握手的 ACK 后,内核会把连接从半连接队列移除,然后创建新的完全的连接,并将其添加到 accept 队列,等待进程调用 accept 函数时把连接取出来。
半连接队列和全连接队列都有最大长度限制,超过限制时,请求会被丢弃,并返回RST
TCP 全连接队列的最大值取决于 min(somaxconn, backlog)。
TCP 半连接队列最大值取决于 max_syn_backlog、somaxconn、backlog
somaxconn tcp_max_syn_backlog 是 Linux 内核的参数,默认值都是 128 sysctl -w net.core.somaxconn=65530 # 设置全连接池大小 sysctl -w net.ipv4.tcp_max_syn_backlog=10240 # 设置半链接池大小 backlog 是 listen(int sockfd, int backlog) 函数中的 backlog 大小,Nginx 默认值是 511。 可以通过nginx.conf配置文件设置其长度,需重启nginx生效。 server { listen 80 default backlog=10240; servarname localhost; ... }
-
net.core.somaxconn
- 阶段:这个参数影响的是三次握手成功后,TCP连接已经被内核接受,即已经从SYN队列转移到accept队列的阶段。在应用程序调用
accept
系统调用之前,这些连接会停留在accept队列中。此参数确定了这个accept队列的最大长度。
- 阶段:这个参数影响的是三次握手成功后,TCP连接已经被内核接受,即已经从SYN队列转移到accept队列的阶段。在应用程序调用
-
net.ipv4.tcp_max_syn_backlog
- 阶段:这个参数影响的是三次握手过程中的第一步到第二步之间,即连接还没有被内核完全接受之前,客户端发送SYN请求后,服务器处于等待状态(挂起),即SYN_RECV状态的连接会被放入SYN队列。此参数确定了SYN队列可以持有的最大请求数。
net.core.somaxconn
涉及的是完全连接(full connection)的队列,也就是已经完成三次握手、等待应用层来accept的队列。net.ipv4.tcp_max_syn_backlog
涉及的是半开连接(half-open connection)的队列,也就是尚未完成三次握手的连接请求队列。
使用 netstat -s | grep “overflowed” 查看全连接队列是否有溢出。
使用 netstat -s | grep “SYNs to LISTEN” 查看半连接队列溢出的情况。(累计值,多次查看)
总的来说就是一个master+多个worker进程
1、主进程主要用来初始化套接字,并管理子进程的生命周期; 2、而 worker 进程,则负责实际的请求处理。
具体来说有两种工作模式
第一种:1个master主进程 + 多个 worker 子进程(worker进程禁用reuseport模式,默认)
# 1、工作模式介绍(共享监听,共享一个全连接队列) 在这种方式下 (1)、主进程执行 bind() + listen() 后,创建多个子进程; (2)、然后,在每个子进程中,都通过 accept() 或 epoll_wait() ,来处理相同的套接字。此时所有子进程/线程共享一个全连接队列 2、特点:所有worker进程共享一个listener全连接队列 3、优点:因为全连接队列是共享的,所以当某个worker进程繁忙的情况下,它去全连接队列里accpet的能力会降低,但其他空闲的worker进程则 不受影响,这会平衡所有worker进程的压力 4、缺点:多个worker进程争抢会带来额外的损耗
第二种:1个master主进程 + 多个 worker 子进程(worker进程启用reuseport模式)
1、工作模式介绍(单独监听,独享自己的全连接队列) 在这种方式下,需要开启 SO_REUSEPORT 选项,所有的worker进程会都监听相同的接口。 然后由内核负责将请求负载均衡到这些监听进程中去,相当于每个进程/线程独享自己的那一个 listener 的全链接队列, 2、特点:每个worker进程都独享自己的listener全连接队列 3、优点: 不需要多个进程/线程竞争某个公共资源,减少竞争的资源消耗,处理效率自然提高了。 4、缺点: 独享可以减少竞争,但会造成分配不均衡,因为每个worker自己listener管理的全连接队列无法分享给别的worker进程,你就无法做到 在你自己的worker繁忙的情况下,让其他空闲的worker进程来accpet()访问你的全连接队列从而分摊你的压力,这会导致单个链接的请求的延迟增大,cpu分配不均匀
在k8s中,现在最新版本的ingress-nginx会默认开启reuseport(第二种),如果你使用的是旧版本,建议手动开启reuseport。
暂时不提k8s,针对我们裸部署的nginx,在启用了reuseport这一配置参数,前后对比,发现以下几个方面优化效果明显:
- CPU负载从30下降到8
- context switch从60K下降到40k
- cpu usage下降
- nginx服务平均延迟下降(付出的代价:单个请求的最高延迟有可能会增加,详见文章最后说明和链接)
- nginx慢请求数量下降
修改参数如下,只需要在listen 端口后面加上 reuseport即可 :
server { listen 80 reuseport; listen 443 ssl http2 reuseport; server_name xxx.xxx.com; charset utf-8;
注意:同一个nginx实例下针对同一个IP+端口,只需要其中一个设置了reuseport即可全部生效。
修改后可以查看listen的socket数量的变化,未使用reuseport时,一个port有一个socket的:
[root@web01 ~]# cat /etc/nginx/nginx.conf user egon; worker_processes 4; # 我的worker进程设置为4个 [root@web01 ~]# ss -lnt |grep 8089 # 4个worker进程reuse了相同的端口 LISTEN 0 511 *:8089 *:* LISTEN 0 511 *:8089 *:* LISTEN 0 511 *:8089 *:* LISTEN 0 511 *:8089 *:* 因为是resuse重用的同一个端口,所以在系统层面netstat -an|grep 8089你当然只能看到一个端口 [root@web01 ~]# netstat -an |grep -w 8089 tcp 0 0 0.0.0.0:8089 0.0.0.0:* LISTEN
修改该参数前后对比如下:
关于SO_REUSERPORT的内容,详细的可以参考 https://lwn.net/Articles/542629/ 和 https://n0p.me/portsharding/
*注意*,启用REUSEPORT也可能导致一些意想不到的问题,比如单个请求处理的最大延迟可能会增加,可以参考 https://blog.cloudflare.com/the-sad-state-of-linux-socket-balancing/
默认情况下,每个worker进程都是单线程
1、概述:Nginx采用的是多进程模型,每个进程都是单线程的。
Nginx的多进程模型主要由一个主进程(master process)和多个工作进程(worker process)组成。主进程负责管理和监控工作进程,而工作进程负责处理实际的客户端请求。 每个工作进程都是单线程的,这意味着每个工作进程在同一时间只能处理一个客户端请求。这种设计选择主要基于以下原因: 1、轻量级:单线程模型相对于多线程或多进程模型来说更加轻量级,减少了线程切换和进程间通信的开销。 2、可扩展性:通过创建多个工作进程,Nginx能够同时处理多个请求,实现高并发处理能力。每个工作进程之间相互独立,可以并行处理请求,提高系统的吞吐量。 3、高效的事件驱动模型:Nginx使用了高效的事件驱动模型(基于epoll、kqueue等),通过异步非阻塞方式处理网络请求,从而避免了线程阻塞和资源浪费。 需要注意的是,尽管每个工作进程是单线程的,但Nginx通过事件驱动和非阻塞I/O的方式能够处理大量并发请求,实现高性能和高吞吐量。这种设计在处理静态内容和反向代理等场景下表现出色,但在涉及大量计算密集型任务的场景下可能会受到性能限制。在这种情况下,通常会将计算任务委托给后端应用服务器来处理。
2、详细一点来说:
当Nginx启动时,它会创建一个主进程(master process),主进程主要负责以下任务: 1、读取并解析配置文件:主进程会读取Nginx的主配置文件(nginx.conf),包括全局配置和默认服务器配置。它还可以包含其他辅助配置文件(如conf.d目录下的文件)。 2、启动工作进程:主进程会根据配置文件中指定的工作进程数量,创建相应数量的工作进程。每个工作进程都是一个独立的进程,负责实际处理客户端请求。 3、监控工作进程:主进程会监控工作进程的运行状态,如果工作进程异常退出或终止,主进程会重新启动新的工作进程来替代。 4、处理信号:主进程可以接收系统信号(如重启、停止等),并根据信号进行相应的操作,例如重新加载配置文件或优雅地关闭工作进程。 每个工作进程都是单线程的,每个工作进程通过异步非阻塞方式处理客户端请求。这种事件驱动的模型允许每个工作进程同时处理多个并发请求,而无需为每个请求创建一个新的线程。
3、工作进程的主要任务包括:
1、接收和处理连接:工作进程通过监听套接字接收客户端的连接请求,并根据配置文件中的服务器块进行请求分发。它会接受请求、解析请求头和请求体,并执行相应的操作。 2、处理请求:工作进程通过事件驱动模型,使用非阻塞I/O方式处理请求。它会执行请求所需的操作,如读取文件、向后端服务器发起代理请求等。 3、响应客户端:工作进程会生成响应数据,并将响应发送回客户端。它会设置响应头、发送响应体,并根据配置进行内容压缩、缓存等操作。 4、日志记录:工作进程会将访问日志和错误日志写入相应的日志文件,记录请求的详细信息和错误信息。
Nginx的多进程单线程模型结合了事件驱动和非阻塞I/O的优势,使得它在高并发场景下能够高效地处理大量的并发请求,同时保持较低的资源消耗。这使得Nginx成为一个流行的高性能Web服务器和反向代理服务器。
events { # 1、epoll仅用于linux2.6以上内核,还有其他io多路复用模型如select、poll等 use epoll; #2、单个后台worker process进程的最大并发链接数,可以调大,但是文件描述也要一起调大,并且你要 # 综合考虑硬件性能。 # 当前 Nginx 服务器能够处理的并发请求数量 = worker_connections × worker_processes worker_connections 1024; # 注意:这个1024并不是开启1024个线程 }
每个worker进程内都是单线程模型,好处是减少锁竞争,轻量级,但也并非十全十美:会存在cpu资源分配不均衡问题
nginx的worker之间的调度单位是连接,而不是请求,因为每个worker进程都是去全连接队列里用accept()方法取到链接后,再去响应用户请求 也就说,一旦某个链接被拿到一个worker进程里,那这个链接的发送过来的请求都在该worker里处理了,不会被其他worker抢走 如果某个worker进程拿到的一个链接里要处理的请求是cpu密集型任务,比如jpeg到webp的图片格式转换,而其他worker拿到的链接里都是一个普通静态文件的访问,那必然导致前者处理速度很慢、很繁忙,而后者处理速度很快、很清闲 繁忙的worker进程accpet接收新链接的能力当然会降低,即在连接层面worker进程之间是可以均衡的,因为nginx的worker进程的调度就是基于链接的,但是请求层面是无法做到均衡的
举个例子
比如:A worker只接了一个链接,B worker接了10个链接,而A worker接的这一个链接里要处理的请求都需要花费10s,而后者虽然接着的链接多但每个链接里要处理的请求都是1ms就处理完毕了 即A的那1个连接里面的请求对应的处理时间比那B的那10个连接的请求要大的话,就会导致CPU资源分配不均匀了, 因为A进程与B进程里都各自只有一个线程,而一个线程只能分配一个cpu,A进程处理1个任务用了一个cpu,B进程处理10个请求任务也只用了1个cpu
想要在nginx这种基于链接的调度模式下,尽可能确保cpu分配均衡一些,那只能在进程内开启多线程了
解决方案:引入线程池,需要付出的代价:增大了出bug的几率,例如锁竞争问题、单个线程bug导致整个进程退出的问题
[root@web01 ~]# cat /etc/nginx/nginx.conf ...... user egon; worker_processes 4; events { use epoll; worker_connections 1024; } thread_pool egon_pool threads=3 max_queue=1024; # 定义线程池 http { #aio threads; aio threads=egon_pool; # 启用线程池 # 启用线程池与reuseport并不冲突 server { listen 8089 reuseport backlog=10240;
重新加载nginx -s reload后查看,每个worker进程状态显示Sl,小写字母l代表多线程模式
[root@web01 ~]# ps aux |grep nginx root 2184 0.0 0.1 39556 2204 ? Ss 19:11 0:00 nginx: master process /usr/sbin/nginx egon 2548 0.0 0.0 64524 1956 ? Sl 20:01 0:00 nginx: worker process egon 2549 0.0 0.0 64524 1956 ? Sl 20:01 0:00 nginx: worker process egon 2550 0.0 0.0 64524 1956 ? Sl 20:01 0:00 nginx: worker process egon 2551 0.0 0.0 64524 1956 ? Sl 20:01 0:00 nginx: worker process [root@web01 ~]# pstree -p 2184 # {}花括号包含的代表是多线程的意思 nginx(2184)─┬─nginx(2548)─┬─{nginx}(2561) │ ├─{nginx}(2562) │ └─{nginx}(2563) ├─nginx(2549)─┬─{nginx}(2552) │ ├─{nginx}(2553) │ └─{nginx}(2554) ├─nginx(2550)─┬─{nginx}(2555) │ ├─{nginx}(2556) │ └─{nginx}(2557) └─nginx(2551)─┬─{nginx}(2558) ├─{nginx}(2559) └─{nginx}(2560)
说明:k8s中ingress-nginx中的nginx开启了reuseport,并且启用了aio threads线程池
5.4 http块
Nginx 服务器配置中最频繁的部分,代理、缓存和日志定义等绝大多数功能和第三方模块的配置都在这里
http { #1、设定日志输出模板 log_format main '$remote_addr - $remote_user [$time_local] "$request" ' '$status $body_bytes_sent "$http_referer" ' '"$http_user_agent" "$http_x_forwarded_for"'; access_log logs/access.log main; #2、网络io优化参数 #2.1 开启sendfile特性可以显著提高静态文件(如图片、CSS、JavaScript文件等)传输的效率 # 原理如下 # 开启sendfile后可以减少数据在操作系统内核和用户空间之间的拷贝次数。 # 没有 sendfile 的情况下,传输文件通常需要经历如下步骤,数据需要在用户空间和内核空间之间进行两次拷贝。 # (1)从硬盘读取文件到用户空间的缓冲区(内核→用户空间) # (2)将读取的数据从用户空间缓冲区写入到操作系统的网络缓冲区(用户空间→内核) # (3)操作系统网络缓冲区发送数据到网络(内核→网络) # 当开启了 sendfile 之后,这个操作简化了,并且可以避免将数据从内核空间拷贝到用户空间。sendfile 系统调用可以直接在内核中传输数据: # (1)内核会将数据从文件系统缓冲区直接发送到网络堆栈,无需先拷贝到用户空间,然后再从用户空间拷贝到内核的网络堆栈 # (2)数据可以直接在内核地址空间中移动,避免了额外的上下文切换和数据复制开销 # 所以,sendfile 能够减少 CPU 的使用,减少操作系统的上下文切换,并允许更快地传输文件数据,特别是对于大型文件的传输 sendfile on; #2.2 通常与sendfile一起用,当 tcp_nopush 设置为 on 时,Nginx 将尽可能发送较大的 TCP 数据包,减少 TCP 报文的数量,提高传输效率。这对于减少网络传输中的 TCP 慢启动阶段,减少网络延迟和提高网络吞吐量非常有用。这在传输大型文件或使用 HTTP/1.1 的 Keep-Alive 长连接时特别有效。 tcp_nopush on; #2.3 开启Nagle算法,数据将会尽快发送出去,而不是等待缓冲区满或者接收到ACK。这会减少延迟,但可能会造成网络利用率低。这个选项在处理需要快速响应的短数据流(例如HTTP/1.1的keep-alive连接)时非常有用。 tcp_nodelay on; #2.4、控制长连接的两个参数: keepalive_timeout 65; # 开启长连接:如果客户端在65秒内没有再次发送新的请求,那么Nginx将关闭这个连接,反之如果在65秒内有新的请求到来,那么这个连接会保持开启,等待处理新的请求 keepalive_requests 100; # 默认情况下,Nginx的keepalive_requests 是设置为100,这个设置针对的是每个长连接在关闭前能处理的最大请求数量。你可以根据需要调整这个值。 #2.5开启gzip压缩,节省带块加速网络传输 gzip on; # 3、控制客户端请求头的缓冲区大小和数量:应对请求头过大的情况 client_header_buffer_size 128k; # 设定用于保存客户端请求头的缓冲区的大小,当客户端发送的请求头超过这个大小时,Nginx 将使用临时文件来保存请求头。这可以防止恶意客户端发送大量数据来消耗服务器资源 large_client_header_buffers 4 128k; # 如果请求头过大,可以使用多个缓冲区来保存。格式为 <数量> <大小>。<数量>代表缓冲区数量,<大小>代表每个缓冲区的大小。 #4、mime.types定义了nginx可以识别的网络资源类型,例如css、js、jpg等 include /etc/nginx/mime.types; #5、http响应header中,如果没有明确指定Content-Type,则默认使用default_type指定的 default_type application/octet-stream; # application/octet-stream。这是一种二进制的数据类型,意味着这种内容不会被浏览器解析,而是作为一个下载文件来处理。这主要用于那些不适合以普通文本或者其他MIME类型表示的文件,例如可执行文件。 # 5、设定虚拟主机配置 server { #侦听80端口 listen 80; listen [::]:80; # [::]: 这代表 IPv6 地址中的一个缩写,它等同于所有的 IPv6 地址,类似于 IPv4 中的 0.0.0.0 #定义使用 www.nginx.cn访问 server_name www.nginx.cn; #定义服务器的默认网站根目录位置 root /usr/share/nginx/html; # 强烈建议用绝对路径,避免混淆,确实可能用相对路径但那除了绕晕你没啥意义 #设定本虚拟主机的访问日志 access_log logs/nginx.access.log main; #默认请求 location / { root /a/b/c; # 优先级高于外部 #定义首页索引文件的名称 index index.php index.html index.htm; } # 定义错误提示页面,出现以下状态码,uri定位到/50x.html,然后触发二次localtion匹配,对上location = /50x.html error_page 500 502 503 504 /50x.html; location = /50x.html { # =号的匹配更精确,所以优先级更高 # 这里没有指定root目录,所以向外层查找,找到server层里定义的root,去该root指定的目录下找/50x.html } #静态文件,nginx自己处理 location ~ ^/(images|javascript|js|css|flash|media|static)/ { #过期30天,静态文件不怎么更新,过期可以设大一点, #如果频繁更新,则可以设置得小一点。 expires 30d; # 这里没有指定root,那去外层也就是server层定义的root指定的目录里找文件 } #PHP 脚本请求全部转发到 FastCGI处理. 使用FastCGI默认配置. location ~ .php$ { fastcgi_pass 127.0.0.1:9000; fastcgi_index index.php; fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; include fastcgi_params; } #禁止访问 .htxxx 文件 location ~ /.ht { deny all; } } }
1、include mime.types;
Nginx 服务器作为 Web 服务器,必须能够识别这些资源类型才能正常响应,具体来说,以下是 /etc/mime.types
在 HTTP 响应中的作用
.jpg
图片文件时,Nginx 会检查 /etc/mime.types
,找到 .jpg
对应的 MIME 类型 image/jpeg
,然后在响应头部设置 Content-Type: image/jpeg
。这样,客户端接收到该文件时就会以图片的形式来处理它。# mime.types包含的部分类型 types { text/html html htm shtml; text/css css; text/xml xml; image/gif gif; image/jpeg jpeg jpg; application/javascript js; application/atom+xml atom; application/rss+xml rss; text/mathml mml; text/plain txt; text/vnd.sun.j2me.app-descriptor jad; text/vnd.wap.wml wml; text/x-component htc; ...... }
2、如果你是使用Nginx作为反向代理服务器,并且你希望与后端服务器之间的连接也保持长连接,你还可以在upstream块中添加keepalive设置,这需要Nginx的版本在1.1.4或者以上。例如:
upstream backend { server backend1.example.com; server backend2.example.com; keepalive 32; }
3、日志格式设置(重要,但很简单,看一下就行):
https://egonlin.com/?p=9568
4、反向代理服务器在转发请求的 http 头信息中,可以增加 x_forwarded_for 信息,用以记录原有客户端的 IP 地址和原来客户端的请求的服务器地址。
server { listen 80; server_name example.com; location / { proxy_pass http://backend_server; proxy_set_header X-Real-IP $remote_addr; # 设置真实/原始客户端的 IP 地址 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; # 追加原始客户端的 IP 地址到 X-Forwarded-For 头信息中。这样,在后端服务器中就可以通过读取 X-Forwarded-For 头信息来获取原始客户端的 IP 地址。 proxy_set_header Host $host; } } 在这个例子中,有三个和代理相关的头信息被添加到了请求中: 1、Host:保存的是原始请求头中的Host字段。这个字段是必要的,因为在某些应用中,会根据这个字段来判断如何处理请求。 2、X-Real-IP:保存的是客户端的真实IP地址。 3、X-Forwarded-For:记录了客户端的真实IP地址,以及所有中间代理服务器的地址。如果这个头已经存在, Nginx会把当前服务器的地址添加到这个头的最后。这个头可以用来追踪请求的全部转发路径。 注意:proxy_set_header 这个指令必须在location块中,而不能在http或者server块中。 因为头信息的设置是在处理请求的时候进行的,所以它必须在处理请求的位置设置。
5、gzip
Nginx 开启 gzip 压缩功能,可以使网站的 css、js 、xml、html 文件在传输时进行压缩,提高访问速度,进而优化 Nginx 性能。 gzip 压缩可以配置 http,server 和 location 块下,位置不同,作用域不同。 1)作用:将响应报⽂发送⾄客户端之前可以启⽤压缩功能,这能够有效地节约带宽,并提⾼响应⾄客户端的速度。 2)语法:gzip 指令 含义 gzip on; 决定是否开启 gzip 模块,on 代表开启,off 代表关闭 gzip_min_length 1100; 设置允许压缩的页面最小字节(从header头的Content-Length中获取) ,当返回内容大于此值时才会使用 gzip 进行压缩,以 K 为单位,当值为 0 时,所有页面都进行压缩。建议大于1k gzip_buffers 16 8k; 设置 gzip 申请内存的大小,其作用是按块大小的倍数申请内存空间。此处指按照原始数据大小以 8k 为单位的 16 倍申请内存 gzip_comp_level 6; 设置 gzip 压缩等级,等级越底压缩速度越快,文件压缩比越小,反之速度越慢文件压缩比越大;等级1-9,最小的压缩最快 但是消耗 cpu gzip_disable “msie6” (IE5.5 和 IE6 SP1使用 msie6 参数来禁止 gzip 压缩 )指定哪些不需要 gzip 压缩的浏览器(将和User-Agents进行匹配),依赖于 PCRE 库 gzip_types text/plain text/css … 设置需要压缩的MIME类型,非设置值不进行压缩,即匹配压缩类型 3) 示例 http { gzip on; gzip_min_length 1k; gzip_buffers 4 16k; gzip_http_version 1.1; gzip_comp_level 9; gzip_types text/plain application/x-javascript text/css application/xml text/javascript application/x-httpd-php application/javascript application/json; gzip_disable "msie6"; gzip_vary on; }
5.5 server块详解-虚拟主机
server 块主要负责配置虚拟主机。每个 http 块可以包含多个 server 块,而每个 server 块就相当于一个虚拟主机。
什么时虚拟主机
在同一台物理服务器上,只部署一个nginx,然后在nginx的配置文件内可以定义多个server, 每个server都可以用来管理一个站点、具有独立的程序及资源,可以独立地对外提供服务供用户访问 用户在访问这些站点时,就好像在访问一台独立的物理服务器一样, 但其实都源自于一台机器,访问的站点只是同一台物理机上的多个server配置而已, 这个server便被称之为虚拟主机了 该技术的产生是为了节省互联网服务器硬件成本。