web
web框图
Apache的MPM模型
web性能相关
IO模型及实现方式
MMAP
web框图
web服务介绍
Netcraft公司于1994年底在英国成立,多年来一直致力于互联网市场以及在线安全方面的咨询服务,其中在国际上最具影响力的当属其针对网站服务器,域名解析/主机提供商,以及SSL市场所做的客观严谨的分析研究。
https://news.netcraft.com/
Apache的MPM工作模式
Apache prefork模型
预派生模式,有一个主控制进程,然后生成多个子进程,使用select模型,最大并发1024,每个子进程有一个独立的线程响应用户请求,相对比较占用内存,但是比较稳定,可以设置最大和最小进程数,是最古老的一种模式,也是最稳定的模式,适用于访问量不是很大的场景。
- 优点:稳定 .
- 缺点:大量用户访问慢,占用资源,1024个进程不适用于高并发场景.
Apache woker模型
一种多进程和多线程混合的模型,有一个控制进程,启动多个子进程,每个子进程里面包含固定的线程,使用线程程来处理请求,当线程不够使用的时候会再启动一个新的子进程,然后在进程里面再启动线程处理请求,由于其使用了线程处理请求,因此可以承受更高的并发。
- 优点:相比prefork 占用的内存较少,可以同时处理更多的请求
- 缺点:使用keepalive的长连接方式,某个线程会一直被占据,即使没有传输数据,也需要一直等待到超时才会被释放。如果过多的线程,被这样占据,也会导致在高并发场景下的无服务线程可用。(该问题在prefork模式下,同样会发生)
keepalive的长连接方式:当用户连接到服务器时,希望一直与服务器保持连接状态,因此在web server配置时一般会将长连接选项开启。
用户与服务器建立连接:
①登录
②建立TCP的三次握手
==>如果不开启长连接,将一会儿建立一次连接,这样会造成资源浪费,避免这种资源浪费就是开启长连接。
长连接时间一般是60s,即60s之后,用户不刷新,不做任何操作,服务器将断开TCP连接。
Apache event模型
Apache中最新的模式,2012年发布的apache 2.4.X系列正式支持event 模型,属于事件驱动模型(epoll),每个进程响应多个请求,在现在版本里的已经是稳定可用的模式。它和worker模式很像,最大的区别在于,event模型解决了keepalive场景下,长期被占用的线程的资源浪费问题(某些线程因为被keepalive,空挂在哪里等待,中间几乎没有请求过来,甚至等到超时)。event MPM中,会有一个专门的线程来管理这些keepalive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放。这样增强了高并发场景下的请求处理能力。
-
优点:单线程响应多请求,占据更少的内存,高并发下表现更优秀,会有一个专门的线程来管理keep-alive类型的线程,当有真实请求过来的时候,将请求传递给服务线程,执行完毕后,又允许它释放
-
缺点:没有线程安全控制
event模型解决空连接问题的方案:每个子进程中设置一个监听线程。
监听线程的作用:
①分配用户的请求,把用户请求分配给空的工作线程。
②将处于keepalive的用户转接至自己(即监听线程)处理,如果用户在keepalive期间再做请求时,监听线程再将请求分配给空的工作线程。
event模型最大缺点:当监听线程故障时,监听线程所处的进程中的所有用户请求都会被中断。
Apache的简单解决模式:Apache可设置当达到多少个处理请求后,将子进程回收,然后重新生成一个子进程,生产环境中如果使用Apache的话,一般会配置。
Nginx-高性能的web服务端
Nginx是由1994年毕业于俄罗斯国立莫斯科鲍曼科技大学的同学为俄罗斯rambler.ru公司开发的,开发工作最早从2002年开始,第一次公开发布时间是2004年10月4日,版本号是0.1.0,官网地址 www.nginx.org
Nginx历经十几年的迭代更新(https://nginx.org/en/CHANGES),目前功能已经非常完善且运行稳定,另外Nginx的版本分为开发版、稳定版和过期版,Nginx以功能丰富著称,它即可以作为http服务器,也可以作为反向代理服务器或者邮件服务器,能够快速的响应静态网页的请求,支持FastCGI/SSL/Virtual Host/URLRwrite/Gzip/HTTP Basic Auth/http或者TCP的负载均衡(1.9版本以上且开启stream模块)等功能,并且支持第三方的功能扩展。
为什么使用Nginx: 天猫 淘宝 小米 163 京东新浪等一线互联网公司都在用Nginx或者进行二次开发基于Nginx的访问流程如下:
用户访问体验统计
互联网存在用户速度体验的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% 将放弃交易
性能影响
◇ 有很多研究都表明,性能对用户的行为有很大的影响:
◇ 79%的用户表示不太可能再次打开一个缓慢的网站
◇ 47%的用户期望网页能在2秒钟以内加载
◇ 40%的用户表示如果加载时间超过三秒钟,就会放弃这个网站
◇ 页面加载时间延迟一秒可能导致转换损失7%,页面浏览量减少11%
◇ 8秒定律:用户访问一个网站时,如果等待网页打开的时间超过8秒,会有超过30%的用户放弃等待
影响用户体验的几个因素
◇ 客户端相关的因素:
①客户端硬件配置
②客户端网络速率
③客户端与服务端距离
◇ 服务端相关的因素:
④服务端网络速率
服务器端网络速率主要是针对的机房的网络出口的带宽。
==>服务器带宽的大小会影响服务器的响应速度,带宽在服务器租用的费用中占不小的比例,需要根据业务类型、用户访问量、响应报文大小等做相应计算。
⑤服务端硬件配置
⑥服务端架构设计
⑦服务端应用程序工作模式
⑧服务端并发数量
服务器的并发数:前面由负载均衡调度,此时考虑的服务器并发数是当前服务器主机分配的并发数。
比如:Java程序服务器顶不住1000个并发访问,一般1个Nginx和2个Tomcat(标配)。
⑨服务端响应文件大小及数量
⑩服务端I/O压力
此处服务器的IO压力主要是针对磁盘或者后端存储。
而后端存储的网卡一般是万兆网卡,网络传输速率大概可以达到1G左右。
==>如果1G速率还是不够,多网卡绑定或者后端存储多设置几个ip。
服务端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,因此可以忽略不计。
常见的机械磁盘平均寻道时间值:
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
综上:
==>优先考虑固态磁盘。
==>机械磁盘:15000转/min的故障率将很高.
存储服务器的机械磁盘的转速一般是10000转/min.
web服务器的机械磁盘的转速倾向于使用15000zhuan/min.
IO模型
系统IO模型
★★★★★★ 注:系统IO模型 ★★★★★★(运维关注系统IO模型)
- 运维管理的是某个服务器,在某个服务器让应用程序怎么读取某个文件或者写入某个文件。
同步/异步:关注的是事件处理的消息通信机制,即在等待一件事情的处理结果时,被调用者是否提供完成通知。
-
关键词:消息通知、被调用者
对于计算机而言:被调用者通常是内核;调用者是应用程序(eg:php,Nginx…) -
同步:synchronous,调用者等待被调用者返回消息后才能继续执行,如果被调用者不提供消息通知机制则为同步,同步需要调用者主动询问事情是否处理完成。
-
异步:asynchronous,被调用者通过状态、通知或回调机制主动通知调用者被调用者的运行状态,即被调用者提供消息通知机制为异步。
同步:进程发出请求调用后,等内核返回响应以后才继续下一个请求,即如果内核一直不返回数据,那么进程就一直等。
异步:进程发出请求调用后,不等内核返回响应,接着处理下一个请求,Nginx是异步的。
阻塞/非阻塞:关注调用者在等待结果返回之前所处的状态
- 关键词:调用者、所处状态、调用过程中
- 阻塞:blocking,指IO操作需要彻底完成后才返回到用户空间,调用结果返回之前,调用者被挂起,干不了别的事情。
- 非阻塞:nonblocking,指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成,最终的调用结果返回之前,调用者不会被挂起,可以去做别的事情。
网络IO模型
☆ 网络IO模型会涉及跨服务器,跨服务器就涉及到网络怎么调用。(开发关注网络IO模型)
☆ 网络IO模型:阻塞型、非阻塞型、复用型、信号驱动型、异步
☆ 【同步阻塞型IO模型(blocking IO)】
阻塞IO模型是最简单的IO模型,用户线程在内核进行IO操作时被阻塞 用户线程通过系统调用read发起IO读操作,由用户空间转到内核空间。内核等到数据包到达后,然后将接收的数据拷贝到用户空间,完成read操作 用户需要等待read将数据读取到buffer后,才继续处理接收的数据。整个IO请求的过程中,用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够 优点:程序简单,在阻塞等待数据期间进程/线程挂起,基本不会占用 CPU 资源 缺点:每个连接需要独立的进程/线程单独处理,当并发请求量大时为了维护程序,内存、线程切换开销较大,apache 的preforck使用的是这种模式。
- 同步阻塞:程序向内核发送IO请求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回,则进程将一直等待并不再接受新的请求,并由进程轮训查看IO是否完成,完成后进程将IO结果返回给Client,在IO没有返回期间进程不能接受其他客户的请求,而且是有进程自己去查看IO是否完成,这种方式简单,但是比较慢,用的比较少。
☆ 【同步非阻塞型I/O模型(nonblocking IO)】
用户线程发起IO请求时立即返回。但并未读取到任何数据,用户线程需要不断地发起IO请求,直到数据到达后,才真正读取到数据,继续执行。即 “轮询”机制存在两个问题:如果有大量文件描述符都要等,那么就得一个一个的read。这会带来大量的Context Switch(read是系统调用,每调用一次就得在用户态和核心态切换一次)。轮询的时间不好把握。这里是要猜多久之后数据才能到。等待时间设的太长,程序响应延迟就过大;设的太短,就会造成过于频繁的重试,干耗CPU而已,是比较浪费CPU的方式,一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞IO这一特性。
- 同步非阻塞:程序向内核发送请IO求后一直等待内核响应,如果内核处理请求的IO操作不能立即返回IO结果,进程将不再等待,而且继续处理其他请求,但是仍然需要进程隔一段时间就要查看内核IO是否完成。
☆ 【IO多路复用型(IO multiplexing)】
IO multiplexing就是我们说的select,poll,epoll,有些地方也称这种IO方式为event driven 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模式
☆ 【信号驱动式IO(signal-driven IO)】
信号驱动IO:signal-driven I/O 用户进程可以通过sigaction系统调用注册一个信号处理程序,然后主程序可以继续向下执行,当有IO操作准备就绪时,由内核通知触发一个SIGIO信号处理程序执行,然后将用户进程所需要的数据从内核空间拷贝到用户空间, 此模型的优势在于等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处理函数的通知。 优点:线程并没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此可以提高资源的利用率 缺点:信号 I/O 在大量 IO 操作时可能会因为信号队列溢出导致没法通知。
- 异步阻塞:程序进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核收到进程请求后进行的IO如果不能立即返回,就由内核等待结果,直到IO完成后内核再通知进程,apache event模型就是主进程+多进程/多线程+信号驱动
☆ 【异步(非阻塞) IO(asynchronous IO)】
相对于同步IO,异步IO不是顺序执行。用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情。等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知。IO两个阶段,进程都是非阻塞的。 Linux提供了AIO库函数实现异步,但是用的很少。目前有很多开源的异步IO库,例如libevent、libev、libuv。
- 异步非阻塞:程序进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核调用的IO如果不能立即返回,内核会继续处理其他事物,直到IO完成后将结果通知给内核,内核在将IO完成的结果返回给进程,期间进程可以接受新的请求,内核也可以处理新的事物,因此相互不影响,可以实现较大的同时并实现较高的IO复用,因此异步非阻塞使用最多的一种通信方式,nginx是异步非阻塞。
IO模型的实现方式
☆ 【1、select】
select库是在linux和windows平台都基本支持的 事件驱动模型库,并且在接口的定义也基本相同,只是部分参数的含义略有差异,最大并发限制1024,是最早期的事件驱动模型。
- ①select支持所有平台
- ②最大并发1024
- ③水平触发
- ④Apache prefork模型使用的select模型
☆ 【2、poll】
在Linux 的基本驱动模型,windows不支持此驱动模型,是select的升级版,取消了最大的并发限制,在编译nginx的时候可以使用–with-poll_module和–without-poll_module这两个指定是否编译select库。
- ①select的升级版、无最大并发连接数限制
☆ 【3、epoll】
epoll是库是Nginx服务器支持的最高性能的事件驱动库之一,是公认的非常优秀的事件驱动模型,它和select和poll有很大的区别,epoll是poll的升级版,但是与poll的效率有很大的区别。epoll的处理方式是创建一个待处理的事件列表,然后把这个列表发给内核,返回的时候在去轮询检查这个表,以判断事件是否发生,epoll支持一个进程打开的最大事件描述符的上限是系统可以打开的文件的最大数,同时epoll库的IO效率不随描述符数目增加而线性下降,因为它只会对内核上报的"活跃"的描述符进行操作。
- 目前基本使用epoll模型
系统可以打开的文件的最大数查看:
[root@centos7-27 doc]# ulimit -n
1024 需要优化
☆ 【4、汇总】
- | select | poll | epoll |
---|---|---|---|
操作方式 | 遍历 | 遍历 | 回调 |
底层实现 | 数组 | 链表 | 哈希表 |
IO效率 | 每次调用都进行线性遍历,时间复杂度为O(n) | 每次调用都进行线性遍历,时间复杂度为O(n) | 事件通知方式,每当fd就绪,系统注册的回调函数就会被调用,将就绪fd放到rdllist里面,时间复杂度为O(1) |
最大并发数 | 1024(X86)或2048(X64) | 无上限 | 无上限 |
fd拷贝 | 每次调用select,都需要把fd集合从用户态拷贝到内核态 | 每次调用poll,都需要把fd集合从用户态拷贝到内核态 | 调用epoll_ct时拷贝进内核并保存,之后每次epoll_wait不拷贝 |
☆ 【5、对比】
通知机制:
- 水平触发-- 多次通知,需要关心数据是否取完以避免重复通知,效率较低。
- 边缘触发-- 一次通知,需要关心数据是否取走以避免数据丢失,效率较高。
对比:
- 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减少复制开销
MMAP介绍
mmap(memory mapping)系统调用使得进程之间通过映射同一个普通文件实现共享内存,普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问。
- Nginx中mmap建议开启
传统文件访问
UNIX访问文件的传统方法是用open打开它们,如果有多个进程访问同一个文件,则每一个进程在自己的地址空间都包含有该文件的副本,这不必要地浪费了存储空间。下图说明了两个进程同时读一个文件的同一页的情形。系统要将该页从磁盘读到高速缓冲区中,每个进程再执行一个存储器内的复制操作将数据从高速缓冲区读到自己的地址空间。
共享存储映射
共享存储映射方法:进程A和进程B都将该页映射到自己的地址空间,当进程A第一次访问该页中的数据时,它生成一个缺页中断,内核此时读入这一页到内存并更新页表使之指向它。以后,当进程B访问同一页面而出现缺页中断时,该页已经在内存,内核只需要将进程B的页表登记项指向次页即可。如下图所示:
mmap()及其相关系统调用
mmap()系统调用使得进程之间通过映射同一个普通文件实现共享内存。普通文件被映射到进程地址空间后,进程可以向访问普通内存一样对文件进行访问,不必再调用read(),write()等操作。