一 Web服务基础介绍:
正常情况下的单次web服务访问流程:
![image-20200714203812157](https://img-blog.csdnimg.cn/img_convert/1b3feb3abaef8dbe6f6eb30b94c39e7e.png)
1.1 互联网发展历程回顾:
1993年3月2日,中国科学院高能物理研究所租用AT&T公司(美国电话电报公司)的国际卫星信道建立的接入美国SLAC国家实验室的64K专线正式开通,成为我国连入Internet的第一根专线。
http://www.ihep.cas.cn/kxcb/kpcg/jsywl/201407/t20140714_4156699.html
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亿;
。。。。。
2012年1月11日淘宝商城正式更名为“天猫”。
2014年9月19日里巴巴集团于纽约证券交易所正式挂牌上市。
2020年福布斯统计马云财富415亿美元。
1.2:web服务介绍:
Netcraft公司于1994年底在英国成立,多年来一直致力于互联网市场以及在线安全方面的咨询服务,其中在国际上最具影响力的当属其针对网站服务器,域名解析/主机提供商,以及SSL市场所做的客观严谨的分析研究。
https://news.netcraft.com/
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-7QnlWD6x-1610347882784)(https://news.netcraft.com/images/2020/06/wss-share.png)]](https://img-blog.csdnimg.cn/20210111145841687.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3dlaXhpbl80NjIzODc1NQ==,size_16,color_FFFFFF,t_70)
1.2.1 Apace-早期的web服务端:
pache起初由美国的伊利诺伊大学香槟分校的国家超级计算机应用中心开发,目前经历了两大版本分别是1.X和2.X,其可以通过编译安装实现特定的功能,目前支持三种不同的MPM(multi-processing module,多进程处理模块,官方网站 http://www.apache.org。
1.2.1.1:Apache prefork模型:
预派生模式,有一个主控制进程,然后生成多个子进程,使用select模型,最大并发1024,每个子进程有一个独立的线程响应用户请求,相对比较占用内存,但是比较稳定,可以设置最大和最小进程数,是最古老的一种模式,也是最稳定的模式,适用于访问量不是很大的场景。
优点:稳定
缺点:大量用户访问慢,占用资源,1024个进程不适用于高并发场景
![image-20200713210623864](https://img-blog.csdnimg.cn/img_convert/84c426b63372499f523c010cb595606b.png)
1.2.1.2 :Apache woker 模型:
一种多进程和多线程混合的模型,有一个控制进程,启动多个子进程,每个子进程里面包含固定的线程,使用线程程来处理请求,当线程不够使用的时候会再启动一个新的子进程,然后在进程里面再启动线程处理请求,由于其使用了线程处理请求,因此可以承受更高的并发。
优点:相比prefork 占用的内存较少,可以同时处理更多的请求
缺点:使用keepalive的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用。(该问题在prefork模式下,同样会发生)
![image-20200713210608316](https://img-blog.csdnimg.cn/img_convert/ac9f1104fed6156f4f31890ee489a316.png)
1.2.1.3:Apache event模型
Apache中最新的模式,2012年发布的apache 2.4.X系列正式支持event 模型,属于事件驱动模型(epoll),每个进程响应多个请求,在现在版本里的已经是稳定可用的模式。它和worker模式很像,最大的区别在于,它解决了keepalive场景下,长期被占用的线程的资源浪费问题(某些线程因为被keepalive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)。event MPM中,会有一个专门的线程来管理这些keepalive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力。
优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放
缺点:没有线程安全控制
![image-20200713210724042](https://img-blog.csdnimg.cn/img_convert/552c0fb50bdc6d615e9f6cccf0bb278c.png)
1.2.2 Nginx-高性能的web服务端
Nginx是由1994年毕业于俄罗斯国立莫斯科鲍曼科技大学的同学为俄罗斯rambler.ru公司开发的,开发工作最早从2002年开始,第一次公开发布时间是2004年10月4日,版本号是0.1.0,官网地址
Nginx历经十几年的迭代更新(https://nginx.org/en/CHANGES), 目前功能已经非常完善且运行稳定,另外Nginx的版本分为开发版、稳定版和过期版,Nginx以功能丰富著称,它即可以作为http服务器,也可以作为反向代理服务器或者邮件服务器,能够快速的响应静态网页的请求,支持FastCGI/SSL/Virtual Host/URL Rwrite/Gzip/HTTP Basic Auth/http或者TCP的负载均衡(1.9版本以上且开启stream模块)等功能,并且支持第三方的功能扩展。
为什么使用Nginx:
天猫 淘宝 小米 163 京东新浪等一线互联网公司都在用Nginx或者进行二次开发
基于Nginx的访问流程如下:
![image-20200713210806273](https://img-blog.csdnimg.cn/img_convert/b702e6c632542ea62c99484683863c1d.png)
说明:nginx会根据请求的类型进行动静分离,静态的请求交给存储服务器,动态请求交给MySQL
1.2.3 用户访问体验统计
互联网存在用户速度体验的1-3-10原则,即1秒最优,1-3秒较优,3~10秒比较慢,10秒以上用户无法接受。用户放弃一个产品的代价很低,只是换一个URL而已。
http://baijiahao.baidu.com/s?id=1643187950686234006&wfr=spider&for=pc #用户体验的重要性
http://www.sohu.com/a/218426004_573333
全球最大搜索引擎 Google:慢500ms = 20% 将放弃访问。
全球最大的电商零售网站 亚马逊:慢100ms = 1% 将放弃交易
![image-20200713210901720](https://img-blog.csdnimg.cn/img_convert/fcdaf739ff8e49b9f23dcf491c191e29.png)
1.2.4:性能影响:
有很多研究都表明,性能对用户的行为有很大的影响:
79%的用户表示不太可能再次打开一个缓慢的网站
47%的用户期望网页能在2秒钟以内加载
40%的用户表示如果加载时间超过三秒钟,就会放弃这个网站
页面加载时间延迟一秒可能导致转换损失7%,页面浏览量减少11%
8秒定律:用户访问一个网站时,如果等待网页打开的时间超过8秒,会有超过30%的用户放弃等待
1.2.4.1:影响用户体验的几个因素:
据说马云在刚开始创业在给客户演示时,打开一个网站花了两个多小时。
https://www.shuimiao.net/NjHaO/
客户端原因:
- 客户端硬件配置
- 客户端网络速率
- 客户端与服务端距离
服务端原因:
- 服务端网络速率
- 服务端硬件配置
- 服务端架构设计
- 服务端应用程序工作模式
- 服务端并发数量
- 服务端响应文件大小及数量
- 服务端I/O压力
1.2.4.2 应用程序工作模式:
httpd MPM(Multi-Processing Module,多进程处理模块)模式:
- prefork:进程模型,两级结构,主进程master负责生成子进程,每个子进程负责响应一个请求
- worker:线程模型,三级结构,主进程master负责生成子进程,每个子进程负责生成多个线程,每个线程响应一个请求
- event:线程模型,三级结构,主进程master负责生成子进程,每个子进程生成多个线程,每个线程响应一个请求,但是增加了一个监听线程,用于解决在设置了keep-alived场景下线程的空等待问题。
Nginx(Master+Worker)模式:
#Centos 7.x目前默认为prefork模式,ubuntu 18.04已经使用event模式
Apache线程验证方式:
[root@localhost ~]# httpd -V #Centos系统
AH00558: httpd: Could not reliably determine the server's fully qualified domain name,using localhost.localdomain. Set the 'ServerName' directive globally to suppress thismessage
Server version: Apache/2.4.6 (CentOS)
Server built: Aug 8 2019 11:41:18
Server's Module Magic Number: 20120211:24
Server loaded: APR 1.4.8, APR-UTIL 1.5.2
Compiled using: APR 1.4.8, APR-UTIL 1.5.2
Architecture: 64-bit
Server MPM: prefork #MPM模式为prefork
threaded: no
# ps -ef | grep httpd
root 2757 1 0 04:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 2758 2757 0 04:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 2759 2757 0 04:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 2760 2757 0 04:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 2761 2757 0 04:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
apache 2762 2757 0 04:03 ? 00:00:00 /usr/sbin/httpd -DFOREGROUND
root 2786 2369 0 04:13 pts/0 00:00:00 grep --color=auto httpd
# cat /proc/2760/status
Name: httpd
State: S (sleeping)
..............
Threads: 1
..............
#Ubuntu 18.04.3系统:
root@mq-node1:~# apachectl -V
Server version: Apache/2.4.29 (Ubuntu)
Server built: 2019-09-16T12:58:48
Server's Module Magic Number: 20120211:68
Server loaded: APR 1.6.3, APR-UTIL 1.6.1
Compiled using: APR 1.6.3, APR-UTIL 1.6.1
Architecture: 64-bit
Server MPM: event #MPM模式为event
threaded: yes (fixed thread count)
forked: yes (variable process count)
# ps -ef | grep apache2
root 918 1 0 20:08 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 919 918 0 20:08 ? 00:00:00 /usr/sbin/apache2 -k start
www-data 920 918 0 20:08 ? 00:00:00 /usr/sbin/apache2 -k start
root 1202 1172 0 20:12 pts/0 00:00:00 grep --color=auto apache2
# cat /proc/919/status
Name: apache2
Umask: 0022
State: S (sleeping)
..............
Threads: 27
..............
1.2.4.3:服务端I/O:
I/O在计算机中指Input/Output, IOPS (Input/Output Per Second)即每秒的输入输出量(或读写次数),是衡量磁盘性能的主要指标之一。IOPS是指的是在单位时间内系统能处理的I/O请求数量,一般以每秒处理的I/O请求数量为单位,I/O请求通常为读或写数据操作请求。
机械磁盘的寻道时间、旋转延迟和数据传输时间:
- 寻道时间:是指磁头移动到正确的磁道上所花费的时间,寻道时间越短则I/O处理就越快,目前磁盘的寻道时间一般在3-15毫秒左右。
- 旋转延迟:是指将磁盘片旋转到数据所在的扇区到磁头下面所花费的时间,旋转延迟取决于磁盘的转速,通常使用磁盘旋转一周所需要时间的1/2之一表示,比如7200转的磁盘平均训传延迟大约为60*1000/7200/2=4.17毫秒,公式的意思为 (每分钟60秒*1000毫秒每秒/7200转每分钟/2),如果是15000转的则为60*1000/15000/2=2毫秒。
- 数据传输时间:指的是读取到数据后传输数据的时间,主要取决于传输速率,这个值等于数据大小除以传输速率,目前的磁盘接口每秒的传输速度可以达到600MB,因此可以忽略不计。
注意:web服务器千万不要用7200转的机械盘,最低用10000的
范例:
常见的机械磁盘平均寻道时间值:
7200转/分的磁盘平均物理寻道时间:9毫秒
10000转/分的磁盘平均物理寻道时间:6毫秒
15000转/分的磁盘平均物理寻道时间:4毫秒
常见磁盘的平均延迟时间:
7200转的机械盘平均延迟:60\*1000/7200/2 = 4.17ms
10000转的机械盘平均延迟:60\*1000/10000/2 = 3ms
15000转的机械盘平均延迟:60\*1000/15000/2 = 2ms
每秒最大IOPS的计算方法:
7200转的磁盘IOPS计算方式:1000毫秒/(9毫秒的寻道时间+4.17毫秒的平均旋转延迟时间)=1000/13.13=75.9 IOPS
10000转的磁盘的IOPS计算方式:1000毫秒/(6毫秒的寻道时间+3毫秒的平均旋转延迟时间)=1000/9=111 IOPS
15000转的磁盘的IOPS计算方式:15000毫秒/(4毫秒的寻道时间+2毫秒的平均旋转延迟时间)=1000/6=166.6 IOPS
一次完整的I/O是 用户空间的进程数据与内核空间的内核数据的报文的完整交换过程 ,但是由于内核空间与用户空间是严格隔离的,所以其数据交换过程中不能由用户空间的进程直接调用内核空间的内存数据,而是需要经历一次从内核空间中的内存数据copy到用户空间的进程内存当中,所以简单说 一次 I/O 就是把数据从内核空间中的内存数据复制到用户空间中进程的内存当中的整个过程 。
而网络通信就是从网络协议栈到用户空间进程的IO,也就是网络IO。
![image-20200713212107477](https://img-blog.csdnimg.cn/img_convert/8a204d76c23ad103f48057043fe9b2c7.png)
磁盘I/O是进程向内核发起系统调用,请求磁盘上的某个资源比如是文件或者是图片,然后内核通过相应的驱动程序将目标图片加载到内核的内存空间,加载完成之后把数据从内核内存再复制给进程内存,如果是比较大的数据也需要等待时间。
每次IO,都要经由两个阶段:
- 第一步:将数据从磁盘文件先加载至内核内存空间(缓冲区),此步骤需要等待数据准备完成,时间较长
- 第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短
1.3 系统 I/O 模型:
1.3.1 同步/ 异步:
关注的是事件处理的消息通信机制,即在等待一件事情的处理结果时,被调用者是否提供完成通知。
同步:synchronous,调用者等待被调用者返回消息后才能继续执行,如果被调用者不提供消息返回则为同步,同步需要调用者主动询问事情是否处理完成。
异步:asynchronous,被调用者通过状态、通知或回调机制主动通知调用者,即异步会主动返回被调用者的状态给调用者。
说明:被调用者通常是内核,调用者就是web服务(Nginx、PHP)
- 同步:进程发出请求调用后,内核不提供通知机制,即文件IO处理完成后不通知进程,需要进程自己去问内核是否处理完成。
- 异步:进程发出请求调用后,内核会在调用处理完成后返回调用结果给进程,Nginx是异步的。
![image-20200713215430517](https://img-blog.csdnimg.cn/img_convert/ad89c5b35c2b1c99e0f3830cdd5baca0.png)
1.3.2 阻塞 / 非阻塞:
关注调用者在等待结果返回之前所处的状态
- 阻塞:blocking,指IO操作需要彻底完成后才返回到用户空间,调用结果返回之前,调用者被挂起,干不了别的事情。
- 非阻塞:nonblocking,指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成,最终的调用结果返回之前,调用者不会被挂起,可以去做别的事情。
![image-20200713215512650](https://img-blog.csdnimg.cn/img_convert/5927c87efe7ee719ecc5752d394d5cfd.png)
1.3.3 系统IO模型组合(面试题)
以我去吃饭为例:我点了10个包子
-
同步与异步:
我点包子之后厨师是否告诉我:
- 同步:厨师做好包子后会放到指定位置,但是做好包子之前需要自己一次次去看包子做好没有,厨师不会在包子做好之后通知我。
- 异步:厨师做好包子后告诉我包子做好放哪了。
-
阻塞与非阻塞::
我点包子后的状态:
- 阻塞:在厨师做包子期间一直在包子盘子前面等着,不能干别的事情。
- 非阻塞:点完包子就可以去干别的事情,比如去逛逛街或者买买买。
-
IO模型组合:
- 同步阻塞:我点完包子后不能去做别的事情,而且不知道包子有没有做好,需要自己一直等着并一次次的问厨师做好没有。
- 同步非阻塞:点完包子后可以去做别的事情,但是不能长时间做别的事情,因为我还是不知道包子有没有做好,也要自己一直等着并一次次的问厨师做好没有,只能抽空做点别的。
- 异步阻塞:我点完包子后不能去走做别的事情,但是厨师在做好包子后会告诉我,也就是我不用再一次次为厨师包子有没有做好了。
- 异步非阻塞:我点完包子后可以做别的事情,而且可以一直在做别的去事情,因为厨师在做好包子后会告诉我。
1.4 网络I/O模型(了解)
阻塞型、非阻塞型、复用型、信号驱动型、异步
1.4.1 同步阻塞型IO模型(blocking IO):
阻塞IO模型是最简单的IO模型,用户线程在内核进行IO操作时被阻塞
- 用户请求到达系统服务进程,然后进程通过系统调用read向内核发起IO读操作,即将用户请求由用户空间转到内核空间,内核接收到IO请求后开始从磁盘读取文件到内核内存,即在等用户请求的文件从磁盘到达内核内存后,然后将接收的数据拷贝到用户空间,然后完成read操作。
- 用户请求需要等待内核将数据读取到进程内存后,处理用户的进程才可以继续处理该请求,整个IO请求的过程中,请求进程是被阻塞的,这导致进程在发起IO请求时,不能做任何事情,而且对CPU的资源利用率不够。
优点:程序简单,在阻塞等待数据期间进程挂起,基本不会占用 CPU 资源
缺点:每个连接需要独立的进程单独处理,当并发请求量大时为了维护程序,内存、进程切换开销较大,apache的preforck使用的是这种模式。
同步阻塞:程序向内核发送IO请求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回,则进程将一直等待并不再接受新的请求,并由进程轮训查看IO是否完成,完成后进程将IO结果返回给Client,在IO没有返回期间进程不能接受其他客户的请求,而且是有进程自己去查看IO是否完成,这种方式简单,但是比较慢,用的比较少。
![image-20200713215800468](https://img-blog.csdnimg.cn/img_convert/914dd2fd65df2a9f3dab23528c13e825.png)
1.4.2 同步非阻塞型I/O模型(nonblocking IO):
用户请求进程向内核发起IO请求时立即返回,但并未读取到任何数据,进程需要不断地发起IO请求,直到数据到达进程空间的内存后,才真正读取到数据并继续执行,即前期需要一次次 “轮询”去查看请求是否数据是否准备报,但是此机制存在两个问题:1.如果有大量文件描述符都要等,那么就得一个一个的read,这会带来大量的Context Switch(read是系统调用,每调用一次就得在用户态和核心态切换一次),2.轮询的时间不好把握,这里是要猜多久之后数据才能到,等待时间设的太长,程序响应延迟就过大,但是设的太短,就会造成过于频繁的重试,干耗CPU而已,所以是比较浪费CPU的方式,因此一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
同步非阻塞:应用进程向内核发送请IO求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回IO结果,进程将不再等待,而且继续处理其他请求,但是仍然需要进程隔一段时间就要查看内核IO是否完成。
![image-20200713215835388](https://img-blog.csdnimg.cn/img_convert/465439f9b37d8155cb1e99f44d46dd6d.png)
1.4.3:IO多路复用型(IO multiplexing):
IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven IO(事件驱动IO),select/poll/epoll的好处就在于单个process就可以同时处理多个网络连接的IO。它的基本原理就是select,poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
当用户进程调用了select,那么整个进程会被block,而同时,kernel会“监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回,这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。
Apache prefork是此模式的主进程+多进程/单线程+select,work是主进程+多进程/多线程+poll模式
![image-20200713215918082](https://img-blog.csdnimg.cn/img_convert/a1313ff1293bcc9a05939e01fdf445b0.png)
1.4.4:信号驱动式IO(signal-driven IO):
信号驱动IO:signal-driven I/O
用户进程可以通过sigaction系统调用注册一个信号处理程序,然后进程可以继续向下执行,当有IO操作准备就绪时,由内核通知触发一个SIGIO信号处理程序执行,然后将用户进程所需要的数据从内核空间拷贝到用户空间,此模型的优势在于等待数据报到达期间进程不被阻塞,进程可以继续执行,只要等待来自信号处理函数的通知。
优点:进程没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此可以提高资源的利用率
缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知
异步阻塞:进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核收到进程请求后进行的IO如果不能立即返回,就由内核等待结果,直到IO完成后内核再通知进程,apache event模型就是主进程+多进程/多线程+信号驱动
![image-20200713220220377](https://img-blog.csdnimg.cn/img_convert/eb431d934842657087b2f324890b6ce9.png)
1.4.5:异步(非阻塞) IO(asynchronous IO):
相对于同步IO,异步IO不是顺序执行,用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情,等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知,异步非阻塞IO的两个阶段,进程都是非阻塞的。
Linux提供了AIO库函数实现异步,但是用的很少,目前有很多开源的异步IO库,例如libevent、libev、libuv等,异步过程如下图所示:
异步非阻塞:程序进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核调用的IO如果不能立即返回,内核会继续处理其他事物,直到IO完成后将结果通知给内核,内核在将IO完成的结果返回给进程,期间进程可以接受新的请求,内核也可以处理新的事物,因此相互不影响,可以实现较大的同时并实现较高的IO复用,因此异步非阻塞使用最多的一种通信方式,nginx就是是异步非阻塞模型
![image-20200713220308906](https://img-blog.csdnimg.cn/img_convert/e5fc4d0e62d52bf9c06e433ed61d875c.png)
1.4.6:IO对比:
这五种网络 I/O 模型中,越往后,阻塞越少,理论上效率也是最优前四种属于同步 I/O,因为其中真正的 I/O 操作(recvfrom)将阻塞进程/线程,只有异步 I/O 模型才与 POSIX 定义的异步 I/O 相匹配。
![image-20200713220322055](https://img-blog.csdnimg.cn/img_convert/a166fbf0782dffd2471b1255fa46ae43.png)
1.4.7:实现方式:
Nginx支持在多种不同的操作系统实现不同的事件驱动模型,但是其在不同的操作系统甚至是不同的系统版本上面的实现方式不尽相同,主要有以下实现方式:
-
select:(用的较少)
select库是在linux和windows平台都基本支持的 事件驱动模型库,并且在接口的定义也基本相同,只是部分参数的含义略有差异,最大并发限制1024,是最早期的事件驱动模型。
-
poll :(不推荐使用,降低性能)
在Linux 的基本驱动模型,windows不支持此驱动模型,是select的升级版,取消了最大的并发限制,在编译nginx的时候可以使用–with-poll_module和–without-poll_module这两个指定是否编译select库。
-
epoll:
epoll是库是Nginx服务器支持的最高性能的事件驱动库之一,是公认的非常优秀的事件驱动模型,它和select和poll有很大的区别,epoll是poll的升级版,但是与poll的效率有很大的区别.
epoll的处理方式是创建一个待处理的事件列表,然后把这个列表发给内核,返回的时候在去轮训检查这个表,以判断事件是否发生,epoll支持一个进程打开的最大事件描述符的上限是系统可以打开的文件的最大数,同时epoll库的IO效率不随描述符数目增加而线性下降,因为它只会对内核上报的“活跃”的描述符进行操作。
-
rtsig:
不是一个常用事件驱动,最大队列1024,不是很常用
-
kqueue
用于支持BSD系列平台的高效事件驱动模型,主要用在FreeBSD 4.1及以上版本、OpenBSD 2.0级以上版本,NetBSD级以上版本及Mac OS X 平台上,该模型也是poll库的变种,因此和epoll没有本质上的区别,都是通过避免轮训操作提供效率。
-
/dev/poll:
用于支持unix衍生平台的高效事件驱动模型,主要在Solaris 平台、HP/UX,该模型是sun公司在开发Solaris系列
平台的时候提出的用于完成事件驱动机制的方案,它使用了虚拟的/dev/poll设备,开发人员将要见识的文件描述符加入这个设备,然后通过ioctl()调用来获取事件通知,因此运行在以上系列平台的时候请使用/dev/poll事件驱动机制。
-
eventport:
该方案也是sun公司在开发Solaris的时候提出的事件驱动库,只是Solaris 10以上的版本,该驱动库看防止内核崩溃等情况的发生。
-
Iocp:
Windows系统上的实现方式,对应第5种(异步I/O)模型。
1.4.8:常用模型汇总:
\ |
select |
poll |
epoll |
操作方式 |
遍历 |
遍历 |
回调 |
底层实现 |
数组 |
链表 |
哈希表 |
IO效率 |
每次调用都进行线性遍历,时间复杂度为O(n) |
每次调用都进行线性遍历,时间复杂度为O(n) |
时间通知方式,每当fd就绪。系统注册的回调函数就会被调用,将就绪fd放到rdllist里面,时间复杂度为O(1) |
最大连接数 |
1024(x86)或2048(x64) |
无上限 |
无上限 |
fd拷贝 |
每次调用select,都需要把fd集合从用户态拷贝到内核态 |
每次调用poll,都需要把fd集合从用户态拷贝到内核态 |
调用epoll_ctl时拷贝进内核并保存,之后每次epoll_wait不拷贝 |
说明:O(1) 表示算法的运行时间为常量,即无论是1000个还是10000个,耗费时间一样
O(n): 表示该算法是线性算法
fd表示事件描述符
1.4.9:常用模型通知对比
水平触发-- 多次通知,需要关心数据是否取完,即数据取走之后即不再通知进程,以避免重复多次无效通知,通知效率较低。
边缘触发-- 一次通知,需要关心数据是否取走,即只通知一次怎么保证数据被进程成功取走了,以避免数据丢失,通知效率较高。
![image-20200713221835046](https://img-blog.csdnimg.cn/img_convert/112061e5b462964ce62866f26ffc8ab9.png)
Select:
POSIX所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理
缺点
- 单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义FD_SETSIZE,再重新编译内核实现,但是这样也会造成效率的降低。
- 单个进程可监视的fd数量被限制,默认是1024,修改此值需要重新编译内核。
- 对socket是线性扫描,即采用轮询的方法,效率较低。
- select 采取了内存拷贝方法来实现内核将 FD 消息通知给用户空间,这样一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大。
poll:
- 本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态。
- 其没有最大连接数的限制,原因是它是基于链表来存储的。
- 大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
- poll特点是“水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
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.4.10:MMAP介绍:
mmap(memory mapping)系统调用使得进程之间通过映射同一个普通文件实现共享内存,普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问。
1.4.10.1:传统方式copy数据:
![image-20200713222215704](https://img-blog.csdnimg.cn/img_convert/c0f3be4552efe60de59c41653868bb6c.png)
1.4.10.2:mmap方式:
![image-20200713222228283](https://img-blog.csdnimg.cn/img_convert/c0b4fb6763ce0dbf1708d7b6242547c4.png)
二 Nginx基础
Nginx:engine X ,2002年开始开发,2004年开源,2019年3月11日,Nginx公司被F5 Networks以6.7亿美元收购,Nginx官网:http://nginx.org,Nginx 商业版为Nginx Plus:https://www.nginx.com/products/nginx/
Nginx 则是免费的、开源的、高性能的HTTP和反向代理服务器、邮件代理服务器、以及TCP/UDP代理服务器,解决C10K问题(10K Connections),http://www.ideawu.net/blog/archives/740.html
nginx的其它的二次发行版:
- Tengine:由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性,Tengine的性能和稳定性已经在大型的网站如淘宝网,天猫商城等得到了很好的检验,它的最终目标是打造一个高效、稳定、安全、易用的Web平台,从2011年12月开始,Tengine成为一个开源项目,官网 http://tengine.taobao.org/
- OpenResty:基于 Nginx 与 Lua 语言的高性能 Web 平台, 章亦春团队开发,官网:http://openresty.org/cn/
2.1 Nginx功能介绍
2.1.1 基础特性
特性:
- 模块化设计,较好的扩展性
- 高可靠性(很少宕机)
- 支持热部署:不停机更新配置文件,升级版本,更换日志文件
- 低内存消耗:10000个keep-alive连接模式下的非活动连接,仅需2.5M内存
- event-driven,aio,mmap,sendfile
基本功能:
- 静态资源的web服务器
- http协议反向代理服务器
- pop3/imap4协议反向代理服务器
- FastCGI(LNMP),uWSGI(python)等协议
- 模块化(非DSO),如zip,SSL模块
2.1.2 和web服务相关的功能
- 虚拟主机(server)
- 支持 keep-alive 和管道连接(利用一个连接做多次请求)
- 访问日志(支持基于日志缓冲提高其性能)
- url rewirte
- 路径别名
- 基于IP及用户的访问控制
- 支持速率限制及并发数限制
- 重新配置和在线升级而无须中断客户的工作进程
2.2 Nginx组织结构
web请求处理机制:
-
多进程方式:服务器每接收到一个客户端请求就有服务器的主进程生成一个子进程响应客户端,直到用户关闭连接,这样的优势是处理速度快,各子进程之间相互独立,但是如果访问过大会导致服务器资源耗尽而无法提供请求。
-
多线程方式:与多进程方式类似,但是每收到一个客户端请求会有服务进程派生出一个线程来个客户方进行交互,一个线程的开销远远小于一个进程,因此多线程方式在很大程度减轻了web服务器对系统资源的要求,但是多线程也有自己的缺点,即当多个线程位于同一个进程内工作的时候,可以相互访问同样的内存地址空间,所以他们相互影响,另外一旦主进程挂掉则所有子线程都不能工作了,IIS服务器使用了多线程的方1式,需要间隔一段时间就重启一次才能稳定。
2.2.1 组织模型
Nginx是多进程组织模型,而且是一个由Master主进程和Worker工作进程组成。
![image-20200713222543370](https://img-blog.csdnimg.cn/img_convert/a91880506f06216de4b7095ee7bbc69a.png)
主进程(master process)的功能:
- 读取Nginx 配置文件并验证其有效性和正确性
- 建立、绑定和关闭socket连接
- 按照配置生成、管理和结束工作进程
- 接受外界指令,比如重启、升级及退出服务器等指令
- 不中断服务,实现平滑升级,重启服务并应用新的配置
- 开启日志文件,获取文件描述符
- 不中断服务,实现平滑升级,升级失败进行回滚处理
- 编译和处理perl脚本
工作进程(woker process)的功能:
- 接受处理客户的请求
- 将请求依次送入各个功能模块进行处理
- IO调用,获取响应数据
- 与后端服务器通信,接收后端服务器的处理结果
- 缓存数据,访问缓存索引,查询和调用缓存数据
- 发送请求结果,响应客户的请求
- 接收主程序指令,比如重启、升级和退出等
![image-20200713222652670](https://img-blog.csdnimg.cn/img_convert/3492bc282888724881a27e8cb84cdd3c.png)
2.2.2:进程间通信:
工作进程是有主进程生成的,主进程使用fork()函数,在Nginx服务器启动过程中主进程根据配置文件决定启动工作进程的数量,然后建立一张全局的工作表用于存放当前未退出的所有的工作进程,主进程生成工作进程后会将新生成的工作进程加入到工作进程表中,并建立一个单向的管道并将其传递给工作进程,该管道与普通的管道不同,它是由主进程指向工作进程的单向通道,包含了主进程向工作进程发出的指令、工作进程ID、工作进程在工作进程表中的索引和必要的文件描述符等信息。
主进程与外界通过信号机制进行通信,当接收到需要处理的信号时,它通过管道向相关的工作进程发送正确的指令,每个工作进程都有能力捕获管道中的可读事件,当管道中有可读事件的时候,工作进程就会从管道中读取并解析指令,然后采取相应的执行动作,这样就完成了主进程与工作进程的交互。
说明:
- 工作进程之间的通信原理基本上和主进程与工作进程之间的通信是一样的,只要工作进程之间能够取得彼此的信息,建立管道即可通信,但是由于工作进程之间是完全隔离的,因此一个进程想要知道另外一个进程的状态信息就只能通过主进程来设置了。
- 为了实现工作进程之间的交互,主进程在生成工作进程之后,在工作进程表中进行遍历,将该新进程的ID以及针对该进程建立的管道句柄传递给工作进程中的其他进程,为工作进程之间的通信做准备,当工作进程1向工作进程2发送指令的时候,首先在主进程给它的其他工作进程工作信息中找到2的进程ID,然后将正确的指令写入指向进程2的管道,工作进程2捕获到管道中的事件后,解析指令并进行相关操作,这样就完成了工作进程之间的通信。
- 用户的请求只能被一个进程处理一次
![image-20200713222844493](https://img-blog.csdnimg.cn/img_convert/cb08bef4c16b681bd0240a9c50c2302e.png)
2.3:Nginx模块介绍:
-
核心模块:是 Nginx 服务器正常运行 必不可少 的模块,提供 错误日志记录 、 配置文件解析 、 事件驱动机制 、进程管理 等核心功能
-
标准HTTP模块:提供 HTTP 协议解析相关的功能,比如: 端口配置 、 网页编码设置 、 HTTP响应头设置 等等
-
可选HTTP模块:主要用于扩展标准的 HTTP 功能,让 Nginx 能处理一些特殊的服务,比如: Flash 多媒体传输 、解析 GeoIP 请求、 网络传输压缩 、 安全协议 SSL 支持等
-
邮件服务模块:主要用于支持 Nginx 的 邮件服务 ,包括对 POP3 协议、 IMAP 协议和 SMTP协议的支持 (基本用不上)
-
第三方模块:是为了扩展 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_*
第三方模块
![image-20200713223114039](https://img-blog.csdnimg.cn/img_convert/2448f97bdc4dfb8b9fa7bb74760cf89d.png)
2.4 Nginx安装
Nginx的安装版本分为Mainline version(主要开发版本,其实就是还处于开发版)、Stable version(当前最新稳定版)和Legacy versions(旧的稳定版)
Nginx安装可以使用yum或源码安装,但是推荐使用源码,一是yum的版本比较旧,二是编译安装可以更方便自定义相关路径,三是使用源码编译可以自定义相关功能,更方便业务的上的使用,源码安装需要提前准备标准的编译器
GCC的全称是(GNU Compiler collection),其有GNU开发,并以GPL即LGPL许可,是自由的类UNIX即苹果电脑Mac OS X操作系统的标准编译器,因为GCC原本只能处理C语言,所以原名为GNU C语言编译器,后来得到快速发展,可以处理C++,Fortran,pascal,objective-C,java以及Ada等其他语言,此外还需要Automake工具,以完成自动创建Makefile的工作,Nginx的一些模块需要依赖第三方库,比如pcre(支持rewrite),zlib(支持gzip模块)和openssl(支持ssl模块)等。
2.4.1 Nginx yum安装
需要提前配置好epel源
官方提供的仓库:http://nginx.org/en/linux_packages.html#RHEL-CentOS
[root@s1 ~]# yum install epel-release -y
#或者使用官方提供的仓库,里面有新版的nginx
[root@Centos7 ~]#vim /etc/yum.repos.d/nginx.repo
[nginx-stable]
name=nginx stable repo
baseurl=http://nginx.org/packages/centos/$releasever/$basearch/
gpgcheck=1
enabled=1
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[nginx-mainline]
name=nginx mainline repo
baseurl=http://nginx.org/packages/mainline/centos/$releasever/$basearch/
gpgcheck=1
enabled=0
gpgkey=https://nginx.org/keys/nginx_signing.key
module_hotfixes=true
[root@Centos7 ~]# yum install -y nginx
[root@Centos7 ~]# rpm -ql nginx
/etc/logrotate.d/nginx
/etc/nginx/fastcgi.conf
/etc/nginx/fastcgi.conf.default
/etc/nginx/fastcgi_params
/etc/nginx/fastcgi_params.default
/etc/nginx/koi-utf
/etc/nginx/koi-win
/etc/nginx/mime.types
/etc/nginx/mime.types.default
/etc/nginx/nginx.conf
/etc/nginx/nginx.conf.default
/etc/nginx/scgi_params
/etc/nginx/scgi_params.default
/etc/nginx/uwsgi_params
/etc/nginx/uwsgi_params.default
/etc/nginx/win-utf
/usr/bin/nginx-upgrade
/usr/lib/systemd/system/nginx.service
/usr/lib64/nginx/modules
/usr/sbin/nginx
/usr/share/doc/nginx-1.18.0
.................
/var/lib/nginx
/var/lib/nginx/tmp
/var/log/nginx
[root@Centos7 ~]# which nginx
/usr/sbin/nginx
2.4.1.1:检查安装:
查看nginx安装包信息
[root@Centos7 ~]#rpm -qi nginx
Name : nginx
Epoch : 1
Version : 1.16.1
Release : 1.el7
Architecture: x86_64
Install Date: Mon 13 Jul 2020 10:37:26 PM CST
Group : Unspecified
Size : 1689960
License : BSD
Signature : RSA/SHA256, Fri 04 Oct 2019 06:38:33 AM CST, Key ID 6a2faea2352c64e5
Source RPM : nginx-1.16.1-1.el7.src.rpm
Build Date : Thu 03 Oct 2019 01:15:40 PM CST
Build Host : buildvm-13.phx2.fedoraproject.org
Relocations : (not relocatable)
Packager : Fedora Project
Vendor : Fedora Project
URL : http://nginx.org/
Bug URL : https://bugz.fedoraproject.org/nginx
Summary : A high performance web server and reverse proxy server
Description :
Nginx is a web server and a reverse proxy server for HTTP, SMTP, POP3 and IMAP protocols, with a strong focus on high concurrency, performance and low memory usage.
2.4.1.2 查看帮助
使用安装完成的二进制文件nginx
[root@Centos7 ~]# nginx -h
nginx version: nginx/1.16.1
Usage: nginx [-?hvVtTq] [-s signal] [-c filename] [-p prefix] [-g directives]
Options:
-?,-h : this help
-v : show version and exit
-V : show version and configure options then exit #显示版本和编译参数
-t : test configuration and exit #测试配置文件是否异常
-T : test configuration, dump it and exit #测试并打印
-q : suppress non-error messages during configuration testing #静默模式
-s signal : send signal to a master process: stop, quit, reopen, reload #发送信号
-p prefix : set prefix path (default: /usr/share/nginx/) #指定Nginx 目录
-c filename : set configuration file (default: /etc/nginx/nginx.conf) #配置文件路径
-g directives : set global directives out of configuration file #设置全局指令
2.4.1.3 验证Nginx
[root@Centos7 ~]#nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok
nginx: configuration file /etc/nginx/nginx.conf test is successful
[root@Centos7 ~]#nginx -V
nginx version: nginx/1.16.1
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=/usr/share/nginx --sbin-path=/usr/sbin/nginx --modules-path=/usr/lib64/nginx/modules --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --http-client-body-temp-path=/var/lib/nginx/tmp/client_body --http-proxy-temp-path=/var/lib/nginx/tmp/proxy --http-fastcgi-temp-path=/var/lib/nginx/tmp/fastcgi --http-uwsgi-temp-path=/var/lib/nginx/tmp/uwsgi --http-scgi-temp-path=/var/lib/nginx/tmp/scgi --pid-path=/run/nginx.pid --lock-path=/run/lock/subsys/nginx --user=nginx --group=nginx --with-file-aio --with-ipv6 --with-http_ssl_module --with-http_v2_module --with-http_realip_module --with-stream_ssl_preread_module --with-http_addition_module --with-http_xslt_module=dynamic --with-http_image_filter_module=dynamic --with-http_sub_module --with-http_dav_module --with-http_flv_module --with-http_mp4_module --with-http_gunzip_module --with-http_gzip_static_module --with-http_random_index_module --with-http_secure_link_module --with-http_degradation_module --with-http_slice_module --with-http_stub_status_module --with-http_perl_module=dynamic --with-http_auth_request_module --with-mail=dynamic --with-mail_ssl_module --with-pcre --with-pcre-jit --with-stream=dynamic --with-stream_ssl_module --with-google_perftools_module --with-debug --with-cc-opt='-O2 -g -pipe -Wall -Wp,-D_FORTIFY_SOURCE=2 -fexceptions -fstack-protector-strong --param=ssp-buffer-size=4 -grecord-gcc-switches -specs=/usr/lib/rpm/redhat/redhat-hardened-cc1 -m64 -mtune=generic' --with-ld-opt='-Wl,-z,relro -specs=/usr/lib/rpm/redhat/redhat-hardened-ld -Wl,-E'
2.4.1.4 Nginx启动脚本
[root@s1 ~]# cat /usr/lib/systemd/system/nginx.service
[Unit]
Description=The nginx HTTP and reverse proxy server
After=network.target remote-fs.target nss-lookup.target
[Service]
Type=forking
PIDFile=/run/nginx.pid
# Nginx will fail to start if /run/nginx.pid already exists but has the wrong
# SELinux context. This might happen when running `nginx -t` from the cmdline.
# https://bugzilla.redhat.com/show_bug.cgi?id=1268621
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/sbin/nginx -t
ExecStart=/usr/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true
[Install]
WantedBy=multi-user.target
2.4.1.5 配置Nginx
默认配置文件:/etc/nginx/nginx.conf,,默认配置如下:
user nginx;
worker_processes auto;
error_log /var/log/nginx/error.log;
pid /run/nginx.pid;
include /usr/share/nginx/modules/*.conf;
events {
worker_connections 1024;
}
http {
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 /var/log/nginx/access.log main;
sendfile on;
tcp_nopush on;
tcp_nodelay on;
keepalive_timeout 65;
types_hash_max_size 2048;
include /etc/nginx/mime.types;
default_type application/octet-stream;
include /etc/nginx/conf.d/*.conf;
server {
listen 80 default_server;
listen [::]:80 default_server;
server_name _;
root /usr/share/nginx/html;
include /etc/nginx/default.d/*.conf;
location / {
}
error_page 404 /404.html;
location = /40x.html {
}
error_page 500 502 503 504 /50x.html;
location = /50x.html {
}
}
}
2.4.1.6 启动Nginx
[root@Centos7 ~]#systemctl start nginx
[root@Centos7 ~]#systemctl status nginx
● nginx.service - The nginx HTTP and reverse proxy server
Loaded: loaded (/usr/lib/systemd/system/nginx.service; disabled; vendor preset: disabled)
Active: active (running) since Mon 2020-07-13 22:43:52 CST; 8s ago
Process: 3141 ExecStart=/usr/sbin/nginx (code=exited, status=0/SUCCESS)
Process: 3138 ExecStartPre=/usr/sbin/nginx -t (code=exited, status=0/SUCCESS)
Process: 3136 ExecStartPre=/usr/bin/rm -f /run/nginx.pid (code=exited, status=0/SUCCESS)
Main PID: 3143 (nginx)
CGroup: /system.slice/nginx.service
├─3143 nginx: master process /usr/sbin/nginx
├─3144 nginx: worker process
└─3145 nginx: worker process
Jul 13 22:43:52 Centos7.8 systemd[1]: Starting The nginx HTTP and reverse proxy server...
Jul 13 22:43:52 Centos7.8 nginx[3138]: nginx: the configuration file /etc/nginx/nginx.conf ... ok
Jul 13 22:43:52 Centos7.8 nginx[3138]: nginx: configuration file /etc/nginx/nginx.conf test...ful
Jul 13 22:43:52 Centos7.8 systemd[1]: Started The nginx HTTP and reverse proxy server.
Hint: Some lines were ellipsized, use -l to show in full.
[root&