nginx原理了解
1、什么是nginx?
Nginx
是一款自由的、开源的、高性能的HTTP服务器和反向代理服务器;同时也是一个IMAP、POP3、SMTP代理服务器;Nginx
可以作为一个HTTP服务器进行网站的发布处理,另外Nginx
可以作为反向代理进行负载均衡的实现。
1.1、Nginx三个主要应用场
1、静态资源服务(通过本地文件系统提供服务)
2、缓存、负载均衡服务器
3、API服务(OpenResty)
2、为什么选择nginx?
(1)更快
这表现在两个方面:一方面,在正常情况下,单次请求会得到更快的响应;另一方面,
在高峰期(如有数以万计的并发请求),Nginx
可以比其他Web服务器更快地响应请求。
实际上,
(2)高扩展性
Nginx
的设计极具扩展性,它完全是由多个不同功能、不同层次、不同类型且耦合度极
低的模块组成。因此,当对某一个模块修复 Bug 或进行升级时,可以专注于模块自身,无须
在意其他。
而且在HTTP模块中,还设计了HTTP
过滤器模块:一个正常的HTTP
模块在处理
完请求后,会有一串HTTP
过滤器模块对请求的结果进行再处理。
这样,当我们开发一个新的HTTP
模块时,不但可以使用诸如 HTTP
核心模块、events模块、log模块 等不同层次或者不同类型的模块,还可以原封不动地复用大量已有的HTTP
过滤器模块。
这种低耦合度的优秀设计,造就了Nginx
庞大的第三方模块,当然,公开的第三方模块也如官方发布的模块一样容易使用。
Nginx
的模块都是嵌入到二进制文件中执行的,无论官方发布的模块还是第三方模块都
是如此。这使得第三方模块一样具备极其优秀的性能,充分利用Nginx
的高并发特性,因
此,许多高流量的网站都倾向于开发符合自己业务特性的定制模块。
(3)高可靠
高可靠性是我们选择Nginx
的最基本条件,因为Nginx
的可靠性是大家有目共睹的,很多家高流量网站都在核心服务器上大规模使用Nginx
。Nginx
的高可靠性来自于其核心框架代码的优秀设计、模块设计的简单性;另外,官方提供的常用模块都非常稳定,每个worker
进程相对独立,master进程在1个worker
进程出错时可以快速“拉起”新的worker
子进程提供服务。
(4)低内存消耗
一般情况下,10000个非活跃的HTTP Keep-Alive连接在Nginx
中仅消耗2.5MB的内存,这是Nginx
支持高并发连接的基础。
(5)单机支持10万以上的并发连接
这是一个非常重要的特性!随着互联网的迅猛发展和互联网用户数量的成倍增长,各大公司、网站都需要应付海量并发请求,一个能够在峰值期顶住10万以上并发请求的 Server,
无疑会得到大家的青睐。理论上,Nginx支持的并发连接上限取决于内存,当然,能够及时地处理更多的并发请求,是与业务特点紧密相关的
(6)热部署
master管理进程与worker
工作进程的分离设计,使得worker
能够提供热部署功能,即可以在7×24小时不间断服务的前提下,升级worker
的可执行文件。当然,它也支持不停止服务就更新配置项、更换日志文件等功能。
(7)最自由的BSD许可协议
这是worker
可以快速发展的强大动力。BSD许可协议不只是允许用户免费使用worker
,它还允许用户在自己的项目中直接使用或修改worker
源码,然后发布。这吸引了无数开发者继续为worker
贡献自己的智慧。
3、Nginx相关的开源版本
1、阿里巴巴Tengine
Tengine是由淘宝网发起的Web服务器项目。它在Nginx的基础上,针对大访问量网站的需求,添加了很多高级功能和特性。
2、openresty
OpenResty® 是一个基于 Nginx 与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。
3、Nginx高效的原因及原理解析
前面已经大概了解了worker
,接下来我们要了解下worker
的基本架构。
3.1、服务器的网络服务模型
web服务器和客户端是一对多的关系,Web服务器必须有能力同时为多个客户端提供服务。一般来说完成并行处理请求工作有三种方式
1、单进程阻塞的网络服务器
说明:
1、创建一个socket,绑定服务器端口(bind),监听端口(listen),在PHP中用stream_socket_server一个函数就能完成上面3个步骤
2、进入while循环,阻塞在accept操作上,等待客户端连接进入。此时程序会进入睡眠状态,直到有新的客户端发起connect到服务器,操作系统会唤醒此进程。accept函数返回客户端连接的socket
3、利用fread读取客户端socket当中的数据收到数据后服务器程序进行处理然后使用fwrite向客户端发送响应。长连接的服务会持续与客户端交互,而短连接服务一般收到响应就会close。
缺点:
一次只能处理一个连接,不支持多个长连接同时处理
2、多进程方式
多进程方式指,服务器每当收到一个客户端请求时,就有服务器主进程生成一个子进程出来和客户端建立连接进行交互,直到连接断开该子进程就结束了。
说明:
前面流程一致就不补充了
1、程序启动后就会创建N个进程。每个子进程进入 Accept,等待新的连接进入。当客户端连接到服务器时,其中一个子进程会被唤醒,开始处理客户端请求,并且不再接受新的TCP连接。
当此连接关闭时,子进程会释放,重新进入 Accept,参与处理新的连接。
这个模型的优势是完全可以复用进程,不需要太多的上下文切换,比如php-fpm基于此模型来处理解析php
多进程方式的优点是设计简单,各个子进程相对独立,处理客户端请求时彼此不受干扰;
缺点是操作系统生成一个子进程需要进行内存复制等操作,在资源和时间上会产生一定的开销;当有大量请求时,会导致系统性能下降;
例如:即时聊天程序,一台服务器可能要维持数十万的连接,那么就要启动数十万的进程来维持。这显然不可能
2、多线程方式
多线程方式指每当服务器接收到一个请求后,会由服务器主进程派生出一个线程出来和客户端进行交互。由于操作系统产生出一个线程的开销远远小于一个进程的开销。故多线程方式在很大程度上减轻了Web服务器对系统资源的要求。
缺点:稳定性!假设某个进程突然关闭会造成整个进程中的所有线程都崩溃。
基于上面的模式我们发现单个进程每次只能通过每次(accept)处理单个请求,没办法一次性连接多个请求,需要的时候再处理呢?
3、单进程IO复用方
说明:
1、保存所有的socket,通过select模型,监听socket描述符的可读事件
2、Select会在内核空间监听一旦发现socket可读,会从内核空间传递至用户空间,在用户空间通过逻辑判断是服务端socket可读,还是客户端的socket可读
3、如果是服务端的socket可读,说明有新的客户端建立,将socket保留到监听数组当中
4、如果是客户端的socket可读,说明当前已经可以去读取客户端发送过来的内容了,通过fread读取socket内容,然后fwrite响应给客户端。
优点:性能最好!一个进程或线程处理多个请求,不需要额外开销,性能最好,资源占用最低。
缺点:稳定性!某个进程或线程出错,可能导致大量请求无法处理,甚至导致整个服务宕机,单进程对于大量任务处理乏力
4.多进程的master-workerIO复用方式
4.1、nginx的基本架构
1.Nginx启动后,会产生一个主进程,主进程执行一系列的工作后会产生一个或者多个工作进程
2.在客户端请求动态站点的过程中,Nginx服务器还涉及和后端服务器的通信。Nginx将接收到的Web请求通过代理转发到后端服务器,由后端服务器进行数据处理和组织;
3.Nginx为了提高对请求的响应效率,降低网络压力,采用了缓存机制,将历史应答数据缓存到本地。保障对缓存文件的快速访问
master进程主要用来管理worker
进程,具体包括以下主要功能:
(1)接收来自外界的信号。
(2)处理配置文件读取。
(3)创建,绑定和关闭套接字
(4)启动,终止和维护配置的工作(worker)进程数
(5)当woker进程退出后(异常情况下),会自动重新启动新的woker进程。
worker
进程的主要任务是完成具体的任务逻辑。其主要关注点是与客户端或后端真实服务器(此时worker
作为中间代理)之间的数据可读/可写等I/O交互事件。
(1)接收客户端请求;
(2)将请求一次送入各个功能模块进行过滤处理;
(3)与后端服务器通信,接收后端服务器处理结果;
(4)数据缓存
(5)响应客户端请求
案例:实现一个简单的php多进程版本的nginx
1、能够接收http请求
2、能够响应http
3、能够接收多个客户端长连接请求
安装pcntl模块
http://cn2.php.net/distributions/php-7.1.14.tar.xz
tar xf php-7.1.14.tar.xz
cd php-7.1.14
cd ext/pcntl
phpize
./configure --with-php-config=/usr/bin/php-config
make
make install
echo "extension=pcntl.so" >> /etc/php.ini
从上面的代码当中我们发现,如果是多进程的模式,也就意味着我们需要大量的进程,才能支持更多的连接请求,那么有没有一种技术可以在一个进程内处理所有并发IO呢?答案是有,这就是IO复用技术。
4、IO复用/EventLoop
1、IO复用是什么
IO多路复用是指内核一旦发现进程指定的一个或者多个IO条件准备读取,它就通知该进程,目前支持I/O多路复用的系统调用有 select
,poll
,epoll
,I/O多路复用就是通过一种机制,一个进程可以监视多个描述符(socket),一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知程序进行相应的读写操作。
2、select跟poll
Select介绍
监视并等待多个文件描述符的属性变化(可读、可写或错误异常)。select
函数监视的文件描述符分 3 类,分别是writefds、readfds、和 exceptfds。调用后 select
会阻塞,直到有描述符就绪(有数据可读、可写、或者有错误异常),或者超时( timeout 指定等待时间),函数才返回。当 select
()函数返回后,可以通过遍历 fdset,来找到就绪的描述符,并且描述符最大不能超过1024
Poll 介绍:
poll
的机制与select
类似,与select
在本质上没有多大差别,管理多个描述符也是进行轮询,根据描述符的状态进行处理,但是poll
没有最大文件描述符数量的限制。poll
和select
同样存在一个缺点就是,包含大量文件描述符的数组被整体复制于用户态和内核的地址空间之间,而不论这些文件描述符是否就绪,它的开销随着文件描述符数量的增加而线性增大。
问题:
select/poll
问题很明显,它们需要循环检测连接是否有事件。如果服务器有上百万个连接,在某一时间只有一个连接向服务器发送了数据,select/poll
需要做循环100万次,其中只有1次是命中的,剩下的99万9999次都是无效的,白白浪费了CPU资源。
epoll
epoll
是在2.6内核中提出的,是之前的 poll
和 poll
的增强版本。相对于 select
和 poll
来说,epoll
更加灵活,没有描述符限制,无需轮询。epoll
使用一个文件描述符管理多个描述符,将用户关系的文件描述符的事件存放到内核的一个事件表中。
简单点来说就是当连接有I/O流事件产生的时候,epoll
就会去告诉进程哪个连接有I/O流事件产生,然后进程就去处理这个进程。
这里可以多加一个选择worker
的原因,因为worker
是基于epoll
的异步非阻塞的服务器程序。自然,worker
能够轻松处理10w+的并发连接,也就无可厚非了。
实例
那么我们也可以基于 epoll
,实现 IO复用异步非阻塞(eventloop
或者叫Reactor
),Event
是核心,Loop
是机制,Loop
可以用 select|poll|epoll
中的任意方式实现。
IO复用异步非阻塞程序使用经典的Reactor模型,Reactor顾名思义就是反应堆的意思,它本身不处理任何数据收发。只是可以监视一个socket(也可以是管道、eventfd、信号)句柄的事件变化。
注:什么是句柄?句柄英文为handler,可以形象的比喻为锅柄、勺柄。也就是资源的唯一标识符、资源的ID。通过这个ID可以操作资源。
php要使用epoll机制需要安装libevent依赖,或者是swoole当中的event机制,从使用角度上看swoole的event更加简单,这里编译下swoole扩展
安装event依赖:
Swoole安装方式跟普通扩展安装方式是一样的,下载解压、编译
wget https://pecl.php.net/get/swoole-4.4.4.tgz
tar -zxf swoole-4.4.4.tgz
phpize
./configure
make
make install
五、进程相
1、fork函
fork函数将创建调用的进程副本,并非根据完全不同的程序创建进程,而是复制正在运行的、调用fork函数的进程,但因为通过同一个进程、复制相同的内存空间,之后需要根据返回值加以区分:
父进程空间:fork函数会返回子进程id
子进程空间:fork函数返回0
如果小于0,说明进程创建失败
2、处理僵尸进程
对于多进程程序而言,父进程一般需要跟踪子进程的退出状态。因此,当子进程结束运行时,内核不会立即释放该进程的进程表表项,以满足父进程后续对该子进程退出信息的查询 (如果父进程还在运行)。
在子进程结束运行之后,父进程读取其退出状态之前,我们称该子进程处于俚尸态,另外一种使子进程进人僵尸状态是:父进程结束或者异常终止,而子进程继续运行。
由此可见,无论哪种情况,如果父进程没有正确地处理子进程的返冋信息,子进程都将停留在僵尸态,并占据若内核资源.这是绝对不能容许的,毕竞内核资源有限,所以我们需要做的就是在父进程中调用wait函数等待查询子进程的信息,回收子进程。
六、nginx操作
6.1 nginx快速编译安装
yum -y install pcre pcre-devel zlib zlib-devel openssl openssl-devel
wget http://nginx.org/download/nginx-1.14.1.tar.gz
tar -zxvf nginx-1.14.1.tar.gz
./configure --prefix=/usr/local/nginx \
--with-http_stub_status_module \
--with-http_gzip_static_module\
--with-http_realip_module\
--with-http_sub_module \
--with-http_ssl_module\
--with-http_realip_module \
--with-http_sub_module \
--with-http_gunzip_module\
--with-http_gzip_static_module\
--with-http_auth_request_module\
--with-http_random_index_module \
--with-http_slice_module \
--with-http_stub_status_module
make && make install
查看加载的模块及版本信息
nginx -V
6.2 配置文件解释
main
(全局设置),main
部分设置的指令将影响其它所有部分的设置;
http(http服务器设置),http标准核心模块,http服务的相应配置
server
(主机设置)
接收请求的服务器需要将不同的请求按规则转发到不同的后端服务器上,在 nginx 中我们可以通过构建虚拟主机(server
)的概念来将这些不同的服务配置隔离
location
(URL匹配特定位置后的设置),location
部分用于匹配网页位置(比如,根目录“/”,“/images”,等等),server
是对应一个域名进行的配置,而 location
是在一个域名下对更精细的路径进行配置.
[图片上传失败...(image-e31ade-1571366341547)]
详细解析
配置语法说明:
1、配置文件由指令与指令块构成
2、每条指令以;分号结尾,指令与参数间以空格符号分隔
3、指令块以{}大括号将多条指令组织在一起
4、使用#符号添加注释,提高可读性
5、include语句允许组合多个配置文件以提升可维护性
6、使用$符号使用变量
7、部分指令的参数支持正则表达式
Nginx的各种指令以及配置繁多,有些配置可以在如下的链接 https://tengine.taobao.org/nginx_docs/cn/docs/ 或者在官方文档上查看
6.3 nginx常用命令
1、查看Nginx的版本号:nginx -V
2、停止 nginx -s stop
3、退出 nginx -s quit
4、重启加载配置 nginx -s reload
5、配置文件启动 nginx -c </path/to/config> 为 Nginx 指定一个配置文件,来代替缺省的
6、nginx -t 不运行,而仅仅测试配置文件。nginx 将检查配置文件的语法的正确性,并尝试打开配置文件中所引用到的文件。
6.4、nginx控制信号
再次回过头看看nginx的原理图,我们还可以通过信号去操作 nginx
,默认,nginx
将其主进程的 pid 写入到 /usr/local/nginx/nginx.pid
文件中
命令 | 解释 |
---|---|
TERM, INT | 快速关闭 |
QUIT | 从容关闭 |
HUP | 重载配置 用新的配置开始新的工作进程 从容关闭旧的工作进程 |
USR1 | 重新打开日志文件 |
USR2 | 平滑升级可执行程序。 |
WINCH | 从容关闭工作进程 |
nginx 停止命令,等所有请求结束后关闭服务
Kill -QUIT nginx
主进程号
重新载入配置
kill -HUP nginx 主进程号