原理

  

进程与线程

  
  进程是具有一定独立功能的在计算机运行的程序的实体,进程开始是程序运行的基本单位,在支持线程的系统下,线程才是基本的运行单位,进程作为线程的容器。程序本身是指令,数据和数据结构的集合,进程才是真正运行的实例。一个程序可能会与多个进程存在关系,每个进程都可以以同步或者异步的方式独立运行。现代计算机系统可以在同一段的时间内将多个程序进行加载,借由时分复用在一个处理器上表现出多个进程同时运行的感觉——即宏观上的并行;微观上串行。使用多线程技术的操作系统,程序的平行线程,可在多 CPU 主机或网络上真正同时运行,变相提高了芯片的性能。
  

web服务的工作方式

  

常见的3中模型

  
  web服务器响应用户的请求,必须以某种方式工作在套接字上。一般情况下会采用以下3中方式:多进程方式,多线程方式,异步方式。
  

  1. 多进程方式:为每个请求启动一个进程来处理。但是在操作系统中,生成进程,销毁进程都会大量消耗cpu资源,因此一旦负载提高时候,性能会出现明显的下降。
    优点:稳定 由于采用的是独立的进程响应请求,进程之前的独立性保障了一旦单个进程出现问题,不会影响到其他进程的正常运行,因此具有很强的稳定性。
    缺点:资源占用高当服务器负载较高时,同样由于每个进程负责响应一个用户的请求,用户请求数量较多,进程生成数量也会增多,进程资源申请,进程生成,进程切换都会消耗更多的资源。同时进程之间的隔离性也会造成大量的内存重复资源占用大量内存空间。
      
    2.多线程方式:一个进程中生成多个线程,由线程来负责处理用户的请求,由于线程的开销会小于进程,同时进程作为线程的容器,所以在一定的程度上,进程的资源也可以被线程所共享,一定程度上节约了内存空间。
    优点:资源开销小线程间部分数据可以共享,且线程切换消耗明显小于进程。
    缺点:稳定性线程之前的频繁的切换,有一定的可能性会造成线程的抖动,而且线程过多也会在一定程度上造成服务器的不稳定。
      
    3.异步方式使用了非阻塞方式来处理用户请求,开销最小。但是异步方式虽然效率很高,多任务之前的调度一旦出现问题,可能会导致整体的故障,所以使用异步方式,一般会是一些功能相对简单,且代码中没有影响调度的错误的程序。
    优点:性能极好一个进程或者线程来处理多个请求,没有额外开销,性能优异,资源占用少。
    缺点:稳定性一旦出现问题,可能导致大量请求无法处理。

web请求的工作流程

NGINX从入门到放弃(原理与编译安装)
1.客户端发起请求到服务器的对外网卡;
2.服务器网卡收到请求后转交给内核处理;
3.内核根据请求的对应套接字,将请求交给工作在用户空间的web服务器进程;
4.web服务器进程根据用户的请求,向内核申请系统调用,申请获得对应的资源(例如静态页面资源);
5.内核根据web服务器的请求发现它申请的是是一个磁盘文件,因此通过调用磁盘驱动程序连接磁盘;
6.内核调度磁盘,获取所需要的资源;
7.内核将资源存放在自己的缓冲区内,接下来通知web服务器进程来获取资源;
8.web服务器进程通过系统调用获得资源;
9.web服务器进程生成响应报文,通过系统调用发给内核来响应用户的请求;
10.内核将响应的报文发往网卡;
11.网卡将响应数据包发送给用户,响应结束。

  
总结
  客户端向服务器端发送web请求的过程有两个I/O的过程,1.客户端请求的网络I/O;2.web服务器请求页面的I/O。
  

I/O模型介绍

  由于web服务器进程无法直接访问磁盘,而是要申请系统调用因此每次IO,都要经由两个阶段:
  第一步:将数据从磁盘文件先加载至内核内存空间(缓冲区),等待数据准;
备完成,时间较长
  第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短。
具体可以参考下图:
NGINX从入门到放弃(原理与编译安装)
根据数据传输阶段可以将I/O动作分为五类模式:
1.阻塞I/O
2.非阻塞I/O
3.I/O复用
4.信号驱动
5.异步I/O

阻塞和非阻塞

阻塞/非阻塞:关注调用者在等待结果返回之前所处的状态
阻塞:blocking,指IO操作需要彻底完成后才返回到用户空间,调用结果返回之前,调用者被挂起;
非阻塞:nonblocking,指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成,最终的调用结果返回之前,调用者不会被挂起。

同步和异步

同步/异步:关注的是消息通信机制
同步:synchronous,调用者等待被调用者返回消息,才能继续执行
异步:asynchronous,被调用者通过状态、通知或回调机制主动通知调用者被调用者的运行状态

同步阻塞I/O模型

NGINX从入门到放弃(原理与编译安装)
同步阻塞I/O模型是最为简单的I/O模型,用户线程在内核进行I/O操作时被阻塞;
用户线程通过系统调用read发起I/O读操作,由用户空间转到内核空间,内核等待数据包到达后,然后将接受到的数据拷贝到用户空间,进而完成read操作;
用户需要等待read将数据读取到buffer后,才能继续处理接收的数据。整个I/O请求的过程中,用户线程是被阻塞的。导致了用户在发起I/O请求后到获取到数据之间的时间段内,无法做任何事情,这样对cpu是个严重的浪费。

同步非阻塞I/O模型

NGINX从入门到放弃(原理与编译安装)
用户线程发起I/O请求后,会立即返回一个响应,但是并未获取到任何数据,用户线程需要不断的发起I/O请求,直到数据正式到达,才能够真正读取到数据,继续执行接下来的操作。即这是一种“轮询”机制;
整个I/O请求的过程之中,虽然用户线程每次发出I/O请求后可以立刻得到响应,但是为了等到数据,仍然需要不断的去轮询,重复请求,进而消耗了大量cpu资源;
同步非阻塞I/O是一种非常浪费cpu资源的方式,实际中很少会使用,而是会在其他I/O模型之中使用非阻塞I/O这个模型。

I/O多路复用模型

NGINX从入门到放弃(原理与编译安装)
多个连接公用一个等待机制,会阻塞进程,但是进程是阻塞在select或者poll上,并不是阻塞在真正的I/O上。
用户首先将需要进行的I/O操作添加到select上,继续执行其他的工作(异步),同时等待着select系统调用返回,当数据到达时候,I/O被激活,select函数返回,用户线程正式发起read请求,读取数据并且继续运行。
从流程上看,使用了select函数进行I/O请求和同步阻塞模型没有大的区别,甚至还多了添加监视I/O,以及调用select函数等的额外附加操作,效率上相对低下,而且发生了两次的阻塞,但是,第一次阻塞在select上时候,select函数可以监控多个I/O是否已经有I/O操作准备就绪,即可达到在同一个线程内同时处理多个I/O请求的目的。而不同于阻塞I/O,一次仅仅能够监控一个I/O。
虽然I/O多路复用允许同时处理多个I/O请求,但是每个I/O请求的过程还是阻塞的(在select函数上进行阻塞),平均时间甚至比同步阻塞I/O模式还长。如果用户线程仅仅是注册自己需要的I/O请求,然后去做自己的事情,等到数据到来后再进行处理,则可以提高cpu的利用率
I/O多路复用模型是最常使用的I/O模型,但是其异步程度有限,因为它使用了会阻塞线程的select系统调用,因此,I/O多路复用技术也只能被称为,异步阻塞I/O模型,并非真正的异步I/O。

IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,就通知该进程,IO多路复用适用如下场合:
1.当客户端处理多个描述符时(一般是交互式输入和网络套接口),必须使用I/O复用
2.当一个客户端同时处理多个套接字时,此情况可能的但很少出现
3.当一个TCP服务器既要处理监听套接口,又要处理已连接套接口,一般也要用到I/O
复用
4.当一个服务器即要处理TCP,又要处理UDP,一般要使用I/O复用
5.当一个服务器要处理多个服务或多个协议,一般要使用I/O复用

信号驱动I/O模型

NGINX从入门到放弃(原理与编译安装)
信号驱动IO:signal-driven I/O
用户进程可以通过sigaction系统调用注册一个信号处理程序,然后主程序可以继续向下执行,当有IO操作准备就绪时,由内核通知触发一个SIGIO信号处理程序执行,然后将用户进程所需要的数据从内核空间拷贝到用户空间。
此模型的优势在于等待数据报到达期间进程不被阻塞。用户主程序可以继续执行,只要等待来自信号处理函数的通知。
该模型并不常用。

异步I/O模型

NGINX从入门到放弃(原理与编译安装)
异步IO与信号驱动IO最主要的区别是信号驱动IO是由内核通知何时可以进行IO操作,而异步IO则是由内核告诉我们IO操作何时完成了。具体来说就是,信号驱动IO当内核通知触发信号处理程序时,信号处理程序还需要阻塞在从内核空间缓冲区拷贝数据到用户空间缓冲区这个阶段,而异步IO直接是在第二个阶段完成后内核直接通知可以进行后续操作了

相比于IO多路复用模型,异步IO并不十分常用,不少高性能并发服务程序使用IO多路复用模型+多线程任务处理的架构基本可以满足需求。况且目前操作系统对异步IO的支持并非特别完善,更多的是采用IO多路复用模型模拟异步IO的方式(IO事件触发时不直接通知用户线程,而是将数据读写完毕后放到用户指定
的缓冲区中)

五种模型的对比

NGINX从入门到放弃(原理与编译安装)

I/O模型的具体实现

I/O模型的实现主要依赖于以下几种方式:
Select:linux实现,对应I/O复用模型,BSD4.2最早实现
Poll: linux实现,对应I/O复用模型,System V Unix最早实现
Epoll: linux实现,对应I/O复用模型,具有信号驱动I/O模型的一部分特性
Kqueue: FreeBSD实现,对应I/O复用模型,具有信号驱动I/O的某些特性
/dev/poll: SUN的Solaris实现,对应I/O复用模型,具有信号驱动的I/O模型的一部分特性
Iocp: windows实现,对应异步I/O模型。

注意
虽然Windows的Locp最为优秀,但是,目前很少有支持asynchronous I/O的系统,但是由于其系统本身的局限性,大型服务器还是在Linux和UNIX下。而且正如上面所述,kqueue、epoll、/dev/poll 与 IOCP相比,就是多了一层从内核copy数据到应用层的阻塞,从而不能算作asynchronous I/O类。但是,这层小小的阻塞无足轻重。

select、poll与epoll

Select:
POSIX所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是
它的一个优点,本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理
缺点:
1.单个进程可监视的fd数量被限制,即能监听端口的数量有限

cat /proc/sys/fs/file-max

2.对socket是线性扫描,即采用轮询的方法,效率较低
3.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便可以收到通知
优点:
1.没有最大并发连接的限制:能打开的FD的上限远大于1024(1G的内存能监听约10万个端口)
2.效率提升:非轮询的方式,不会随着FD数目的增加而效率下降;只有活跃可用的FD才会调用callback函数,即epoll最大的优点就在于它只管理“活跃”的连接,而跟连接总数无关
3.内存拷贝,利用mmap(Memory Mapping)加速与内核空间的消息传递;即epoll使用mmap减少复制开销。

简介

  Nginx (engine x) 是一个高性能的HTTP和反向代理服务器,也是一个IMAP/POP3/SMTP服务器。Nginx是由伊戈尔·赛索耶夫为俄罗斯访问量第二的Rambler.ru站点(俄文:Рамблер)开发的,第一个公开版本0.1.0发布于2004年10月4日。

  他将源代码以类BSD许可证的形式发布,因它的稳定性、丰富的功能集、示例配置文件和低系统资源的消耗而闻名。2011年6月1日,nginx 1.0.4发布。

  Nginx是一款轻量级的Web 服务器/反向代理服务器及电子邮件(IMAP/POP3)代理服务器,并在一个BSD-like 协议下发行。其特点是占有内存少,并发能力强,事实上nginx的并发能力确实在同类型的网页服务器中表现较好,中国大陆使用nginx网站用户有:百度、京东、新浪、网易、腾讯、淘宝等。
  

nginx特性及基本功能

特性
1.模块化的设计,有不错的拓展性;
2.高可靠性;
3.支持热部署:不停机更新配置文件,升级版本,更换日志文件;
4.较低的内存消耗:10000个keep-alive(长连接)连接模式下的非活动连接,仅仅占用2.5M的内存空间;
5.event-driven(事件驱动),aio(异步非阻塞I/O),mmap(一种内存映射文件的方法),sendfile(内核直接封装报文)

基本的功能
1.静态资源的web服务器;
2.http协议的反向代理器;
3.pop3/imap4协议反向代理服务器(邮件)
4.FastCGI(LNMP),uWSGI(python)等协议;
5.模块化(非DSO);

nginx架构

NGINX从入门到放弃(原理与编译安装)

master/worker结构

一个master进程:负责加载和分析配置文件,管理worker进程,平滑升级。
一个或者多个worker进程:负责处理并响应用户的请求。
缓存相关的进程:
cache loader:载入缓存的对象;
cache manager:管理缓存对象。

nginx模块

nginx是高度模块化的,但是其模块在早期是不支持DSO(动态装卸载)机制的;1.9.11版本之后才支持动态的装卸载;
分类:
  核心模块:core module
  标准模块:
    http模块:ngxhttp
      http core modules 默认功能
      http optional modules 需要编译时候指定
    mail模块 : ngxmail

    stream模块: ngxstream*
第三方模块

nginx的安装

官方rpm包安装

这里可以获得官方rpm包,之后本地安装即可
http://nginx.org/packages/centos/7/x86_64/RPMS/

epel安装

Fedora-EPEL源内有nginx的安装,nginx未被直接收录到base源中。
https://mirrors.aliyun.com/epel/7/x86_64/

编译安装nginx

1.安装编译环境

yum groupinstall "development tools" -y

2.安装编译时可能会需要的包

yum install pcre-devel openssl-devel zlib-devel -y

3.添加nginx系统账号

useradd -r nginx

4.获取nginx源码包,解压并编译

#获取nginx源码包
wget http://nginx.org/download/nginx-1.14.0.tar.gz
#解压
 tar -xvf nginx-1.14.0.tar.gz 
 cd nginx-1.14.0/
#编译
./configure --prefix=/usr/local/nginx --conf-path=/etc/nginx/nginx.conf --error-log-path=/var/log/nginx/error.log --http-log-path=/var/log/nginx/access.log --pid-path=/var/run/nginx.pid --lock-path=/var/run/nginx.lock --user=nginx --group=nginx --with-http_ssl_module --with-http_v2_module --with-http_dav_module --with-http_stub_status_module --with-threads --with-file-aio

make &&make install

NGINX从入门到放弃(原理与编译安装)
NGINX从入门到放弃(原理与编译安装)
编译安装选项
--prefix=/etc/nginx 安装路径
--sbin-path=/usr/sbin/nginx 指明nginx程序文件安装路径
--conf-path=/etc/nginx/nginx.conf 主配置文件安装位置
--error-log-path=/var/log/nginx/error.log 错误日志文件安装位置
--http-log-path=/var/log/nginx/access.log 访问日志文件安装位置
--pid-path=/var/run/nginx.pid 指明pid文件安装位置
--lock-path=/var/run/nginx.lock 锁文件安装位置
--http-client-body-temp-path=/var/cache/nginx/client_temp 客户端
body部分的临时文件存放路径,服务器允许客户端使用put方法提交大数据时,临时存放的磁盘路径
--http-proxy-temp-path=/var/cache/nginx/proxy_temp 作为代理服务器,服务器响应报文的临时文件存放路径
--http-fastcgi-temp-path=/var/cache/nginx/fastcgi_temp 作为fastcgi代理服务器,服务器响应报文的临时文件存放路径
--http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp 作为uwsgi代理服务器,服务器响应报文的临时文件存放路径
--http-scgi-temp-path=/var/cache/nginx/scgi_temp 作为scgi反代服务器,服务器响应报文的临时文件存放路径
--user=nginx 指明以那个身份运行worker进程,主控master进程一般由root运行
--group=nginx
--with-http_ssl_module 表示把指定模块编译进来
5.修改配置文件,添加环境变量

vim /etc/nginx/nginx.conf
#修改运行用户为nginx
user nginx;

#添加环境变量
vim /etc/profile.d/nginx.sh
PATH=/usr/local/nginx/sbin:$PATH
source /etc/profile.d/nginx.sh 

基本配置完成下面的配置是为了省事
6.编写centos7的systemd管理nginx的脚本

vim /usr/lib/systemd/system/nginx.service

#以下是文件内容
[Unit]
Description=the nginx web server
After=network.target remote-fs.target nss-lookup.target

[Service]
Type=forking
PIDFile=/run/nginx.pid
ExecStartPre=/usr/bin/rm -f /run/nginx.pid
ExecStartPre=/usr/local/nginx/sbin/nginx -t
ExecStart=/usr/local/nginx/sbin/nginx
ExecReload=/bin/kill -s HUP $MAINPID
KillSignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true

[Install]
WantedBy=multi-user.target

#保存退出后添加systemd管理
systemctl daemon-reload 
#启动nginx
systemctl strat nginx

nginx安装成功
NGINX从入门到放弃(原理与编译安装)