深入理解Nginx
什么是Nginx?
nginx简介
nginx是开源的,免费的,高性能的HTTP,反向代理,和邮件服务器,Nginx出现解决了C10K问题.
官网: http://nginx.org
特点:
- 模块化设计,可扩展性高.支持动态装卸载.
- 高可靠性 可持续运行.
- 支持热部署,不停机更新配置文件,升级版本,更换日志文件等.
- 低内存消耗,10000个 keep-alive 连接模式下的非活动连接,仅需大概2.5M内存
- 异步的IO模型,sendfile,
Nginx的功能
- 静态资源web服务器
- http 协议反向代理服务器
- pop3/imap4 协议反向代理服务器
- API 服务(openResty)
Nginx的程序架构
进程架构:
master进程: 一个 master 进程负责加载和分析配置文件,管理 worker 进程,平滑升级,
worker进程: 一个或者多个 worker 进程 处理并响应用户请求.
缓存进程: cache loader 载入缓存对象
cache manager 管理缓存对象
Nginx的模块
nginx 高度模块化, 但早起不支持 DSO 机制, 1.9.11 版本支持动态装载和卸载模块.
模块分类:
- 核心模块
- 标准模块
- http模块
- mail模块
- stream模块
- 第三方模块
网络流量常用语
IP(独立IP): 独立IP数, 一天内相同客户机IP地址只计算一次,记录远程客户机IP地址的计算机访问网站的次数.是度量网站流量的重要指标.
PV(访问量): 机 page view,页面浏览量或者点击量,用户每次刷新即被计算一次,PV反映的是浏览某网站的页面数, PV与来访者的数量成正比,PV就是网站被访问的页面数量.
UV(独立访客): 访问网站的一台电脑为一个方可,一天内相同的客户端只被计算一次,此值主要通过访问者携带的电脑cookies计算.
HTTP相关知识
I/O模型
I/O
网络IO: 本质是socket读取,服务器端的网络吞吐.
磁盘IO.
I/O的两个阶段
磁盘IO:
第一步: 将数据从磁盘文件先加载到内核内存空间(缓冲区),等待数据准备完成,耗时比较长.
第二步: 将数据从内核缓冲区复制到用户空间的进程内存中,时间比较短.
网络IO:
第一步:通常涉及等待网络上的数据分组到达,然后被复制到内核的某个缓冲区。
第二步:把数据从内核缓冲区复制到应用进程缓冲区。
I/O模型
- 同步: 关注的是消息通信的机制, 客户端主动等待调用者返回消息,才能继续执行.
- 异步: 关注的是消息通信的机制, 服务端(被调用者)通过状态,通知或回调机制主动通知调用者被调用者的运行状态.
- 阻塞: 关注调用者在等待结果返回之前所处的状态, 指IO操作需要彻底完成之后才返回到用户空间,调用结果返回之前,调用者被挂起.
- 非阻塞: 指IO操作被调用后立即返回给用户一个状态值,无需等到IO操作彻底完成,最终的调用结果返回之前,调用者不会被挂起.
阻塞型
阻塞型IO,用户线程在内核进行IO操作时被阻塞.直到服务端准备好数据返回.
用户线程通过系统调用read发起IO读操作,由用户空间到内核空间,内核等到数据包到达后,然后将接受到的数据拷贝到用户空间,完成read操作.
用户需要等待read 将数据读取到 buffer 后,才继续处理接收的数据,整个IO 请求的过程中, 用户线程是被阻塞的,这导致用户在发起IO请求时,不能做任何事情,对CPU的资源利用率不够.
非阻塞型
用户线程发起IO请求时立即返回,但并未督导任何数据,用户线程需要不断地发起IO请求,知道数据到达后,才真正的读取到数据,继续执行,用户在发起IO 请求的时候会不断询问发起IO请求等到数据.
整个IO请求的过程中,瑞然用户线程每次发起IO请求后可以立即返回,但是为了等到数据,仍需要不断地轮询,重复请求,消耗大量的CPU资源.
复用型
多个连接功用一个等待机制,本模型会阻塞进程,但是进程是阻塞在select 或者 poll 这两个系统调用上,而不是阻塞在真正的IO操作上.主要阻塞在IO复用器上.
用户首先将需要进程IO操作添加到 select 中, 继续执行要做的其他用作(异步),同事等待 select 系统调用返回,当数据到达时候,IO被激活, select 函数返回,用户线程正式发起 read 请求. 读取数据并继续执行.
信号驱动型
信号驱动IO: signal-driven I/O
用户进程可以通过 sigaction 系统调用注册一个信号处理程序,然后主程序可以继续向下执行,当有IO操作准备就绪时,由内核通知触发 SIGIO 信号处理程序执行, 然后将用户进程所需要的数据从内核空间拷贝到用户空间.
发起调用后给对方留一个回调接口,第二步阻塞.内核内存到自己的内存会被阻塞. 完成百分之九十五的速度.
异步
异步IO 与信号驱动IO 最主要的区别是 信号驱动IO是由内核通知何时可以进行IO操作,而异步IO 则是由内核告诉我们IO操作何时完成了.
信号驱动IO当内核通知触发信号处理程序时, 信号处理程序还需要阻塞在从内和空间缓冲区拷贝数据到用户空间缓冲区这个阶段.而异步IO直接是在第二个阶段完成之后内核直接通知可以进行后续操作了.
相比于IO多路复用模型,异步IO并不十分常用,不少高性能并发服务程序使用IO多路复用模型+多线程任务处理的架构基本可以满足需求。况且目前操作系统对异步IO的支持并非特别完善,更多的是采用IO多路复用模型模拟异步IO的方式(IO事件触发时不直接通知用户线程,而是将数据读写完毕后放到用户指定的缓冲区中)
I/O模型的具体实现方式
select
POSIX所规定,目前几乎在所有平台上都支持,基于良好的跨平台特点,本质上是通过设置或者自检存放fs标志位的数据结果来进行下一步处理
缺点:
最大并发数限制,因为一个进程所打开的 FD (文件描述符)是有限制的即监听端口就会有限制.,由 FD_SETSIZE 设置,默认值是 1024/2048 ,因此 Select 模型的最大并发数就被相应限制了。
cat /proc/sys/fs/file-max
效率问题, select 每次调用都会线性扫描全部的 FD 集合. 对于socket 是线性扫描,采用轮询方法,效率低.
select 采取了内存拷贝方法来实现内核将 FD 消息通知给用户空间,这样一个用来存放大量fd的数据结构,这样会使得用户空间和内核空间在传递该结构时复制开销大.
poll
本质上和select没有区别, 都是将用户传入的数据拷贝到内核空间,然后查询每个fd对应的设备状态.
优点就是没有最大的连接数的限制,是因为其采用基于链表来存储数据的.
epoll
在 Linux 2.6 内核中提出了 select 和 poll 的增强版本.
epoll 没有最大并发连接的限制,上限是最大可以打开文件的数目,这个数字一般远大于 2048, 一般来说这个数目和系统内存关系很大 ,(1G 的内存能监听约10万个端口).
效率提升:非轮询的方式,不会随着FD数目的增加而效率下降;只有活跃可用的FD才会调用callback函数,即epoll最大的优点就在于它只管理“活跃”的连接,而跟连接总数无关.
效率提升, epoll 最大的优点就在于它只管你“活跃”的连接 ,而跟连接总数无关,因此在实际的网络环境中, Epoll 的效率就会远远高于 select 和 poll 。
内存拷贝,利用mmap(Memory Mapping)加速与内核空间的消息传递;即epoll使用mmap减少复制开销.
sendfile
什么是sendfile?
sendfile 是 linux 2.0+ 提出的一个系统调用,主要是用来解决优化传统网络传输的过程.
传统的socket 数据传输流程
磁盘→kernel buffer→user buffer→kernel socket buffer→存储协议(协议栈)
具体执行:
一般来说一个网络应用是通过读硬盘数据,然后写数据到socket 来完成网络传输的。
- 系统调用 read()产生一个上下文切换:从 user mode 切换到 kernel mode,然后 DMA 执行拷贝,把文件数据从硬盘读到一个 kernel buffer 里。
- 数据从 kernel buffer拷贝到 user buffer,然后系统调用 read() 返回,这时又产生一个上下文切换:从kernel mode 切换到 user mode。
- 系统调用write()产生一个上下文切换:从 user mode切换到 kernel mode,然后把步骤2读到 user buffer的数据从user buffer拷贝至kernel socket buffer。
- 系统调用 write()返回,产生一个上下文切换:从 kernel mode 切换到 user mode(第4次切换了),然后 DMA 从 kernel buffer拷贝数据到协议栈(第4次拷贝了)。
总的来说数据发生了四次拷贝,产生了四次上下文切换。
什么是DAM?
DMA(Direct Memory Acess)是一种不通过CPU,直接在系统内存和外设之间交互数据的接口技术。正常来说如果使用CPU来进行数据交换,则需要首先使用CPU读取外设字节至内部寄存器,然后再将寄存器中的字节写入内存。而DMA的出现则可以直接将外设字节一次写入内存,加快了数据写入内存的速度。
sendfile网络传输
硬盘 >> kernel buffer (快速拷贝到kernel socket buffer) >>协议栈
- 系统调用sendfile()通过 DMA把硬盘数据拷贝到 kernel buffer,然后数据被 kernel 直接拷贝到另外一个与 socket相关的 kernel buffer。这里没有 user mode和 kernel mode之间的切换,在 kernel中直接完成了从一个 buffer到另一个 buffer的拷贝。
- DMA 把数据从 kernelbuffer 直接拷贝给协议栈,没有切换,也不需要数据从 user mode 拷贝到 kernel mode,因为数据就在 kernel 里。
nginx部署
yum安装
yum 安装nginx 需要先安装 epel仓库.
[root@exercise ~]# yum install epel-release # 安装epel仓库 [root@exercise ~]# yum install nginx # 安装nginx # centos 官网 yum 仓库 http://nginx.org/en/linux_packages.html#RHEL-CentOS
编译安装ngixn
nginx软件包下载地址: 点击进入
# 准备工作 useradd -r nginx # 创建用户 mkdir /var/cache/nginx/client_temp -p # 安装依赖工具 yum groupinstall "Development Tools" "Server Platform Development" yum install pcre-devel openssl-devel zlib-devel wget http://nginx.org/download/nginx-1.14.2.tar.gz # 下载 要安装的nginx 软件包 tar xf nginx-1.14.2.tar.gz # 解压软件包 cd nginx-1.14.2/ # 编译nginx 并安装 ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx-1.14.2 \ --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.lok --http-client-body-temp-path=/var/cache/nginx/client_temp \ --with-http_ssl_module --with-http_v2_module --with-http_dav_module \ --with-http_stub_status_module --with-threads --with-file-aio make -j4 make install 创建软连接 ln -s /usr/local/nginx-1.14.2/ /usr/local/nginx ln -s /usr/local/nginx/sbin/* /usr/sbin/ nginx
编译命令说明:
具体编译参数可使用 --help命令查看详细参数 ./configure --help
表示启用: --with-......
默认启用添加参数则禁用: --without- ......
1 # 常规编译参数 2 --user=nginx # 指明以那个身份运行worker进程,主控master进程一般由root运行 3 --group=nginx # 指明运行用户的用户组 4 --prefix=/etc/nginx # 安装路径 5 --sbin-path=/usr/sbin/nginx # 指明nginx程序文件安装路径 6 --conf-path=/etc/nginx/nginx.conf # 主配置文件安装位置 7 --error-log-path=/var/log/nginx/error.log # 错误日志文件安装位置 8 --http-log-path=/var/log/nginx/access.log # 访问日志文件安装位置 9 --pid-path=/var/run/nginx.pid # 指明pid文件安装位置 10 --lock-path=/var/run/nginx.lock # 锁文件安装位置 11 12 # 一下参数可选 如果使用此参数 需要手动创建目录 13 --http-client-body-temppath=/var/cache/nginx/client_temp # 客户端body部分的临时文件存放路径,如果服务器允许客户端使用put方法提交大数据时,临时存放的磁盘路径。 14 --http-proxy-temp-path=/var/cache/nginx/proxy_temp # 作为代理服务器,服务器响应报文的临时文件存放路径 15 --http-fastcgi-temppath=/var/cache/nginx/fastcgi_temp # 作为fastcgi代理服务器,服务器响应报文的临时文件存放路径 16 --http-uwsgi-temp-path=/var/cache/nginx/uwsgi_temp # 作为uwsgi代理服务器,服务器响应报文的临时文件存放路径 17 --http-scgi-temp-path=/var/cache/nginx/scgi_temp # 作为scgi反代服务器,服务器响应报文的临时文件存放路径 18 19 # 指定编译进来的模块 20 --with-http_ssl_module # 用来支持 https 相关配置 21 --with-http_v2_module # 用来支持HTTP / 2 22 --with-http_dav_module # 用来启用 ngx_http_dav_module支持(增加PUT,DELETE,MKCOL:创建集合,COPY 和 MOVE 方法为文件和目录指定权限,限制不同类型的用户对于页面有不同的操作权限)默认情况下为关闭,需编译开启 23 --with-http_stub_status_module # 用来获取 nginx 的工作状态 24 --with-threads # 启用thread pool支持 25 --with-file-aio # 允许使用的异步文件I / O(AIO)在FreeBSD和Linux。
更多编辑参数说明: 点击进入
编写systemd启动脚本
[root@exercise ~]# cat /usr/lib/systemd/system/nginx.service [Unit] # 主要写一些Unit信息 Description=The nginx HTTP and reverse proxy server # 描述性文本 After=network.target remote-fs.target nss-lookup.target # 描述服务类别,表示本服务需要在那些服务之后启动 # Before=xxxx.service # 表示需要在某些服务启动之前启动,After和Before字段只涉及启动顺序,不涉及依赖关系。 [Service] # 服务的关键 Type=forking # 表示后台运行模式 PIDFile=/var/run/nginx.pid #存放pid的绝对路径 ExecStartPre=/usr/bin/rm -f /var/run/nginx.pid # ExecStartPre 字段表示在服务启动之前要做的任务 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 # 表示多用户
nginx命令
nginx # 启动 nginx 文件 nginx -s [option] # 管理nginx 进程 stop(停止), quit(优雅退出), reopen(重新读取日志文件), reload(重新载入配置文件) nginx -V # 显示版本信息及相关编译信息 nginx -v # 只显示版本信息 nginx -t # 检查配置文件语法 nginx -p # 指定默认路径 nginx -c # 指定配置文件 nginx -g # 设置配置文件外的全局命令
信号方式管理ngixn
使用方法: kill -s [parameter] [pid] [parameter] HUP: 重新加载 USR1: 重新读取日志文件 TERM: 停止 QUIT: 优雅停止
nginx reload流程
- 向 master 进程发送 HUP 信号.
- master 进程校验配置语法是否正确.
- master进程打开新的监听端口.
- master进程用新配置启动的 worker 子进程.
- master进程向老 worker 子进程发送 QUIT 信号.
- 老 worker 进程关闭监听句柄. 处理完当前连接后结束进程.
nginx 优雅关闭流程
- 设置定时器 worker_shutdown_timeout
- 关闭监听句柄
- 关闭空闲连接
- 在循环中等待全部连接被关闭
- 退出程序
日志切割
nginx热升级
热升级步骤:
1. 备份 nginx 文件 [root@test1 ~]# cp /usr/local/nginx-1.14.2/sbin/nginx /usr/local/nginx-1.14.2/sbin/nginx.lod -f 2. 查看 nginx 版本及编译参数. [root@test1 nginx-1.16.1]# nginx -V nginx version: nginx/1.14.2 built by gcc 4.8.5 20150623 (Red Hat 4.8.5-39) (GCC) configure arguments: --user=nginx --group=nginx --prefix=/usr/local/nginx-1.14.2/ --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.lok 3. 编译新版nginx 不执行 make install [root@test1 nginx-1.16.1]# ./configure --user=nginx --group=nginx --prefix=/usr/local/nginx-1.14.2/ --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.lok [root@test1 nginx-1.16.1]# make 4. 替换 nginx 文件 [root@test1 nginx-1.16.1]# cd objs/ [root@test1 objs]# cp -f nginx /usr/local/nginx-1.14.2/sbin/ cp:是否覆盖"/usr/local/nginx-1.14.2/sbin/nginx"? y 5. 向 master 进程发送 USR2 信号告诉他要进行热部署. [root@test1 objs]# ps -ef | grep nginx root 5575 1 0 13:49 ? 00:00:00 nginx: master process nginx nginx 5576 5575 0 13:49 ? 00:00:00 nginx: worker process root 5627 1625 0 13:50 pts/0 00:00:00 grep --color=auto nginx [root@test1 objs]# kill -USR2 5575 [root@test1 objs]# ps -ef | grep nginx root 5575 1 0 13:49 ? 00:00:00 nginx: master process nginx nginx 5576 5575 0 13:49 ? 00:00:00 nginx: worker process root 5656 5575 0 13:50 ? 00:00:00 nginx: master process nginx nginx 5657 5656 0 13:50 ? 00:00:00 nginx: worker process root 5665 1625 0 13:50 pts/0 00:00:00 grep --color=auto nginx 6. 告诉老的 master 进程关闭所有的 work进程 [root@test1 objs]# kill -WINCH 5575 [root@test1 objs]# ps -ef | grep nginx root 5575 1 0 13:49 ? 00:00:00 nginx: master process nginx root 5656 5575 0 13:50 ? 00:00:00 nginx: master process nginx nginx 5657 5656 0 13:50 ? 00:00:00 nginx: worker process root 5763 1625 0 13:52 pts/0 00:00:00 grep --color=auto nginx 此时 nginx 热升级已经完成,但是老的 master 进程还会存在,如果有问题可直接进行回滚.没有问题可让他优雅退出 kill -QUIT 5575
版本回退:
1. 恢复 nginx 文件 [root@test1 ~]# cp /usr/local/nginx-1.14.2/sbin/nginx.lod /usr/local/nginx-1.14.2/sbin/nginx -f cp:是否覆盖"/usr/local/nginx-1.14.2/sbin/nginx"? y 2. 唤醒老的 master进程 [root@test1 ~]# ps -ef | grep nginx root 5575 1 0 13:49 ? 00:00:00 nginx: master process nginx root 5656 5575 0 13:50 ? 00:00:00 nginx: master process nginx nginx 5657 5656 0 13:50 ? 00:00:00 nginx: worker process root 6144 1625 0 14:00 pts/0 00:00:00 grep --color=auto nginx [root@test1 ~]# kill -HUP 5575 [root@test1 ~]# ps -ef | grep nginx root 5575 1 0 13:49 ? 00:00:00 nginx: master process nginx root 5656 5575 0 13:50 ? 00:00:00 nginx: master process nginx nginx 5657 5656 0 13:50 ? 00:00:00 nginx: worker process nginx 6159 5575 0 14:00 ? 00:00:00 nginx: worker process root 6164 1625 0 14:00 pts/0 00:00:00 grep --color=auto nginx 3. 告诉新的master 进程要平滑回退. 回退版本的 worker 进程接收新的用户请求,同事新版的 master 进程的worker 进程不在接 收到用户请求 [root@test1 ~]# kill -USR2 5656 4. 关闭 新版本的 master 的 worker 进程 [root@test1 ~]# kill -WINCH 5656 [root@test1 ~]# ps -ef | grep nginx root 5575 1 0 13:49 ? 00:00:00 nginx: master process nginx root 5656 5575 0 13:50 ? 00:00:00 nginx: master process nginx nginx 6159 5575 0 14:00 ? 00:00:00 nginx: worker process root 6399 1625 0 14:04 pts/0 00:00:00 grep --color=auto nginx 5. 新版的 master 进程进行优雅退出 [root@test1 ~]# kill -QUIT 5656 [root@test1 ~]# ps -ef | grep nginx root 5575 1 0 13:49 ? 00:00:00 nginx: master process nginx nginx 6159 5575 0 14:00 ? 00:00:00 nginx: worker process root 6474 1625 0 14:06 pts/0 00:00:00 grep --color=auto nginx
nginx配置文件详解
点此进入: >>>>>>>>>>>>
nginx反向代理
作者:闫世成
出处:http://cnblogs.com/yanshicheng