一.Web 服务基础介绍
正常情况下的单次
web
服务访问流程:
![](https://i-blog.csdnimg.cn/direct/00917ebf585f47548a0f5df9bce6edda.png)
1.1 互联网发展历程回顾
1993年3月2日,中国科学院高能物理研究所租用AT&T公司的国际卫星信道建立的接入美国SLAC国家实 验室的64K专线正式开通,成为我国连入Internet的第一根专线。
1995年马云开始创业并推出了一个web网站 中国黄页。
1999年创建阿里巴巴 www.alibabagroup.com
2003年5月10日创立淘宝网
2004年12月,马云创立第三方网上支付平台支付宝(蚂蚁金服旗下,共有蚂蚁金服支付宝、余额宝、招 财宝、蚂蚁聚宝、网商银行、蚂蚁花呗、芝麻信用等子业务板块)
2009年开始举办双十一购物狂欢节,以下是历年交易成交额:
2009年双十一:5000万元
2010年双十一:9.36亿元
2011年双十一:33.6亿元
2012年双十一:191亿元
2013年双十一:350亿元
2014年双十一:571亿元
2015年双十一:912.17亿元
2016年双十一:1207亿元
2017年双十一:1682.69亿元
2018年双十一:2135亿元
2019年双十一:2684亿元
2020年双十一:4982亿元
2021年双十一:5403亿元
2022年双十一:5571亿元
2012年1月11日淘宝商城正式更名为“天猫”
2014年9月19日里巴巴集团于纽约证券交易所正式挂牌上市
1.2 Web 服务介绍
Apache起初由美国的伊利诺伊大学香槟分校的国家超级计算机应用中心开发,目前经历了两大版本分别是1.X和2.X ,其可以通过编译安装实现特定的功能。
1.2.1 Apache 经典的 Web 服务端
Apache
起初由美国的伊利诺伊大学香槟分校的国家超级计算机应用中心开发
目前经历了两大版本分别是
1.X
和
2.X
其可以通过编译安装实现特定的功能
1.2.1.1 Apache prefork 模型
预派生模式,有一个主控制进程,然后生成多个子进程,使用
select
模型,最大并发
1024
每个子进程有一个独立的线程响应用户请求
相对比较占用内存,但是比较稳定,可以设置最大和最小进程数
是最古老的一种模式
,
也是最稳定的模式,适用于访问量不是很大的场景
优点:稳定
缺点:每个用户请求需要对应开启一个进程
,
占用资源较多,并发性差
,
不适用于高并发场景
![](https://i-blog.csdnimg.cn/direct/599f65812e9445a3aca005e08031f19a.png)
1.2.1.2 Apache worker 模型
一种多进程和多线程混合的模型
有一个控制进程,启动多个子进程
每个子进程里面包含固定的线程
使用线程程来处理请求
当线程不够使用的时候会再启动一个新的子进程
,
然后在进程里面再启动线程处理请求,
由于其使用了线程处理请求,因此可以承受更高的并发
优点:相比
prefork
占用的内存较少,可以同时处理更多的请求
缺点:使用
keepalive
的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超 时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用(该问题在 prefork模式下,同样会发生)
![](https://i-blog.csdnimg.cn/direct/c3d3c15134854d2fb6e0a55a9083f0ce.png)
1.2.1.3 Apache event模型
Apache
中最新的模式,
2012
年发布的
apache 2.4.X
系列正式支持
event
模型
,
属于事件驱动模型
(epoll)
每个进程响应多个请求,在现在版本里的已经是稳定可用的模式
它和
worker
模式很像,最大的区别在于,它解决了
keepalive
场景下长期被占用的线程的资源浪费问题
(某些线程因为被
keepalive
,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)
event MPM
中,会有一个专门的线程来管理这些
keepalive
类型的线程
当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场
景下的请求处理能力
优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理
keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放
缺点:没有线程安全控制
![](https://i-blog.csdnimg.cn/direct/6cc242e4a7fd41d9aaed0b1d51045879.png)
1.2.2 Nginx-高性能的 Web 服务端
Nginx
是由
1994
年毕业于俄罗斯国立莫斯科鲍曼科技大学的同学为俄罗斯
rambler.ru
公司开发的,开发 工作最早从2002
年开始,第一次公开发布时间是
2004
年
10
月
4
日,版本号是
0.1.0
2019
年
3
月
11
日
F5
与
NGINX
达成协议
,F5
将收购
NGINX
的所有已发行股票,总价值约为
6.7
亿美元。 6.7亿美金约合
44.97
亿人民币
,nginx
核心模块代码长度
198430
(包括空格、注释),所以一行代码约为 2.2万人民币
官网地址
www.nginx.org
Nginx
历经十几年的迭代更新(
https://nginx.org/en/CHANGES
), 目前功能已经非常完善且运行稳 定,另外Nginx
的版本分为开发版、稳定版和过期版,
nginx
以功能丰富著称,它即可以作为
http
服务 器,也可以作为反向代理服务器或者邮件服务器能够快速的响应静态网页的请求
支持
FastCGI/SSL/Virtual Host/URL Rwrite /Gzip / HTTP Basic Auth/http
或者
TCP
的负载均衡
(1.9
版本以 上且开启stream
模块
)
等功能,并且支持第三方的功能扩展。
天猫 淘宝 京东 小米
163
新浪等一线互联网公司都在用
Nginx
或者进行二次开发
基于
Nginx
的工作场景:
![](https://i-blog.csdnimg.cn/direct/bff1583c299f4c0aa9768737cd5f6fdf.png)
1.2.3 用户访问体验和性能
1.2.3.1 用户访问体验统计
互联网存在用户速度体验的
1-3-10
原则,即
1
秒最优,
1-3
秒较优,
3~10
秒比较慢,
10
秒以上用户无法接 受。用户放弃一个产品的代价很低,只是换一个URL
而已。
![](https://i-blog.csdnimg.cn/direct/c508e3f0780e4fe3a82fac111a7ad92f.png)
全球最大搜索引擎
Google
:慢
500ms = 20%
将放弃访问。
全球最大的电商零售网站亚马逊:慢
100ms = 1%
将放弃交易
有很多研究都表明,性能对用户的行为有很大的影响:
79%
的用户表示不太可能再次打开一个缓慢的网站
47%
的用户期望网页能在
2
秒钟以内加载
40%
的用户表示如果加载时间超过三秒钟,就会放弃这个网站
页面加载时间延迟一秒可能导致转换损失
7%
,页面浏览量减少
11%
8
秒定律:用户访问一个网站时,如果等待网页打开的时间超过
8
秒,会有超过
30%
的用户放弃等待
1.2.3.2 影响用户体验的因素
据说马云在刚开始创业在给客户演示时,打开一个网站花了不到四个小时。
影响用户体验的因素 :
1.
客户端
客户端硬件配置
客户端网络速率
客户端与服务端距离
2.
服务器
服务端网络速率
服务端硬件配置
服务端架构设计
服务端应用程序工作模式
服务端并发数量服务端响应文件大小及数量
buffer cache
服务端
I/O
压力
1.2.4
服务端
I/O
流程
1.2.4 服务端 I/O 流程
I/O
在计算机中指
Input/Output
,
IOPS (Input/Output Per Second)
即每秒的输入输出量
(
或读写次数
)
, 是衡量磁盘性能的主要指标之一。IOPS
是指单位时间内系统能处理的
I/O
请求数量,一般以每秒处理的 I/O请求数量为单位,
I/O
请求通常为读或写数据操作请求。
一次完整的
I/O
是用户空间的进程数据与内核空间的内核数据的报文的完整交换,但是由于内核空间与用 户空间是严格隔离的,所以其数据交换过程中不能由用户空间的进程直接调用内核空间的内存数据,而 是需要经历一次从内核空间中的内存数据copy
到用户空间的进程内存当中,所以简单说
I/O
就是把数据从 内核空间中的内存数据复制到用户空间中进程的内存当中。
服务器的
I/O
磁盘
I/O
网络
I/O :
一切皆文件
,
本质为对
socket
文件的读写
1.2.4.1 磁盘 I/O
磁盘
I/O
是进程向内核发起系统调用,请求磁盘上的某个资源比如是
html
文件或者图片,然后内核通过相 应的驱动程序将目标文件加载到内核的内存空间,加载完成之后把数据从内核内存再复制给进程内存, 如果是比较大的数据也需要等待时间
机械磁盘的寻道时间、旋转延迟和数据传输时间:寻道时间:是指磁头移动到正确的磁道上所花费的时间,寻道时间越短则 I/O 处理就越快,目前磁盘的寻道时 间一般在3-15 毫秒左右。旋转延迟:是指将磁盘片旋转到数据所在的扇区到磁头下面所花费的时间,旋转延迟取决于磁盘的转速,通常 使用磁盘旋转一周所需要时间的1/2 之一表示,比如 7200 转的磁盘平均训传延迟大约为 60*1000/7200/2=4.17毫秒,公式的意思为 (每分钟 60 秒 *1000 毫秒每秒 /7200 转每分 /2 ),如果是 15000转的则为 60*1000/15000/2=2 毫秒。数据传输时间:指的是读取到数据后传输数据的时间,主要取决于传输速率,这个值等于数据大小除以传输速 率,目前的磁盘接口每秒的传输速度可以达到600MB ,因此可以忽略不计。常见的机械磁盘平均寻道时间值:7200 转 / 分的磁盘平均物理寻道时间: 9 毫秒10000 转 / 分的磁盘平均物理寻道时间: 6 毫秒15000 转 / 分的磁盘平均物理寻道时间: 4 毫秒常见磁盘的平均延迟时间:7200 转的机械盘平均延迟: 60*1000/7200/2 = 4.17ms10000 转的机械盘平均延迟: 60*1000/10000/2 = 3ms15000 转的机械盘平均延迟: 60*1000/15000/2 = 2ms每秒最大 IOPS 的计算方法:7200 转的磁盘 IOPS 计算方式: 1000 毫秒 /(9 毫秒的寻道时间 +4.17 毫秒的平均旋转延迟时间 )=1000/13.13=75.9 IOPS10000 转的磁盘的 IOPS 计算方式: 1000 毫秒 /(6 毫秒的寻道时间 +3 毫秒的平均旋转延迟时间 )=1000/9=111IOPS15000 转的磁盘的 IOPS 计算方式: 15000 毫秒 /(4 毫秒的寻道时间 +2 毫秒的平均旋转延迟时间 )=1000/6=166.6 IOPS
1.2.4.2 网络 I/O
网络通信就是网络协议栈到用户空间进程的
IO
就是网络
IO
![](https://i-blog.csdnimg.cn/direct/1fce7c25946541eca251ec0f5d11490e.png)
网络
I/O
处理过程
获取请求数据,客户端与服务器建立连接发出请求,服务器接受请求(
1-3
)
构建响应,当服务器接收完请求,并在用户空间处理客户端的请求,直到构建响应完成(
4
)
返回数据,服务器将已构建好的响应再通过内核空间的网络
I/O
发还给客户端(
5-7
)
不论磁盘和网络
I/O
每次
I/O
,都要经由两个阶段:
第一步:将数据从文件先加载至内核内存空间(缓冲区),等待数据准备完成,时间较长
第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短
1.3 I/O 模型
1.3.1 I/O 模型相关概念
同步
/
异步:关注的是消息通信机制,即调用者在等待一件事情的处理结果时,被调用者是否提供完成状 态的通知。
同步:
synchronous
,被调用者并不提供事件的处理结果相关的通知消息,需要调用者主动询问事
情是否处理完成
异步:
asynchronous
,被调用者通过状态、通知或回调机制主动通知调用者被调用者的运行状态
![](https://i-blog.csdnimg.cn/direct/9ecabf7748b141c49648af1cd06766a2.png)
阻塞
/
非阻塞:关注调用者在等待结果返回之前所处的状态
阻塞:
blocking
,指
IO
操作需要彻底完成后才返回到用户空间,调用结果返回之前,调用者被挂
起,干不了别的事情。
非阻塞:
nonblocking
,指
IO
操作被调用后立即返回给用户一个状态值,而无需等到
IO
操作彻底完
成,在最终的调用结果返回之前,调用者不会被挂起,可以去做别的事情。
![](https://i-blog.csdnimg.cn/direct/134d0515f4f848d6855b3882e231e068.png)
1.3.2 网络 I/O 模型
阻塞型、非阻塞型、复用型、信号驱动型、异步
1.3.2.1 阻塞型 I/O 模型(blocking IO)
阻塞
IO
模型是最简单的
I/O
模型,用户线程在内核进行
IO
操作时被阻塞
用户线程通过系统调用
read
发起
I/O
读操作,由用户空间转到内核空间。内核等到数据包到达后,然 后将接收的数据拷贝到用户空间,完成read
操作
用户需要等待
read
将数据读取到
buffer
后,才继续处理接收的数据。整个
I/O
请求的过程中,用户线
程是被阻塞的,这导致用户在发起
IO
请求时,不能做任何事情,对
CPU
的资源利用率不够
优点:程序简单,在阻塞等待数据期间进程
/
线程挂起,基本不会占用
CPU
资源
缺点:每个连接需要独立的进程
/
线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开销 较apache
的
preforck
使用的是这种模式。
同步阻塞:程序向内核发送
I/O
请求后一直等待内核响应,如果内核处理请求的
IO
操作不能立即返回
,
则进 程将一直等待并不再接受新的请求,并由进程轮询查看I/O
是否完成,完成后进程将
I/O
结果返回给 Client,在
IO
没有返回期间进程不能接受其他客户的请求,而且是有进程自己去查看
I/O
是否完成,这种 方式简单,但是比较慢,用的比较少。
1.3.2.2 非阻塞型 I/O 模型 (nonblocking IO)
用户线程发起
IO
请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起
IO
请求,直到数据 到达后,才真正读取到数据,继续执行。即 “
轮询
”
机制存在两个问题:如果有大量文件描述符都要等, 那么就得一个一个的read
。这会带来大量的
Context Switch
(
read
是系统调用,每调用一次就得在用户 态和核心态切换一次)。轮询的时间不好把握。这里是要猜多久之后数据才能到。等待时间设的太长, 程序响应延迟就过大;
设的太短,就会造成过于频繁的重试,干耗
CPU
而已,是比较浪费
CPU
的方式,一 般很少直接使用这种模型,而是在其他IO
模型中使用非阻塞
IO
这一特性。
非阻塞:程序向内核发送请
I/O
求后一直等待内核响应,如果内核处理请求的
IO
操作不能立即返回
IO
结 果,进程将不再等待,而且继续处理其他请求,但是仍然需要进程隔一段时间就要查看内核I/O
是否完 成。
查看上图可知,在设置连接为非阻塞时,当应用进程系统调用 recvfrom
没有数据返回时,内核会立即返 回一个 EWOULDBLOCK
错误,而不会一直阻塞到数据准备好。如上图在第四次调用时有一个数据报准 备好了,所以这时数据会被复制到 应用进程缓冲区 ,于是 recvfrom
成功返回数据
当一个应用进程这样循环调用
recvfrom
时,称之为轮询
polling
。这么做往往会耗费大量
CPU
时间,实 际使用很少
1.3.2.3 多路复用 I/O 型(I/O multiplexing)
上面的模型中
,
每一个文件描述符对应的
IO
是由一个线程监控和处理
多路复用
IO
指一个线程可以同时(实际是交替实现,即并发完成)监控和处理多个文件描述符对应各自 的IO
,即复用同一个线程
一个线程之所以能实现同时处理多个
IO,
是因为这个线程调用了内核中的
SELECT,POLL
或
EPOLL
等系统调 用,从而实现多路复用IO
![](https://i-blog.csdnimg.cn/direct/7efd6dcbb17b465985a697f975dac33e.png)
I/O multiplexing
主要包括
:select
,
poll
,
epoll
三种系统调用,
select/poll/epoll
的好处就在于单个
process
就可以同时处理多个网络连接的
IO
。
它的基本原理就是
select/poll/epoll
这个
function
会不断的轮询所负责的所有
socket
,当某个
socket
有数 据到达了,就通知用户进程。
当用户进程调用了
select
,那么整个进程会被
block
,而同时,
kernel
会
“
监视
”
所有
select
负责的
socket
, 当任何一个socket
中的数据准备好了,
select
就会返回。这个时候用户进程再调用
read
操作,将数据从 kernel拷贝到用户进程。
Apache prefork
是此模式的
select
,
worker
是
poll
模式。
IO
多路复用(
IO Multiplexing)
:是一种机制,程序注册一组
socket
文件描述符给操作系统,表示
“
我要 监视这些fd
是否有
IO
事件发生,有了就告诉程序处理
”IO
多路复用一般和
NIO
一起使用的。
NIO
和
IO
多路
复用是相对独立的。
NIO
仅仅是指
IO API
总是能立刻返回,不会被
Blocking;
而
IO
多路复用仅仅是操作系统 提供的一种便利的通知机制。操作系统并不会强制这俩必须得一起用,可以只用IO
多路复用
+ BIO
,这时 还是当前线程被卡住。IO
多路复用和
NIO
是要配合一起使用才有
实际意义
IO
多路复用是指内核一旦发现进程指定的一个或者多个
IO
条件准备读取,就通知该进程多个连接共用一 个等待机制,本模型会阻塞进程,但是进程是阻塞在select
或者
poll
这两个系统调用上,而不是阻塞在真 正的IO
操作上用户首先将需要进行
IO
操作添加到
select
中,同时等待
select
系统调用返回。当数据到达 时,IO
被激活,
select
函数返回。用户线程正式发起
read
请求,读取数据并继续执行从流程上来看,使用 select函数进行
IO
请求和同步阻塞模型没有太大的区别,甚至还多了添加监视
IO
,以及调用
select
函数的 额外操作,效率更差。并且阻塞了两次,但是第一次阻塞在select
上时,
select
可以监控多个
IO
上是否已 有IO
操作准备就绪,即可达到在同一个线程内同时处理多个
IO
请求的目的。而不像阻塞
IO
那种,一次只 能监控一个IO
虽然上述方式允许单线程内处理多个
IO
请求,但是每个
IO
请求的过程还是阻塞的(在
select 函数上阻塞),平均时间甚至比同步阻塞IO
模型还要长。如果用户线程只是注册自己需要的
IO
请求,然 后去做自己的事情,等到数据到来时再进行处理,则可以提高CPU
的利用率
IO
多路复用是最常使用的
IO模型,但是其异步程度还不够“
彻底
”
,因它使用了会阻塞线程的
select
系统调用。因此
IO
多路复用只能称 为异步阻塞IO
模型,而非真正的异步
IO
优缺点
优点:可以基于一个阻塞对象,同时在多个描述符上等待就绪,而不是使用多个线程
(
每个文件描述 符一个线程)
,这样可以大大节省系统资源
缺点:当连接数较少时效率相比多线程
+
阻塞
I/O
模型效率较低,可能延迟更大,因为单个连接处理 需要 2
次系统调用,占用时间会有增加
O
多路复用适用如下场合:
当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用
I/O
复用
当一个客户端同时处理多个套接字时,此情况可能的但很少出现
当一个服务器既要处理监听套接字,又要处理已连接套接字,一般也要用到
I/O
复用
当一个服务器即要处理
TCP
,又要处理
UDP
,一般要使用
I/O
复用
当一个服务器要处理多个服务或多个协议,一般要使用
I/O
复用
1.3.2.4 信号驱动式 I/O 模型 (signal-driven IO)
信号驱动
I/O
的意思就是进程现在不用傻等着,也不用去轮询。而是让内核在数据就绪时,发送信号通知 进程。
调用的步骤是,通过系统调用
sigaction
,并注册一个信号处理的回调函数,该调用会立即返回,然后主 程序可以继续向下执行,当有I/O
操作准备就绪
,
即内核数据就绪时,内核会为该进程产生一个
SIGIO
信 号,并回调注册的信号回调函数,这样就可以在信号回调函数中系统调用 recvfrom
获取数据
,
将用户进 程所需要的数据从内核空间拷贝到用户空间
此模型的优势在于等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处理函数的通知。
在信号驱动式
I/O
模型中,应用程序使用套接口进行信号驱动
I/O
,并安装一个信号处理函数,进程继续运行并不阻塞
在信号驱动式
I/O
模型中,应用程序使用套接口进行信号驱动
I/O
,并安装一个信号处理函数,进程继续运行并不阻塞
当数据准备好时,进程会收到一个
SIGIO
信号,可以在信号处理函数中调用
I/O
操作函数处理数据。优 点:线程并没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此 可以提高资源的利用率
缺点:信号
I/O
在大量
IO
操作时可能会因为信号队列溢出导致没法通知
异步阻塞:程序进程向内核发送IO
调用后,不用等待内核响应,可以继续接受其他请求,内核收到进程 请求后 进行的IO如果不能立即返回,就由内核等待结果,直到
IO
完成后内核再通知进程
1.3.2.5 异步 I/O 模型 (asynchronous IO)
异步
I/O
与 信号驱动
I/O
最大区别在于,信号驱动是内核通知用户进程何时开始一个
I/O
操作,而异步
I/O 是由内核通知用户进程I/O
操作何时完成,两者有本质区别
,
相当于不用去饭店场吃饭,直接点个外卖,把 等待上菜的时间也给省了
相对于同步
I/O
,异步
I/O
不是顺序执行。用户进程进行
aio_read
系统调用之后,无论内核数据是否准备 好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket
数据准备好了,内核直 接复制数据给进程,然后从内核向进程发送通知。IO
两个阶段,进程都是非阻塞的。
信号驱动
IO
当内核通知触发信号处理程序时,信号处理程序还需要阻塞在从内核空间缓冲区拷贝数据到 用户空间缓冲区这个阶段,而异步IO
直接是在第二个阶段完成后,内核直接通知用户线程可以进行后续 操作了
优点:异步
I/O
能够充分利用
DMA
特性,让
I/O
操作与计算重叠
缺点:要实现真正的异步
I/O
,操作系统需要做大量的工作。目前
Windows
下通过
IOCP
实现了真正的 异步 I/O
,在
Linux
系统下,
Linux 2.6
才引入,目前
AIO
并不完善,因此在
Linux
下实现高并发网络编 程时以 IO
复用模型模式
+
多线程任务的架构基本可以满足需求
Linux
提供了
AIO
库函数实现异步,但是用的很少。目前有很多开源的异步
IO
库,例如
libevent
、
libev
、 libuv。
异步非阻塞:程序进程向内核发送
IO
调用后,不用等待内核响应,可以继续接受其他请求,内核调用的 IO如果不能立即返回,内核会继续处理其他事物,直到
IO
完成后将结果通知给内核,内核在将
IO
完成的 结果返回给进程,期间进程可以接受新的请求,内核也可以处理新的事物,因此相互不影响,可以实现 较大的同时并实现较高的IO
复用,因此异步非阻塞使用最多的一种通信方式。
1.3.3 五种 IO 对比
这五种
I/O
模型中,越往后,阻塞越少,理论上效率也是最优前四种属于同步
I/O
,因为其中真正的
I/O 操作(recvfrom)
将阻塞进程
/
线程,只有异步
I/O
模型才与
POSIX
定义的异步
I/O
相匹配
![](https://i-blog.csdnimg.cn/direct/740b94a6c0f645fb96aea078bd15af31.png)
1.3.4 I/O 的具体实现方式
1.3.4.1 I/O常见实现
Nginx
支持在多种不同的操作系统实现不同的事件驱动模型,但是其在不同的操作系统甚至是不同的系统 版本上面的实现方式不尽相同,主要有以下实现方式:
1
、
select
:
select
库是在
linux
和
windows
平台都基本支持的 事件驱动模型库,并且在接口的定义也基本相同,只是 部分参数的含义略有差异,最大并发限制1024
,是最早期的事件驱动模型。
2
、
poll
:
在
Linux
的基本驱动模型,
windows
不支持此驱动模型,是
select
的升级版,取消了最大的并发限制,在 编译nginx
的时候可以使用
--with-poll_module
和
--without-poll_module
这两个指定是否编译
select
库。
3
、
epoll
:
epoll
是库是
Nginx
服务器支持的最高性能的事件驱动库之一,是公认的非常优秀的事件驱动模型,它和 select和
poll
有很大的区别,
epoll
是
poll
的升级版,但是与
poll
有很大的区别
.epoll
的处理方式是创建一个 待处理的事件列表,然后把这个列表发给内核,返回的时候在去轮询检查这个表,以判断事件是否发 生,epoll
支持一个进程打开的最大事件描述符的上限是系统可以打开的文件的最大数,同时
epoll
库的 I/O效率不随描述符数目增加而线性下降,因为它只会对内核上报的
“
活跃
”
的描述符进行操作。
4
、
kqueue
:
用于支持
BSD
系列平台的高校事件驱动模型,主要用在
FreeBSD 4.1
及以上版本、
OpenBSD 2.0
级以上版本NetBSD
级以上版本及
Mac OS X
平台上,该模型也是
poll
库的变种,因此和
epoll
没有本质上的区别,都是通过避免轮询操作提供效率。
5
、
Iocp
:
Windows
系统上的实现方式,对应第
5
种(异步
I/O
)模型。
6
、
rtsig
:
不是一个常用事件驱动,最大队列1024
,不是很常用
7
、
/dev/poll:
用于支持
unix
衍生平台的高效事件驱动模型,主要在
Solaris
平台、
HP/UX
,该模型是
sun
公司在开发 Solaris系列平台的时候提出的用于完成事件驱动机制的方案,它使用了虚拟的
/dev/poll
设备,开发人员将要见识的文件描述符加入这个设备,然后通过ioctl()
调用来获取事件通知,因此运行在以上系列平台的时候请使用/dev/poll
事件驱动机制。
8
、
eventport
:
该方案也是
sun
公司在开发
Solaris
的时候提出的事件驱动库,只是
Solaris 10
以上的版本,该驱动库看防止内核崩溃等情况的发生。
1.3.4.2 常用I/O模型比较
Select
:
POSIX
所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,本质上是通过设置或者检查存放fd
标志位的数据结构来进行下一步处理
缺点
单个进程能够监视的文件描述符的数量存在最大限制,在
Linux
上一般为
1024
,可以通过修改宏定
FD_SETSIZE
,再重新编译内核实现,但是这样也会造成效率的降低单个进程可监视的
fd
数量被限制,默 认是1024
,修改此值需要重新编译内核对
socket
是线性扫描,即采用轮询的方法,效率较低
select
采取 了内存拷贝方法来实现内核将 FD
消息通知给用户空间,这样一个用来存放大量
fd
的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大
poll
:
本质上和
select
没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个
fd
对应的设备状态其没有 最大连接数的限制,原因是它是基于链表来存储的大量的fd
的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义poll
特点是
“
水平触发
”
,如果报告了
fd
后,没有被处理,那么下次poll时会再次报告该
fd select
是边缘触发即只通知一次
epoll
:
在
Linux 2.6
内核中提出的
select
和
poll
的增强版本支持水平触发
LT
和边缘触发
ET
,最大的特点在于边缘触发,它只告诉进程哪些fd
刚刚变为就需态,并且只会通知一次使用
“
事件
”
的就绪通知方式,通过
epoll_ctl注册fd
,一旦该
fd
就绪,内核就会采用类似
callback
的回调机制来激活该
fd
,
epoll_wait
便可以收到通知
优点
:没有最大并发连接的限制:能打开的FD
的上限远大于
1024(1G
的内存能监听约
10
万个端口
)
,具体查看/proc/sys/fs/file-max,此值和系统内存大小相关
效率提升:非轮询的方式,不会随着
FD
数目的增加而效率下降
;
只有活跃可用的
FD
才会调用
callback
函 数,即epoll
最大的优点就在于它只管理
“
活跃
”
的连接,而跟连接总数无关
内存拷贝,利用
mmap(Memory Mapping)
加速与内核空间的消息传递
;
即
epoll
使用
mmap
减少复制开销
总结
:
1
、
epoll
只是一组
API
,比起
select
这种扫描全部的文件描述符,
epoll
只读取就绪的文件描述符,再加入基于事件的就绪通知机制,所以性能比较好
2
、基于
epoll
的事件多路复用减少了进程间切换的次数,使得操作系统少做了相对于用户任务来说的无用功。
3
、
epoll
比
select
等多路复用方式来说,减少了遍历循环及内存拷贝的工作量,因为活跃连接只占总并发连接的很小一部分。
实例:最大并发连接数和内存有直接关系
#内存1G
[root@centos8 ~]#free -h
total used free shared buff/cache available
Mem: 952Mi 168Mi 605Mi 12Mi 178Mi 629Mi
Swap: 2.0Gi 0B 2.0Gi
[root@centos8 ~]#cat /proc/sys/fs/file-max
92953
#内存2G
[root@centos8 ~]#free -h
total used free shared buff/cache available
Mem: 1.9Gi 258Mi 1.3Gi 12Mi 341Mi 1.6Gi
Swap: 2.0Gi 0B 2.0Gi
[root@centos8 ~]#cat /proc/sys/fs/file-max
195920
实例:
[root@centos8 ~]#grep -R FD_SETSIZE linux-5.8/*
linux-5.8/Documentation/userspace-api/media/v4l/func-select.rst:
``FD_SETSIZE``.
linux-5.8/include/uapi/linux/posix_types.h: #undef __FD_SETSIZE
linux-5.8/include/uapi/linux/posix_types.h: #define __FD_SETSIZE 1024 #单个进程能够
监视的文件描述符的文件最大数量
linux-5.8/include/uapi/linux/posix_types.h: unsigned long fds_bits[__FD_SETSIZE
/ (8 * sizeof(long))];
linux-5.8/tools/include/nolibc/nolibc.h: #define FD_SETSIZE 256
linux-5.8/tools/include/nolibc/nolibc.h:typedef struct { uint32_t
fd32[FD_SETSIZE/32]; } fd_set;
linux-5.8/tools/include/nolibc/nolibc.h: if (fd < 0 || fd >= FD_SETSIZE)
linux-5.8/tools/testing/selftests/net/nettest.c: rc = select(FD_SETSIZE,
NULL, &wfd, NULL, tv);
范例
: select
和
epoll
帮助
![零拷贝](C:\Users\Administrator\Desktop\4.企业高性能web服务器\images\零拷贝.bmp)
[root@centos8 ~]#whatis epoll
epoll (7) - I/O event notification facility
[root@centos8 ~]#whatis select
select (2) - synchronous I/O multiplexing
select (3) - synchronous I/O multiplexing
select (3p) - synchronous I/O multiplexing
[root@centos8 ~]#whatis poll
poll (2) - wait for some event on a file descriptor
poll (3p) - input/output multiplexing
[root@centos8 ~]#man 2 select
SELECT(2) Linux Programmer's
Manual SELECT(2)
NAME
select, pselect, FD_CLR, FD_ISSET, FD_SET, FD_ZERO - synchronous I/O
multiplexing
[root@centos8 ~]#man 2 poll
POLL(2) Linux Programmer's
Manual POLL(2)
NAME
poll, ppoll - wait for some event on a file descriptor
1.4 零拷贝
1.4.1 零拷贝介绍
1.4.1.1 传统 Linux中 I/O 的问题
传统的
Linux
系统的标准
I/O
接口(
read
、
write
)是基于数据拷贝的,也就是数据都是
copy_to_user或者 copy_from_user
,这样做的好处是,通过中间缓存的机制,减少磁盘
I/O
的操作,但是坏处也很明显,大量数据的拷贝,用户态和内核态的频繁切换,会消耗大量的 CPU
资源,严重影响数据传输的性能,统计表明,在Linux
协议栈中,数据包在内核态和用户态之间的拷贝所用的时间甚至占到了数据包整个处理流程时间的57.1%
1.4.1.2 什么是零拷贝
零拷贝就是上述问题的一个解决方案,通过尽量避免拷贝操作来缓解
CPU
的压力。零拷贝并没有真正做 到“0”
拷贝,它更多是一种思想,很多的零拷贝技术都是基于这个思想去做的优化
1.4.2 零拷页相关技术
1.4.2.1 MMAP ( Memory Mapping )
mmap()
系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间 后,进程可以向访问普通内存一样对文件进行访问。
mmap
是一种内存映射文件的方法,即将一个文件或者其它对象映射到进程的地址空间,实现文件磁盘 地址和进程虚拟地址空间中一段虚拟地址的一一对映关系。
实现这样的映射关系后,进程就可以采用指针的方式读写操作这一段内存,而系统会自动回写脏页面到对应的文件磁盘上,即完成了对文件的操作而不必再调用read,write
等系统调用函数。相反,内核空间对这段区域的修改也直接反映用户空间,从而可以实现不同进程间的文件共享。
内存映射减少数据在用户空间和内核空间之间的拷贝操作
,
适合大量数据传输
上面左图为传统读写
,
右图为
MMAP.
两者相比
mmap
要比普通的
read
系统调用少了一次
copy
的过程。因为read调用,进程是无法直接访问
kernel space
的,所以在
read
系统调用返回前,内核需要将数据从内核复制到进程指定的buffer
。但
mmap
之后,进程可以直接访问
mmap
的数据
(page cache)
。
1.4.2.2 SENDFILE
1.4.2.3 DMA 辅助的 SENDFILE
二.Nginx 架构和安装
2.1 Nginx 概述
2.1.1 Nginx 介绍
Nginx
:
engine X
,
2002
年开发,分为社区版和商业版
(nginx plus )
2019
年
3
月
11
日
F5 Networks 6.7
亿美元的价格收购
Nginx
是免费的、开源的、高性能的
HTTP
和反向代理服务器、邮件代理服务器、以及
TCP/UDP
代理服务器
解决
C10K
问题(
10K Connections
)
Nginx
官网:
http://nginx.org
nginx
的其它的二次发行版:
Tengine
:由淘宝网发起的
Web
服务器项目。它在
Nginx
的基础上,针对大访问量网站的需求,添加
了很多高级功能和特性。
Tengine
的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了
很好的检验。它的最终目标是打造一个高效、稳定、安全、易用的
Web
平台。从
2011
年
12
月开始, Tengine成为一个开源项目官网
:
http://tengine.taobao.org/
OpenResty
:基于
Nginx
与
Lua
语言的高性能
Web
平台, 章亦春团队开发,官网:
http://openr
esty.org/cn/
2.1.2 Nginx 功能介绍
静态的
web
资源服务器
html
,图片,
js
,
css
,
txt
等静态资源
http/https
协议的反向代理
结合
FastCGI/uWSGI/SCGI
等协议反向代理动态资源请求
tcp/udp
协议的请求转发(反向代理)
imap4/pop3
协议的反向代理
2.1.3 基础特性
模块化设计,较好的扩展性
高可靠性
支持热部署:不停机更新配置文件,升级版本,更换日志文件
低内存消耗:
10000
个
keep-alive
连接模式下的非活动连接,仅需
2.5M
内存
event-driven,aio,mmap
,
sendfile
2.1.4 Web 服务相关的功能
虚拟主机(
server
)
支持
keep-alive
和管道连接
(
利用一个连接做多次请求
)
访问日志(支持基于日志缓冲提高其性能)
url rewirte
路径别名
基于
IP
及用户的访问控制
支持速率限制及并发数限制
重新配置和在线升级而无须中断客户的工作进程
2.2 Nginx 架构和进程
2.2.2 Nginx 进程结构
web
请求处理机制
多进程方式:服务器每接收到一个客户端请求就有服务器的主进程生成一个子进程响应客户端,直
到用户关闭连接,这样的优势是处理速度快,子进程之间相互独立,但是如果访问过大会导致服务
器资源耗尽而无法提供请求
多线程方式:与多进程方式类似,但是每收到一个客户端请求会有服务进程派生出一个线程和此客
户端进行交互,一个线程的开销远远小于一个进程,因此多线程方式在很大程度减轻了
web
服务器
对系统资源的要求,但是多线程也有自己的缺点,即当多个线程位于同一个进程内工作的时候,可
以相互访问同样的内存地址空间,所以他们相互影响,一旦主进程挂掉则所有子线程都不能工作
了,
IIS
服务器使用了多线程的方式,需要间隔一段时间就重启一次才能稳定。
Nginx
是多进程组织模型,而且是一个由
Master
主进程和
Worker
工作进程组成。
![](https://i-blog.csdnimg.cn/direct/1b7ed21fd6f7436b82c635958644132e.png)
主进程
(master process)
的功能:
对外接口:接收外部的操作(信号)
对内转发:根据外部的操作的不同,通过信号管理
Worker
监控:监控
worker
进程的运行状态,
worker
进程异常终止后,自动重启
worker
进程
读取
Nginx
配置文件并验证其有效性和正确性
建立、绑定和关闭
socket
连接
按照配置生成、管理和结束工作进程
接受外界指令,比如重启、升级及退出服务器等指令
不中断服务,实现平滑升级,重启服务并应用新的配置
开启日志文件,获取文件描述符
不中断服务,实现平滑升级,升级失败进行回滚处理
编译和处理
perl
脚本
工作进程(
worker process
)的功能:
所有
Worker
进程都是平等的
实际处理:网络请求,由
Worker
进程处理
Worker
进程数量:一般设置为核心数,充分利用
CPU
资源,同时避免进程数量过多,导致进程竞争CPU资源,
增加上下文切换的损耗
接受处理客户的请求
将请求依次送入各个功能模块进行处理
I/O
调用,获取响应数据
与后端服务器通信,接收后端服务器的处理结果
缓存数据,访问缓存索引,查询和调用缓存数据
发送请求结果,响应客户的请求
接收主程序指令,比如重启、升级和退出等
![](https://i-blog.csdnimg.cn/direct/e4195f57c0124c94b00f7b4c847b47f0.png)
2.2.3 Nginx 进程间通信
工作进程是由主进程生成的,主进程使用
fork()
函数,在
Nginx
服务器启动过程中主进程根据配置文件决定启动工作进程的数量,然后建立一张全局的工作表用于存放当前未退出的所有的工作进程,主进程生成工作进程后会将新生成的工作进程加入到工作进程表中,并建立一个单向的管道并将其传递给工作进程,该管道与普通的管道不同,它是由主进程指向工作进程的单向通道,包含了主进程向工作进程发出的指令、工作进程ID
、工作进程在工作进程表中的索引和必要的文件描述符等信息。主进程与外界通过信号机制进行通信,当接收到需要处理的信号时,它通过管道向相关的工作进程发送正确的指令,每个工作进程都有能力捕获管道中的可读事件,当管道中有可读事件的时候,工作进程就会从管道中读取并解析指令,然后采取相应的执行动作,这样就完成了主进程与工作进程的交互。
worker 进程之间的通信原理基本上和主进程与 worker 进程之间的通信是一样的,只要 worker 进程之间能够 取得彼此的信息,建立管道即可通信,但是由于worker 进程之间是完全隔离的,因此一个进程想要知道另外一 个进程的状态信息, 就只能通过主进程来实现。为了实现worker 进程之间的交互, master 进程在生成 worker 进程之后,在 worker 进程表中进行遍历,将该 新进程的PID 以及针对该进程建立的管道句柄传递给 worker 进程中的其他进程,为 worker 进程之间的通信做 准备,当worker 进程 1 向 worker 进程 2 发送指令的时候,首先在 master 进程给它的其他 worker 进程工作信息 中找到2 的进程 PID ,然后将正确的指令写入指向进程 2 的管道, worker 进程 2 捕获到管道中的事件后,解析指令并进行相关操作,这样就完成了worker 进程之间的通信。另 worker 进程可以通过共享内存来通讯的,比如 upstream 中的 zone ,或者 limit_req 、 limit_conn 中的zone等。操作系统提供了共享内存机制
2.2.4 Nginx 启动和 HTTP 连接建立
Nginx
启动时,
Master
进程,加载配置文件
Master
进程,初始化监听的
socket
Master
进程,
fork
出多个
Worker
进程
Worker
进程,竞争新的连接,获胜方通过三次握手,建立
Socket
连接,并处理请求
2.2.5 HTTP 处理过程
2.3 Nginx 模块介绍
nginx
有多种模块
核心模块:是
Nginx
服务器正常运行必不可少的模块,提供错误日志记录 、配置文件解析 、事件
驱动机制 、进程管理等核心功能
标准
HTTP
模块:提供
HTTP
协议解析相关的功能,比如: 端口配置 、 网页编码设置 、
HTTP
响应头设置 等等
可选
HTTP
模块:主要用于扩展标准的
HTTP
功能,让
Nginx
能处理一些特殊的服务,比如:
Flash
多媒体传输 、解析
GeoIP
请求、 网络传输压缩 、 安全协议
SSL
支持等
邮件服务模块:主要用于支持
Nginx
的 邮件服务 ,包括对
POP3
协议、
IMAP
协议和
SMTP
协议的支持
Stream
服务模块
:
实现反向代理功能
,
包括
TCP
协议代理
第三方模块:是为了扩展
Nginx
服务器应用,完成开发者自定义功能,比如:
Json
支持、
Lua
支
持等
nginx高度模块化,但其模块早期不支持
DSO
机制
;1.9.11
版本支持动态装载和卸载
模块分类:
核心模块: core module标准模块:HTTP 模块: ngx_http_*HTTP Core modules # 默认功能HTTP Optional modules # 需编译时指定Mail 模块 : ngx_mail_*Stream 模块 ngx_stream_*第三方模块
2.4 Nginx 安装
[root@Nginx ~]# dnf install gcc pcre-devel zlib-devel openssl-devel -y
[root@Nginx nginx-1.24.0]# useradd -s /sbin/nologin -M nginx
[root@Nginx nginx]# tar zxf nginx-1.24.0.tar.gz
[root@Nginx nginx-1.24.0]# useradd -s /sbin/nologin -M nginx
[root@Nginx nginx]# cd nginx-1.24.0/
[root@Nginx nginx-1.24.0]# ls
auto CHANGES.ru configure html Makefile objs src
CHANGES conf contrib LICENSE man README
[root@Nginx nginx-1.24.0]# ./configure --prefix=/usr/local/nginx \
--user=nginx \ # 指定nginx运行用户
--group=nginx \ # 指定nginx运行组
--with-http_ssl_module \ # 支持https://
--with-http_v2_module \ # 支持http版本2
--with-http_realip_module \ # 支持ip透传
--with-http_stub_status_module \ # 支持状态页面
--with-http_gzip_static_module \ # 支持压缩
--with-pcre \ # 支持正则
--with-stream \ # 支持tcp反向代理
--with-stream_ssl_module \ # 支持tcp的ssl加密
--with-stream_realip_module # 支持tcp的透传ip
[root@Nginx nginx-1.24.0]# make && make install
2.6 平滑升级和回滚
[root@Nginx nginx]# tar zxf nginx-1.26.1.tar.gz
[root@Nginx nginx]# cd nginx-1.26.1/
#开始编译新版本
[root@Nginx nginx-1.26.1]# ./configure --with-http_ssl_module --withhttp_v2_module --with-http_realip_module --with-http_stub_status_module --withhttp_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --
with-stream_realip_module
#只要make无需要make install
[root@Nginx nginx-1.26.1]# make
#查看两个版本
[root@Nginx nginx-1.26.1]# ll objs/nginx /usr/local/nginx/sbin/nginx
-rwxr-xr-x 1 root root 1239416 Jul 18 15:08 objs/nginx
-rwxr-xr-x 1 root root 5671488 Jul 18 11:41 /usr/local/nginx/sbin/nginx
#把之前的旧版的nginx命令备份
[root@Nginx ~]# cd /usr/local/nginx/sbin/
[root@Nginx sbin]# cp nginx nginx.24
#把新版本的nginx命令复制过去
[root@Nginx sbin]# \cp -f /root/nginx/nginx-1.26.1/objs/nginx
/usr/local/nginx/sbin
#检测一下有没有问题
[root@Nginx sbin]# nginx -t
nginx: the configuration file /usr/local/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /usr/local/nginx/conf/nginx.conf test is successful
[root@Nginx sbin]# kill -USR2 48732 #nginx worker ID
#USR2 平滑升级可执行程序,将存储有旧版本主进程PID的文件重命名为nginx.pid.oldbin,并启动新的
nginx
#此时两个master的进程都在运行,只是旧的master不在监听,由新的master监听80
#此时Nginx开启一个新的master进程,这个master进程会生成新的worker进程,这就是升级后的Nginx进
程,此时老的进程不会自动退出,但是当接收到新的请求不作处理而是交给新的进程处理。
[root@Nginx sbin]# ps aux | grep nginx
root 48732 0.0 0.1 9868 2436 ? Ss 14:17 0:00 nginx: master
process /usr/local/nginx/sbin/nginx
nobody 48733 0.0 0.2 14200 4868 ? S 14:17 0:00 nginx: worker
process
root 52075 0.0 0.3 9876 6528 ? S 15:41 0:00 nginx: master
process /usr/local/nginx/sbin/nginx
nobody 52076 0.0 0.2 14208 4868 ? S 15:41 0:00 nginx: worker
process
[root@Nginx sbin]# curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.24.0 ##依旧是旧版本生生效
Date: Thu, 18 Jul 2024 07:45:58 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 18 Jul 2024 03:41:13 GMT
Connection: keep-alive
ETag: "66988ed9-267"
Accept-Ranges: bytes
#回收旧版本
[root@Nginx sbin]# kill -WINCH 48732
[root@Nginx sbin]# ps aux | grep nginx
root 48732 0.0 0.1 9868 2436 ? Ss 14:17 0:00 nginx: master
process /usr/local/nginx/sbin/nginx
root 52075 0.0 0.3 9876 6528 ? S 15:41 0:00 nginx: master
process /usr/local/nginx/sbin/nginx
nobody 52076 0.0 0.2 14208 4868 ? S 15:41 0:00 nginx: worker
process
#检测版本信息
[root@Nginx sbin]# curl -I localhost
HTTP/1.1 200 OK
Server: nginx/1.26.1 #新版本生效
Date: Thu, 18 Jul 2024 07:59:45 GMT
Content-Type: text/html
Content-Length: 615
Last-Modified: Thu, 18 Jul 2024 03:41:13 GMT
Connection: keep-alive
ETag: "66988ed9-267"
Accept-Ranges: bytes
nginx启动标本编写
三 Nginx 核心配置详解
3.1 配置文件说明
默认的
nginx.conf
配置文件格式说明
#全局配置端,对全局生效,主要设置nginx的启动用户/组,启动的工作进程数量,工作模式,Nginx的PID路
径,日志路径等。
user nginx nginx;
worker_processes 1; #启动工作进程数数量
events { #events #设置快,主要影响nginx服务器与用户的网络连接,比如是否允许同时接受多
个网络连接,使用哪种事件驱动模型 #处理请求,每个工作进程可以同时支持的
最大连接数,是否开启对多工作进程下的网络连接进行序列化等。
worker_connections 1024; #设置单个nginx工作进程可以接受的最大并发,作为web服务器
的时候最大并发数为 #worker_connections *
worker_processes,作为反向代理的时候为
#(worker_connections * worker_processes)/2
}
http { #http块是Nginx服务器配置中的重要部分,缓存、代理和日志格
式定义等绝大多数功能和第三方模块都 #可以在这设置,http块可
以包含多个server块,而一个server块中又可以包含多个location块,
#server块可以配置文件引入、MIME-Type定义、日志自定义、是
否启用sendfile、连接超时时间和 #单个链接的请求上限等。
include mime.types;
default_type application/octet-stream;
sendfile on; #作为web服务器的时候打开sendfile加快静态文件传输,指定是
否使用
#sendfile系统调用来传输文件
#sendfile系统调用在两个文件描述符之间直接传递数据(完全在
内核中操作)
#从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率
很高,被称之为零拷贝,
#硬盘 >> kernel buffer (快速拷贝到kernelsocket
buffer) >>协议栈。
keepalive_timeout 65; #长连接超时时间,单位是秒
server { #设置一个虚拟机主机,可以包含自己的全局快,同时也可以包含多
个location模块
#比如本虚拟机监听的端口、本虚拟机的名称和IP配置,多个
server 可以使用一个端口比如都使用 #80端口提供web服务
listen 80; #配置server监听的端口
3.2 全局配置
Main 全局配置段常见的配置指令分类
正常运行必备的配置
优化性能相关的配置
用于调试及定位问题相关的配置
事件驱动相关的配置
全局配置说明:
server_name localhost; #本server的名称,当访问此名称的时候nginx会调用当前serevr
内部的配置进程匹配。
location / { #location其实是server的一个指令,为nginx服务器提供比较
多而且灵活的指令
#都是在location中体现的,主要是基于nginx接受到的请求字符
串
#对用户请求的UIL进行匹配,并对特定的指令进行处理
#包括地址重定向、数据缓存和应答控制等功能都是在这部分实现
#另外很多第三方模块的配置也是在location模块中配置。
root html; #相当于默认页面的目录名称,默认是安装目录的相对路径,可以使
用绝对路径配置。
index index.html index.htm; #默认的页面文件名称
}
error_page 500 502 503 504 /50x.html; #错误页面的文件名称
location = /50x.html { #location处理对应的不同错误码的页面定
义到/50x.html
#这个跟对应其server中定义的目录下。
root html; #定义默认页面所在的目录
}
}
#和邮件相关的配置
#mail {
# ...
# } mail 协议相关配置段
#tcp代理配置,1.9版本以上支持
#stream {
# ...
# } stream 服务器相关配置段
#导入其他路径的配置文件
#include /apps/nginx/conf.d/*.conf
}
3.2 全局配置
全局配置说明
:
user nginx nginx; #启动Nginx工作进程的用户和组
worker_processes [number | auto]; #启动Nginx工作进程的数量,一般设为和CPU核心数相同
worker_cpu_affinity 00000001 00000010 00000100 00001000 | auto ;
#将Nginx工作进程绑定到指定的CPU核心,默认Nginx是不进行进程绑定的,绑定并不是意味着当前nginx进
程独占以一核心CPU,但是可以保证此进程不运行在其他核心上,这就极大减少了nginx的工作进程在不同的
cpu核心上的来回跳转,减少了CPU对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务
器的性能。
CPU MASK: 00000001:0号CPU
00000010:1号CPU
10000000:7号CPU
#示例
worker_cpu_affinity 0001 0010 0100 1000;第0号---第3号CPU
worker_cpu_affinity 0101 1010;
#示例
worker_processes 4;
worker_cpu_affinity 00000010 00001000 00100000 10000000;
[root@centos8 ~]# ps axo pid,cmd,psr | grep nginx
31093 nginx: master process /apps 1
34474 nginx: worker process 1
34475 nginx: worker process 3
34476 nginx: worker process 5
34477 nginx: worker process 7
#错误日志记录配置,语法:error_log file [debug | info | notice | warn | error | crit
| alert | emerg]
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log /usr/local/nginx/logs/error.log error;
#pid文件保存路径
pid /usr/local/nginx/logs/nginx.pid;
worker_priority 0; #工作进程优先级,-20~20(19)
worker_rlimit_nofile 65536; #所有worker进程能打开的文件数量上限,
#包括:Nginx的所有连接(例如与代理服务器的连接等)
#而不仅仅是与客户端的连接
#另一个考虑因素是实际的并发连接数不能超过系统级别的最大打开文件
数的限制
#最好与ulimit -n 或者limits.conf的值保持一致,
#修改pam限制
[root@Nginx ~]# sudo -u nginx ulimit -n
1024
[root@Nginx ~]# vim /etc/security/limits.conf
* - nofile 100000
[root@Nginx ~]# sudo -u nginx ulimit -n
100000
daemon off; #前台运行Nginx服务用于测试、docker等环境。
master_process off|on; #是否开启Nginx的master-worker工作模式,仅用于开发调试场景,默认为
on
events {
worker_connections 65535; #设置单个工作进程的最大并发连接数
use epoll; #使用epoll事件驱动,
#Nginx支持众多的事件驱动,
#比如:select、poll、epoll,只能设置在events模块中
设置
accept_mutex on; #on为同一时刻一个请求轮流由work进程处理,
#而防止被同时唤醒所有worker
#避免多个睡眠进程被唤醒的设置,默认为off
#新请求会唤醒所有worker进程,此过程也称为"惊群"
#因此nginx刚安装完以后要进行适当的优化。建议设置为on
multi_accept on; #on时Nginx服务器的每个工作进程可以同时接受多个新的网
络连接
#此指令默认为off,
#即默认为一个工作进程只能一次接受一个新的网络连接
#打开后几个同接受多个。建议设置为on
}
3.4 核心配置示例
3.4.1 新建一个 PC web 站点
3.4.2 root 与 alias
3.4.3 location 的详细使用
3.4.4 Nginx 账户认证功能
3.4.5 自定义错误页面
3.4.6 自定义错误日志
3.4.7 检测文件是否存在
3.4.8 长连接配置
keepalive_requests 3;
keepalive_timeout 65 60;
#开启长连接后,返回客户端的会话保持时间为60s,单次长连接累计请求达到指定次数请求或65秒就会被断
开,第二个数字60为发送给客户端应答报文头部中显示的超时时间设置为60s:如不设置客户端将不显示超时时
间。
Keep-Alive:timeout=60 #浏览器收到的服务器返回的报文
#如果设置为0表示关闭会话保持功能,将如下显示:
#Connection:close 浏览器收到的服务器返回的报文
#使用命令测试:
[root@node100 ~]# telnet lee.timinglee.org 80
Trying 172.25.254.200...
Connected to lee.timinglee.org.
Escape character is '^]'.
GET / HTTP/1.1 ##输入动作
HOST: lee.timinglee.org ##输入访问HOST
##输入回车
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Sat, 20 Jul 2024 12:54:16 GMT
Content-Type: text/html
Content-Length: 15
Last-Modified: Sat, 20 Jul 2024 08:49:12 GMT
Connection: keep-alive
ETag: "669b7a08-f"
Accept-Ranges: bytes
172.25.254.200
GET / HTTP/1.1 #第二次操作
HOST: lee.timinglee.org #第二次操作
#第二次操作
HTTP/1.1 200 OK
Server: nginx/1.24.0
Date: Sat, 20 Jul 2024 12:54:25 GMT
Content-Type: text/html
3.4.9 作为下载服务器配置
ngx_http_autoindex_module 模块处理以斜杠字符 "/" 结尾的请求,并生成目录列表,可以做为下载服务
配置使用
相关指令:
示例:实现下载站点
Content-Length: 15
Last-Modified: Sat, 20 Jul 2024 08:49:12 GMT
Connection: close
ETag: "669b7a08-f"
Accept-Ranges: bytes
172.25.254.200
Connection closed by foreign host. #自动断开链接
'
3.4.9 作为下载服务器配置
四 Nginx 高级配置
4.1 Nginx 状态页
#配置示例:
location /nginx_status {
stub_status;
auth_basic "auth login";
auth_basic_user_file /apps/nginx/conf/.htpasswd;
allow 192.168.0.0/16;
allow 127.0.0.1;
deny all;
}
#状态页用于输出nginx的基本状态信息:
#输出信息示例:
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
上面三个数字分别对应accepts,handled,requests三个值
Reading: 6 Writing: 179 Waiting: 106
Active connections: #当前处于活动状态的客户端连接数
#包括连接等待空闲连接数=reading+writing+waiting
accepts: #统计总值,Nginx自启动后已经接受的客户端请求连接的总数。
handled: #统计总值,Nginx自启动后已经处理完成的客户端请求连接总数
#通常等于accepts,除非有因worker_connections限制等被拒绝的
连接
requests: #统计总值,Nginx自启动后客户端发来的总的请求数
4.2 Nginx 压缩功能
Nginx支持对指定类型的文件进行压缩然后再传输给客户端,而且压缩还可以设置压缩比例,压缩后的文
件大小将比源文件显著变小,样有助于降低出口带宽的利用率,降低企业的IT支出,不过会占用相
应的CPU资源。
Nginx对文件的压缩功能是依赖于模块 ngx_http_gzip_module,默认是内置模块
配置指令如下:
示例:
Reading: #当前状态,正在读取客户端请求报文首部的连接的连接数
#数值越大,说明排队现象严重,性能不足
Writing: #当前状态,正在向客户端发送响应报文过程中的连接数,数值越大,说明
访问量很大
Waiting: #当前状态,正在等待客户端发出请求的空闲连接数
开启 keep-alive的情况下,这个值等于active –
(reading+writing)
4.2 Nginx 压缩功能
4.4 Nginx 变量使用
$remote_addr;
#存放了客户端的地址,注意是客户端的公网IP
$args;
#变量中存放了URL中的所有参数
#例如:https://search.jd.com/Search?keyword=手机&enc=utf-8
#返回结果为: keyword=手机&enc=utf-8
$is_args
#如果有参数为? 否则为空
$document_root;
#保存了针对当前资源的请求的系统根目录,例如:/webdata/nginx/timinglee.org/lee。
$document_uri;
#保存了当前请求中不包含参数的URI,注意是不包含请求的指令
#比如:http://lee.timinglee.org/var?\id=11111会被定义为/var
#返回结果为:/var
$host;
#存放了请求的host名称
limit_rate 10240;
echo $limit_rate;
#如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置, 则显示0
$remote_port;
#客户端请求Nginx服务器时随机打开的端口,这是每个客户端自己的端口
$remote_user;
#已经经过Auth Basic Module验证的用户名
$request_body_file;
#做反向代理时发给后端服务器的本地资源的名称
$request_method;
#请求资源的方式,GET/PUT/DELETE等
$request_filename;
#当前请求的资源文件的磁盘路径,由root或alias指令与URI请求生成的文件绝对路径,
#如:webdata/nginx/timinglee.org/lee/var/index.html
$request_uri;
#包含请求参数的原始URI,不包含主机名,相当于:$document_uri?$args,
#例如:/main/index.do?id=20190221&partner=search
$scheme;
#请求的协议,例如:http,https,ftp等
$server_protocol;
#保存了客户端请求资源使用的协议的版本,例如:HTTP/1.0,HTTP/1.1,HTTP/2.0等
$server_addr;
#保存了服务器的IP地址
$server_name;
#虚拟主机的主机名
$server_port;
#虚拟主机的端口号
$http_user_agent;
#客户端浏览器的详细信息
$http_cookie;
#客户端的所有cookie信息
$cookie_<name>
#name为任意请求报文首部字部cookie的key名
$http_<name>
#name为任意请求报文首部字段,表示记录请求报文的首部字段,ame的对应的首部字段名需要为小写,如果有
横线需要替换为下划线
五 Nginx Rewrite 相关功能
5.1 ngx_http_rewrite_module 模块指令
5.1.1 if 指令
5.1.2 set 指令
5.1.3 break 指令
5.1.4 return 指令
5.2 rewrite 指令
5.2.2 rewrite案例: 域名永久与临时重定向
5.2.2.1 永久重定向301
5.2.3 rewrite 案例: break 与 last
5.2.3.1 break和last区别案例
5.2.4 rewrite案例: 自动跳转 https
5.3 Nginx 防盗链
5.3.1 实现盗链
5.3.2
实现防盗链
[root@Nginx ~]# vim /usr/local/nginx/conf.d/vhosts.conf
server {
listen 80;
server_name lee.timinglee.org;
root /webdata/nginx/timinglee.org/lee;
location /images {
valid_referers none blocked server_names *.timinglee.org ~\.baidu\.;
if ($invalid_referer){
#return 403;
rewrite ^/ http://lee.timinglee.org/daolian.png permanent;
}
}
}
#重启Nginx并访问测试
六 Nginx 反向代理功能
6.1 实现 http 反向代理
6.1.1.4 反向代理示例: 缓存功能
#准备后端httpd服务器
[root@apache20 app1]# pwd
/var/www/html/static
[root@apache20 static]# cat /var/log/messages > ./log.html #准备测试页面
[root@apache30 ~]# ab -n1000 -c100 http://www.timinglee.org/static/index.html
6.1.1.4.2 准备缓存配置
6.1.1.4.3 访问并验证缓存文件
Concurrency Level: 100
Time taken for tests: 23.238 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 2011251000 bytes
HTML transferred: 2010991000 bytes
Requests per second: 43.03 [#/sec] (mean)
Time per request: 2323.789 [ms] (mean)
Time per request: 23.238 [ms] (mean, across all concurrent requests)
Transfer rate: 84521.97 [Kbytes/sec] received
[root@Nginx ~]# vim /apps/nginx/conf/nginx.conf
@@@@内容省略@@@@
#gzip on;
proxy_cache_path /apps/nginx/proxy_cache levels=1:2:2 keys_zone=proxycache:20m
inactive=120s max_size=1g; #配置在nginx.conf http配置段
[root@Nginx ~]# vim /apps/nginx/conf.d/vhost.conf
location ~ /static { #要缓存的URL 或者放在server配置项对所有URL都进行缓存
proxy_pass http://172.25.254.20:8080;
proxy_cache proxycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 301 10m;
proxy_cache_valid any 1m; #必须指定哪些响应码的缓存
}
#/data/nginx/proxycache/ 目录会自动生成
[root@Nginx ~]# ll /apps/nginx/proxy_cache/ -d
drwx------ 3 nginx root 4096 7月 25 20:07 /apps/nginx/proxy_cache/
[root@Nginx ~]# tree /apps/nginx/proxy_cache/
/data/nginx/proxycache/
0 directories, 0 files
#访问web并验证缓存目录
[root@apache30 ~]# ab -n1000 -c100 http://www.timinglee.org/static/index.html
[root@centos8 ~]# ab -n 2000 -c200 http://www.magedu.org/static/log.html
Concurrency Level: 100
Time taken for tests: 10.535 seconds
Complete requests: 1000
Failed requests: 0
Total transferred: 2011251000 bytes
HTML transferred: 2010991000 bytes
Requests per second: 94.92 [#/sec] (mean)
Time per request: 1053.507 [ms] (mean)
Time per request: 10.535 [ms] (mean, across all concurrent requests)
Transfer rate: 186435.60 [Kbytes/sec] received
6.1.2 http 反向代理负载均衡
在上一个节中Nginx可以将客户端的请求转发至单台后端服务器但是无法转发至特定的一组的服务器,而
且不能对后端服务器提供相应的服务器状态监测,Nginx 可以基于ngx_http_upstream_module模块提
供服务器分组转发、权重分配、状态监测、调度算法等高级功能
官方文档: https://nginx.org/en/docs/http/ngx_http_upstream_module.html
6.1.2.1 http upstream配置参数
#验证缓存目录结构及文件大小
[root@Nginx ~]# tree /apps/nginx/proxy_cache/
/apps/nginx/proxy_cache/
└── e
└── 50
└── 99
└── 319432ef3663735a9d3cb4e0c1d9950e
3 directories, 0 files
6.1.2 http 反向代理负载均衡
6.2 实现 Nginx 四层负载均衡
6.2.1 udp 负载均衡实例: DNS
6.2.2 负载均衡实例: MySQL
6.3 实现 FastCGI
6.3.2 FastCGI实战案例 : Nginx与php-fpm在同一服务器
6.3.3 php的动态扩展模块(php的缓存模块)
6.3.4 php高速缓存
7 nginx 二次开发版本
7.1 openresty
Nginx
是俄罗斯人发明的,
Lua
是巴西几个教授发明的,中国人章亦春把
LuaJIT VM
嵌入到
Nginx
中,实现了 OpenResty
这个高性能服务端解决方案
OpenResty®
是一个基于
Nginx
与
Lua
的高性能
Web
平台,其内部集成了大量精良的
Lua
库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web
应用、
Web
服务和动态网关。
OpenResty®
通过汇聚各种设计精良的
Nginx
模块(主要由
OpenResty
团队自主开发),从而将
Nginx有效地变成一个强大的通用 Web
应用平台。这样,
Web
开发人员和系统工程师可以使用
Lua
脚本语言调动 Nginx
支持的各种
C
以及
Lua
模块,快速构造出足以胜任
10K
乃至
1000K
以上单机并发连接的高性能 Web
应用系统。
OpenResty
由于有功能强大且方便的的
API,
可扩展性更强
,
如果需要实现定制功能
,OpenResty
是个不错的选择
官网
:
http://openresty.org/cn
7.2 编译安装 openresty
![](https://i-blog.csdnimg.cn/direct/b6b75fff408243a68ec113489219f017.png)