性能影响

互联网存在用户速度体验的1-3-10原则,即1秒最优,3-10秒比较慢,10秒以上用户无法接受。用户放弃一个产品的代价很低,只是换一个URL而已。

影响用户体验的几个因素

  • 客户端硬件配置
  • 客户端网络速率
  • 客户端与服务端距离
  • 服务端网络速率
  • 服务端硬件配置
  • 服务端架构设计
  • 服务端应用程序工作模式
  • 服务端并发数量
  • 服务端响应文件大小及数量
  • 服务端I/0压力

服务端I/O

I/O在计算机中指Input/Output,IOPS (Input/Output Per Second)即每秒的输入输出量(或读写次数),是衡量磁盘性能的主要指标之一。IOPS是指的是在单位时间内系统能处理的I/O请求数量,一般以每秒处理的I/O请求数量为单位,I/0请求通常为读或写数据操作请求。

一次完整的I/O是用户空间的进程数据与内核空间的内核数据的报文的完整交换过程,但是由于内核空间与用户空间是严格隔离的,所以其数据交换过程中不能由用户空间的进程直接调用内核空间的内存数据,而是需要经历一次从内核空间中的内存数据copy到用户空间的进程内存当中,所以简单说一次I/O就是把数据从内核空间中的内存数据复制到用户空间中进程的内存当中的整个过程。
而网络通信就是从网络协议栈到用户空间进程的I/O,也就是网络I/O。

image-20200522215643568

磁盘I/O是进程向内核发起系统调用,请求磁盘上的某个资源比如是文件或者是图片,然后内核通过相应的驱动程序将目标图片加载到内核的内存空间,加载完成之后把数据从内核内存再复制给进程内存,如果是比较大的数据也需要等待时间。

每次I/O,都要经由两个阶段:
第一步:将数据从磁盘文件先加载至内核内存空间(缓冲区), 此步骤需要等待数据准备完成,时间较长
第二步:将数据从内核缓冲区复制到用户空间的进程的内存中,时间较短

I/O模型

系统I/O

同步/异步

关注的是事件处理的消息通信机制,即在等待一件事情的处理结果时,被调用者是否提供完成通知。

  • 同步: synchronous, 调用者等待被调用者返回消息后才能继续执行,如果被调用者不提供消息返回则为同步,同步需要调用者主动询问事情是否处理完成。

    进程发出请求调用后,内核不提供通知机制,即文件I/O处理完成后不通知进程,需要进程自己去问内核是否处理完成。

  • 异步: asynchronous, 被调用者通过状态、通知或回调机制主动通知调用者,即异步会主动返回被调用者的状态给调用者。

    进程发出请求调用后,内核会在调用处理完成后返回调用结果给进程,Nginx是异步的。

image-20200522220129787

阻塞/非阻塞

关注调用者在等待结果返回之前所处的状态

  • 阻塞: blocking,指I/O操作需要彻底完成后才返回到用户空间,调用结果返回之前,调用者被挂起,干不了别的事情。
  • 非阻塞: nonblocking, 指I/O操作被调用后立即返回给用户一个状态值,无需等到I/O操作彻底完成,最终的调用结果返回之前,调用者不会被挂起,可以去做别的事情。

image-20200522220624181

系统IO模型组合

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
以我去吃饭为例:我点了10个包子

同步与异步:
我点包子之后厨师是否告诉我:
同步:厨师做好包子后会放到指定位置,但是做好包子之前需要自己一次次去看包子做好没有,厨师不会在包子做好之后通知我。
异步:厨师做好包子后告诉我包子做好放哪了。
阻塞与非阻塞:
我点包子后的状态:
阻塞:在厨师做包子期间一直在包子盘子前面等着,不能干别的事情。
非阻塞:点完包子就可以去干别的事情,比如去逛逛街或者买买买。

IO模型组合:
同步阻塞:我点完包子后不能去做别的事情,而且不知道包子有没有做好,需要自己一直等着并一次次的问厨师做好没有。
同步非阻塞:点完包子后可以去做别的事情,但是不能长时间做别的事情,因为我还是不知道包子有没有做好,也要自己一直等着并一次次的问厨师做好没有,只能抽空做点别的。
异步阻塞:我点完包子后不能去做别的事情,但是厨师在做好包子后会告诉我,也就是我不用再一次次问厨师包子有没有做好了。
异步非阻塞:我点完包子后可以做别的事情,而且可以一直在做别的去事情,因为厨师在做好包子后会告诉我。

网络I/O

阻塞型、非阻塞型、复用型、信号驱动型、异步

同步阻塞型I/O(blocking IO)

阻塞I/O模型是最简单的I/O模型,用户线程在内核进行I/O操作时被阻塞

1
2
用户请求到达系统服务进程,然后进程通过系统调用read( )向内核发起IO读操作,即将用户请求由用户空间转到内核空间,内核接收到I/O请求后开始从磁盘读取文件到内核内存,即在等用户请求的文件从磁盘到达内核内存后,然后将接收的数据拷贝到用户空间,然后完成read操作。
用户请求需要等待内核将数据读取到进程内存后,处理用户的进程才可以继续处理该请求,整个I/O请求的过程中,请求进程是被阻塞的,这导致进程在发起I/O请求时,不能做任何事情,而且对CPU的资源利用率不够。

优点:程序简单,在阻塞等待数据期间进程挂起,基本不会占用CPU资源
缺点:每个连接需要独立的进程单独处理,当并发请求量大时为了维护程序,内存、进程切换开销较大,apache的prefork使用的是这种模式。

1
同步阻塞:程序向内核发送I/O请求后一直等待内核响应,如果内核处理请求的I/O操作不能立即返回,则进程将一直等待并不再接受新的请求,并由进程轮询查看I/O是否完成,完成后进程将I/O结果返回给Client,在IO没有返回期间进程不能接受其他客户的请求,而且是有进程自己去查看I/O是否完成,这种方式简单,但是比较慢,用的比较少。

image-20200522221931653

同步非阻塞型I/O(nonblocking IO)

用户请求进程向内核发起I/O请求时立即返回,但并未读取到任何数据,进程需要不断地发起I/O请求,直到数据到达进程空间的内存后,才真正读取到数据并继续执行,即前期需要一次次 “轮询”去查看请求是否数据是否准备报,但是此机制存在两个问题: 1.如果有大量文件描述符都要等,那么就得一个一个的read,这会带来大量的ContexSwitch (read是系统调用, 每调用一次就得在用户态和核心态切换一次) , 2.轮询的时间不好把握,这里是要猜多久之后数据才能到,等待时间设的太长,程序响应延迟就过大,但是设的太短,就会造成过于频繁的重试,干耗CPU而已,所以是比较浪费CPU的方式,因此一般很少直接使用这种模型,而是在其他IO模型中使用非阻塞I/O这一特性。

image-20200522222319548

I/O多路复用型(IO multiplexing)

I/O multiplexing就是我们说的select, poll, epoll, 有些地方也称这种I/O方式为event driven I/O(事件驱动I/O),
select/poll/epoll的好处就在于单个process就可以同时处理多个网络连接的I/O。它的基本原理就是select, poll,epoll这个function会不断的轮询所负责的所有socket,当某个socket有数据到达了,就通知用户进程。
当用户进程调用了select,那么整个进程会被block,而同时,kernel会”监视”所有select负责的socket,当任何一个socket中的数据准备好了,select就会返回, 这个时候用户进程再调用read操作,将数据从kernel拷贝到用户进程。

1
Apache prefork是此模式的主进程+多进程/单线程+select, work是主进程+多进程/多线程+poll模式

image-20200523081231277

信号驱动式IO(signal-driven IO)

用户进程可以通过sigaction系统调用注册一个信号处理程序,然后进程可以继续向下执行,当有IO操作准备就绪时,由内核通知触发一个SIGIO信号处理程序执行,然后将用户进程所需要的数据从内核空间拷贝到用户空间,此模型的优势在于等待数据报到达期间进程不被阻塞,进程可以继续执行,只要等待来自信号处理函数的通知。

  • 优点:进程没有在等待数据时被阻塞,内核直接返回调用接收信号,不影响进程继续处理其他请求因此可以提高资源的利用率
  • 缺点:信号IO在大量IO操作时可能会因为信号队列溢出导致没法通知
1
异步阻塞进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核收到进程请求后进行的IO如果不能立即返回,就由内核等待结果,直到IO完成后内核再通知进程,apache event模型就是主进程+多进程/多线程+信号驱动

image-20200523081750981

异步非阻塞IO(asynchronous)

相对于同步IO,异步IO不是顺序执行,用户进程进行aio_read系统调用之后,无论内核数据是否准备好,都会直接返回给用户进程,然后用户态进程可以去做别的事情,等到socket数据准备好了,内核直接复制数据给进程,然后从内核向进程发送通知,异步非阻塞IO的两个阶段,进程都是非阻塞的。
Linux提供了AIO库函数实现异步,但是用的很少,目前有很多开源的异步IO库,例如libevent、 libev、 libuv等,
异步过程如下图所示:

1
异步非阻塞:程序进程向内核发送IO调用后,不用等待内核响应,可以继续接受其他请求,内核调用的IO如果不能立即返回,内核会继续处理其他事物,直到IO完成后将结果通知给内核,内核再将IO完成的结果返回给进程,期间进程可以接受新的请求,内核也可以处理新的事物,因此相互不影响,可以实现较大的同时并实现较高的IO复用,因此异步非阻塞使用最多的一种通信方式,nginx就是是异步非阻塞模型。

image-20200523082402713

IO对比

这五种网络I/O模型中,越往后,阻塞越少,理论上效率也是最优前四种属于同步I/O,因为其中真正的I/O操作
(recvfrom)将阻塞进程/线程,只有异步I/O模型才与POSIX定义的异步I/O相匹配。

image-20200524131352724

实现方式

Nginx支持在多种不同的操作系统实现不同的事件驱动模型,但是其在不同的操作系统甚至是不同的系统版本上面的实现方式不尽相同,主要有以下实现方式:

1、select:
select库是在linux和windows平台都基本支持的事件驱动模型库,并且在接口的定义也基本相同,只是部分参数的含义略有差异,最大井发限制1024,是最早期的事件驱动模型。
2、poll:
在Linux的基本驱动模型,windows不支持此驱动模型,是select的升级版,取消了最大的并发限制,在编译nginx的时候可以使用–with-poll_ module和–without-poll_module这两个指定是否编译select库。
3、epoll:
epoll库是Nginx服务器支持的最高性能的事件驱动库之一, 是公认的非常优秀的事件驱动模型,它和select和poll有很大的区别,epoll是poll的升级版, 但是与poll的效率有很大的区别.
epoll的处理方式是创建一个待处理的事件列表,然后把这个列表发给内核,返回的时候在去轮询检查这个表,以判断事件是否发生,epoll支持一个进程打开的最大事件描述符的上限是系统可以打开的文件的最大数,同时epoll库的IO效率不随描述符数目增加而线性下降,因为它只会对内核上报的“活跃”的描述符进行操作。
4、rtsig:
不是一个常用事件驱动,最大队列1024,不是很常用
5、kqueue:
用于支持BSD系列平台的高效事件驱动模型,主要用在FreeBSD 4. 1及以上版本、openBSD 2。0级以上版本,NetBSD级以上版本及Mac os x平台上,该模型也是poll库的变种,因此和epoll没有本质上的区别,都是通过避免轮训操作提供效率。
6、/dev/poll:
用于支持unix衍生平台的高效事件驱动模型,主要在Solaris平台、HP/UX, 该模型是sun公司在开发Solaris系列平台的时候提出的用于完成事件驱动机制的方案,它使用了虚拟的/dev/pol1设备,开发人员将要见识的文件描述符加入这个设备,然后通过ioctl( )调用来获取事件通知,因此运行在以上系列平台的时候请使用/ dev/poll事件驱动机制
7、eventport:
该方案也是sun公司在开发Solaris的时候提出的事件驱动库,只是Solaris 10以上的版本,该驱动库看防止内核崩溃等情况的发生。
8、Iocp:
Windows系统上的实现方式,对应第5种(异步I/0) 模型。

常用模型汇总

\ 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不拷贝

常用模型通知对比

  • 水平触发–多次通知,需要关心数据是否取完,即数据取走之后即不再通知进程,以避免重复多次无效通知,通知效率较低。
  • 边缘触发–一次通知,需要关心数据是否取走,即只通知一次怎么保证数据被进程成功取走了,以避免数据丢失,通知效率较高。

image-20200523084013033

1
2
3
4
5
6
7
8
select:
POSIX所规定,目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点,本质上是通过设置或者检查存放fd标志位的数据结构来进行下一步处理
缺点
单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义FD_ SETSIZE, 再重新编译内核实现,但是这样也会造成效率的降低。
单个进程可监视的fd数量被限制,默认是1024, 修改此值需要重新编译内核。
对socket是线性扫描,即采用轮询的方法,效率较低。
select采取了内存拷贝方法来实现内核将FD消息通知给用户空间,这样一个用来存放大量fd的数据结构, 这样会
使得用户空间和内核空间在传递该结构时复制开销大。
1
2
3
4
5
poll:
本质上和select没有区别,它将用户传入的数组拷贝到内核空间,然后查询每个fd对应的设备状态。
其没有最大连接数的限制,原因是它是基于链表来存储的。
大量的fd的数组被整体复制于用户态和内核地址空间之间,而不管这样的复制是不是有意义。
poll特点是"水平触发”,如果报告了fd后,没有被处理,那么下次poll时会再次报告该fd。
1
2
3
4
5
6
7
8
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)系统调用使得进程之间通过映射同一个普通文件实现共享内存,普通文件被映射到进程地址空间后,进程可以像访问普通内存一样对文件进行访问。

传统方式拷贝数据

image-20200523084715500

mmap方式

image-20200523085054882

Nginx基础

Nginx: engineX, 2002年开始开发,2004年开源,2019年3月11日, Nginx公司被E5 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/

Nginx功能介绍

  • 静态的web资源服务器html,图片, js, css, txt等静态资源
  • 结合FastCGI/uWSGI/SCGI等协议反向代理动态资源请求
  • http/https协议的反向代理
  • imap4/pop3协议的反向代理
  • tcp/udp协议的请求转发(反向代理)

基础特性

  • 特性:
    • 模块化设计,较好的扩展性
    • 高可靠性
    • 支持热部署:不停机更新配置文件,升级版本,更换日志文件
    • 低内存消耗: 10000个keep- alive连接模式下的非活动连接,仅需2. 5M内存
    • event - driven, aio, mmap, sendfile
  • 基本功能:
    • 静态资源的web服务器
    • http协议反向代理服务器
    • pop3/ imap4协议反向代理服务器
    • FastCGI(LNMP), UWSGI (python)等协议
    • 模块化(非DSO) ,如zip, SSL模块

和web相关的功能

  • 虚拟主机(server)
  • 支持keep-alive和管道连接(利用一个连接做多次请求)
  • 访问日志(支持基于日志缓冲提高其性能)
  • url rewirte
  • 路径别名
  • 基于IP及用户的访问控制
  • 支持速率限制及并发数限制
  • 重新配置和在线升级而无须中断客户的工作进程

Nginx组织结构

web请求处理机制:

  1. 多进程方式: 服务器每接收到一个客户端请求就有服务器的主进程生成一个子进程响应客户端,直到用户关闭连接,这样的优势是处理速度快,各子进程之间相互独立,但是如果访问过大会导致服务器资源耗尽而无法提供请求。
  2. 多线程方式: 与多进程方式类似,但是每收到一个客户端请求会有服务进程派生出一个线程来个客户方进行交互,一个线程的开销远远小于一个进程,因此多线程方式在很大程度减轻了web服务器对系统资源的要求,但是多线程也有自己的缺点,即当多个线程位于同一个进程内工作的时候,可以相互访问同样的内存地址空间,所以他们相互影响,另外一旦主进程挂掉则所有子线程都不能工作了,IIS服务器使用了多线程的方式,需要间隔一段时间就重启一次才能稳定。

组织模型

Nginx是多进程组织模型,而且是一个由Master主进程和Worker工作进程组成。

image-20200523100147356

主进程(master process)的功能

  • 读取Nginx配置文件并验证其有效性和正确性
  • 建立、绑定和关闭socket连接
  • 按照配置生成、管理和结束工作进程
  • 接受外界指令,比如重启、升级及退出服务器等指令
  • 不中断服务,实现平滑升级,重启服务并应用新的配置
  • 开启日志文件,获取文件描述符
  • 不中断服务,实现平滑升级,升级失败进行回滚处理
  • 编译和处理perl脚本

工作进程(work process)的功能

  • 接受处理客户的请求

  • 将请求以此送入各个功能模块进行处理

  • IO调用,获取相应数据

  • 与后端服务器通信,接收后端服务器的处理结果

  • 缓存数据,访问缓存索引,查询和调用缓存数据

  • 发送请求结果,响应客户的请求

  • 接收主程序指令,比如重启、升级和退出等

    image-20200524132819924

进程间通信

工作进程是有主进程生成的,主进程使用fork()函数,在Nginx服务器启动过程中主进程根据配置文件决定启动工作进程的数量,然后建立一张全局的工作表用于存放当前未退出的所有的工作进程,主进程生成工作进程后会将新生成的工作进程加入到工作进程表中,并建立一个单向的管道并将其传递给工作进程,该管道与普通的管道不同,它是由主进程指向工作进程的单向通道,包含了主进程向工作进程发出的指令、工作进程ID、工作进程在工作进程表中的索引和必要的文件描述符等信息。

主进程与外界通过信号机制进行通信,当接收到需要处理的信号时,它通过管道向相关的工作进程发送正确的指令,每个工作进程都有能力捕获管道中的可读事件,当管道中有可读事件的时候,工作进程就会从管道中读取并解析指令,然后采取相应的执行动作,这样就完成了主进程与工作进程的交互。

工作进程之间的通信原理基本上和主进程与工作进程之间的通信是一样的,只要工作进程之间能够取得彼此的信息,建立管道即可通信,但是由于工作进程之间是完全隔离的,因此一个进程想要知道另外一个进程的状态信息就只能通过主进程来设置了。为了实现工作进程之间的交互,主进程在生成工作进程之后,在工作进程表中进行遍历,将该新进程的ID以及针对该进程建立的管道句柄传递给工作进程中的其他进程,为工作进程之间的通信做准备,当工作进程1向工作进程2发送指令的时候,首先在主进程给它的其他工作进程工作信息中找到2的进程ID,然后将正确的指令写入指向进程2的管道,工作进程2捕获到管道中的事件后,解析指令并进行相关操作,这样就完成了工作进程之间的通信。

image-20200523102552854

Nginx模块介绍

  • 核心模块:是Nginx服务器正常运行必不可少的模块,提供错误日志记录、配置文件解析、事件驱动机制、
    进程管理等核心功能
  • 标准HTTP模块:提供HTTP协议解析相关的功能,比如:端口配置、网页编码设置、HTTP响应头设置等等
  • 可选HTTP模块:主要用于扩展标准的HTTP功能,让Nginx能处理一些特殊的服务, 比如: Flash 多媒体传输、解析GeolP请求、网络传输压缩、安全协议SSL支持等
  • 邮件服务模块:主要用于支持Nginx的邮件服务,包括对POP3协议、IMAP 协议和SMTP协议的支持
  • 第三方模块:是为了扩展Nginx服务器应用,完成开发者自定义功能,比如: Json 支持、Lua 支持等

nginx高度模块化,但其模块早期不支持DSO机制; 1.9.11版本支持动态装载和卸载

image-20200523103805394

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模块) 和openssI (支持ssl模块)等。

Nginx yum安装

Install the prerequisites:

1
yum install yum-utils

To set up the yum repository, create the file named /etc/yum.repos.d/nginx.repo with the following contents:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
[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

By default, the repository for stable nginx packages is used. If you would like to use mainline nginx packages, run the following command:

1
yum-config-manager --enable nginx-mainline

To install nginx, run the following command:

1
yum install nginx

When prompted to accept the GPG key, verify that the fingerprint matches 573B FD6B 3D8F BC64 1079 A6AB ABF5 BD82 7BD9 BF62, and if so, accept it.

Nginx编译安装

下载源码包http://nginx.org/en/download.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[root@s2 -]# cd /usr/local/src/
[root@s2 src]# wget https://nginx.org/download/nginx-1.18.1.tar.gz
[root@s2 src]# tar xf nginx-1.18.1.tar.gz
[root@s2 src]# cd nginx-1.18.1/
编译是为了检查系统环境是否符合编译安装的要求,比如是否有gcc编译工具,是否支持编译参数当中的模块,并根据
开启的参数等生成Makefile文件为下一一步做准备:
[root@s2 nginx-1.18.1]# . /configure --prefix=/apps/nginx \
--user=nginx \
--group=nginx \
--with-http_ssl_module \
--with-http_V2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre\
--with-stream\
--with-stream_ssl_module\
--with-stream_realip_module
[root@s2 nginx-1.18.1]# make #编译步骤, 根据Makefile文件生成相应的模块
[root@s2 nginx-1.18.1]# make install #创建目录,并将生成的模块和文件复制到相应的目录:
[root@s2 nginx-1.18.1]# useradd nginx -s /sbin/nologin -u 2000 #以普通用户启动nginx
[root@s2 nginx-1.18.1]# chown nginx.nginx -R /apps/nginx/

备注: nginx完成安装以后,有四个主要的目录:

  • conf:该目录中保存了nginx所有的配置文件,其中nginx.conf是nginx服务器的最核心最主要的配置文件,其他的.conf则是用来配置nginx相关的功能的,例如fastcgi功能使用的是fastcgi.conf和fastcgi_params两个文件,配置文件一般都有个样板配置文件,是文件名.default结尾,使用的使用将其复制为并将default去掉即可。
  • html:该目录中保存了nginx服务器的web文件,但是可以更改为其他目录保存web文件,另外还有一个50x的web文件是默认的错误页面提示页面。
  • logs:该目录用来保存nginx服务器的访问日志错误日志等日志,logs目录可以放在其他路径,比
    如/var/logs/nginx里面。
  • sbin:该目录用来保存nginx二进制启动脚本,可以接受不同的参数以实现不同的功能。

验证版本及编译参数

1
2
3
4
5
6
7
8
[root@s2 nginx-1.16.1]# /apps/nginx/sbin/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=/apps/nginx --user=nginx --group=nginx --with-
http_ssl_module --with-http_v2_module --with-http_realip_module --with-
http_stub_status module --with-http_gzip_static_module --with-pcre --with-streamwith-stream_ssl_module --with-stream_realip_module

创建Nginx自启动脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
[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 wr ong
# 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=/apps/nginx/sbin/nginx -t
ExecStart=/apps/nginx/sbin/nginx
ExecReload=/bin/kill -S HUP $MAINPID
Killsignal=SIGQUIT
TimeoutStopSec=5
KillMode=process
PrivateTmp=true

[Install]
WantedBy=multi-user.target

Nginx核心配置详解

默认配置文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[root@s2 -]# grep -V "#” /apps/nginx/conf/nginx.conf | grep -V "A$"
#全局配置端,对全局生效,主要设置nginx的启动用户/组,启动的工作进程数量,工作模式,Nginx的PID路径, 日志路径等。
user nginx nginx;
worker_processes 1; #启动工作进程数数量
events { #events设置快, 主要影响nginx服务器与用户的网络连接,比如是否允许同时接受多个网络连接,使用哪种事件驱动模型处理请求,每个工作进程可以同时支持的最大连接数,是否开启对多工作进程下的网络连接进行序列化等。
worker_connections 1024;#设置单个nginx工作进程可以接受的最大并发,作为web服务器的时候最大并发数为worker_connections * worker_processes, 作为反向代理的时候为(worker_connections *worker_processes)/2
http { #http块是Nginx服务器配置中的重要部分,缓存、代理和日志格式定义等绝大多数功能和第三方模块都可以在这设置,http块可以包含多个server块,而一个server块中又可以包含多个location块,server 块可以配置文件引入、MIME-Type定义、日志自定义、是否启sendfile、连接超时时间和单个链接的请求上限等。
include mime.types;
default_type application/octet-stream;
sendfile on; #作为web服务器的时候打开sendfile加快静态文件传输,指定是否使用sendfile系统调用来传输文件,sendfile系统调用在两个文件描述符之间直接传递数据(完全在内核中操作),从而避免了数据在内核缓冲区和用户缓冲区之间的拷贝,操作效率很高,被称之为零拷贝,硬盘 >> kernel buffer (快速拷贝到kernelsocket buffer) >>协议栈。
keepalive_ timeout 65; #长连接超时时间, 单位是秒
server { #设置-个虚拟机主机,可以包含自己的全局快,同时也可以包含多个location模块。比如本虚拟机监听的端口、本虚拟机的名称和IP配置,多个server 可以使用一个端口,比如都使用80端口提供web服务
listen 80; #配置server 监听的端口
server_name localhost; #本server的名称,当访问此名称的时候nginx 会调用当前serevr内部的配置进程匹配。
location / { #location其实是server的一个指令,为nginx服务器提供比较多而且灵活的指令,都是在location中提现的,主要是基于nginx接受到的请求字符串,对用户请求的URL进行匹配,并对特定的指令进行处理,包括地址重定向、数据缓存和应答控制等功能都是在这部分实现,另外很多第三方模块的配置也是在location模块中配置。
root html; #相当于默认页面的目录名称,默认是相对路径,可以使用绝对路径配置。
index index.html index.htm; #默认的页面文件名称
}
error_ page 500 502 503 504 /50x.html; #错误页面的文件名称

全局配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
user nginx nginx; #启动Nginx工作进程的用户和组
worker_processes [number| auto]; #启动Nginx工作进程的数量
worker_cpu_affinity [0000001 0000010 0000100 00001000 | auto ]; #将Nginx工作进程绑定到指定的CPU核心,默认Nginx是不进行进程绑定的,绑定并不是意味着当前nginx进程独占以一核心CPU,但是可以保证此进程不会运行在其他核心上,这就极大减少了nginx的工作进程在不同的cpu核心上的来回跳转,减少了CPU对进程的资源分配与回收以及内存管理等,因此可以有效的提升nginx服务器的性能。
[root@s2 ~]#ps axo pid, cmd, psr,user | grep nginx
4106 nginx: master process / apps 1 root
4181 nginx: worker process θ nginx
4182 nginx: worker process 1 nginx
4184 grep --color-auto nginx θ root
#错误日志记录配置,语法: error log file [debug | info | notice| warn | error| crit |alert | emerg ]
#error_log logs/error.log;
#error_log logs/error.log notice;
error_log /apps/nginx/logs/error.log error;
#pid文件保存路径
pid /apps/nginx/logs/nginx.pid;
worker_priority 0; #工作进程nice值, -20到+19
worker_rlimit_nofile 65536; #这个数字包括Nginx的所有连接(例如与代理服务器的连接等),而不仅仅是与客户端的连接,另一个考虑因素是实际的并发连接数不能超过系统级别的最大打开文件数的限制,
[root@s2 ~]# watch -n1 'ps -axo pid, cmd,nice I grep nginx' #验证进程优先级

daemon off; #前台运行Nginx服务用于测试、 docker等环境。
master_process off|on; #是否开启Nginx的master-woker工作模式,仅用于开发调试场景。

events { #事件模型配置参数
worker_connections 65536; #设置 单个工作进程的最大并发连接数
use epoll; #使用epoll事件驱动, Nginx支持众多的事件驱动,比如select、poll、 epoll, 只能设置在events模块中设置。
accept_mutex on; #优化同一时刻只有一个请求而避免多个睡眠进程被唤醒的设置,on为防止被同时唤醒,默认为off,全部唤醒的过程也成为"惊群”,因此nginx刚安装完以后要进行适当的优化。
multi_accept on; #Nginx服务器的每个工作进程可以同时接受多个新的网络连接,但是需要在配置文件中配置,此指令默认为关闭,即默认为一个工作进程只能一次接受一个新的网络连接, 打开后几个同时接受多个。

http详细配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
http {
include mime .types; #导入支持的文件类型
default_type application/octet-stream; #设置默认的类型,会提示下载不匹配的类型文件

#日志配置部分
#log_format main '$remote.addr.$remote_user [$time_1ocal] "Srequest"
#'$status $body_bytes_sent
#"$http_referer"
#"$http_user_agent" "$http_x_forwarded_for"';
#access_1og logs/access.1og main;

#自定义优化参数
sendfile on; #实现文件零拷贝
#tcp_nopush on; #在开启了 sendfile的情况下,合并请求后统一发送给客户端。
#tcp_nodelay off; #在开启了keepalived模式下的连接是否启用TCP. NODELAY选项,当为off时,延迟0.2s发送,默认on时,不延迟发送,立即发送用户相应报文。
keepalive_timeout 65; #设置会话保持时间
#gzip on; 开启文件压缩
server {
listen 80; #设置监听地址和端口
server_name localhost; #设置server name, 可以以空格隔开写多个并支持正则表达式,如*.kinmfer.com www.kinmfer.* www.(site\d+)\.kinmfer\.com$ default_server
#匹配机制:
#(1)首先是字符串精确匹配;
#(2)左侧*通配符;
#(3)右侧*通配符;
#(4)正则表达式;

#charset koi8-r; #设置编码格式,默认是俄语格式,可以改为utf-8
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;

#location ~ \.php$ { #以fastcgi的方式转发php请求到php处理
#root html;
#fastcgi_ pass 127.0.0.1:9000;
#fastcgi_index index.php;
#fastcgi param SCRIPT_ FILENAME /scripts$fastcgi script_ name;
#include fastcgi_params;
#}
# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht { #拒绝web形式访问指定文件,如很多的网站都是通过. htaccess文件来改变自己的重定向等功能。
# deny all;
#}
location ~ /passwd.html {
deny all;
}
}

# HTTPS server
#
#server { #https服务器配置
# listen 443 ssl;
# server_name localhost;
# ssl_ certificate cert.pem;
# ssl_certificate_key cert.key;
# ssl_session_cache shared:SSL:1m;
# ssl_session_timeout 5m;
# ssl_ciphers HIGH: !aNULL: !MD5;
# ssl_prefer_server.ciphers on;
# location / {
#root html;
#index index.html index.htm;
#}

核心配置示例

root和alias

root:指定web的家目录,在定义location的时候,文件的绝对路径等于root+location,如:

1
2
3
4
5
6
7
8
9
10
11
12
13
server {
listen 80;
server_name www.kinmfer.com
location / {
root /data/nginx/html/pc ;
}
location /about {
root /data/nginx/html/pc; #必须要在html目录中创建一个about目录才可以访问,否则报错。
index index.html;
}
[root@s2 ~]# mkdir /data/nginx/html/pc/about
[root@s2 ~]# echo about > /data/nginx/html/pc/about/index.html
重启Nginx并访问测试

alias:定义路径别名,会把访问的路径重新定义到其指定的路径,如:

1
2
3
4
5
6
7
8
9
server {
listen 80;
server_name www.kinmfer.com;
location /about { #使用alias的时候uri后面如果加了斜杠则下面的路径配置必须加斜杠,否则403
alias /data/nginx/html/pc; #当访问about的时候, 会显示alias定义的/data/nginx/html/pc 里面的内容。
index index .html;
}
重启Nginx并访问测试
http://www.kinmfer.com/about/index.html #访问指定文件资源

location的详细使用

在没有使用正则表达式的时候,nginx会先在server中的多个location选取匹配度最高的一个uri, uri是用户请求的字符串,即域名后面的web文件路径,然后使用该location模块中的正则uri和字符串,如果匹配成功就结束搜索,并使用此location处理此请求。

1
2
3
4
5
6
7
8
9
10
11
语法规则: location [=|~|~*|^~] /uri/ { ... }
= #用于标准uri前,需要请求字串与uri精确匹配,如果匹配成功就停止向下匹配并立即处理请求。
~ #用于标准uri前,表示包含正则表达式井且区分大小写,并且匹配
!~ #用于标准uri前, 表示包含正则表达式并且区分大小写,并且不匹配

~* #用于标准uri前, 表示包含正则表达式并且不区分大写,并且匹配
!~* #用于标准uri前,表示包含正则表达式并且不区分大小写,并且不匹配
^~ #用于标准uri前, 表示包含正则表达式并且匹配以什么开头
$ #用于标准uri前, 表示包含正则表达式并且匹配以什么结尾
\ #用于标准uri前, 表示包含正则表达式并且转义字符。可以转. * ?等
* #用于标准uri前,表示包含正则表达式并且代表任意长度的任意字符

location优先级: (location =) > (location ^~ 路径) > (location ,* 正则顺序) > (location 部分起始路径) > (/)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
直接匹配网站根会加速Nginx访问处理:
location = / {
..... :
}
location / {
......:
}

静态资源配置:
location ^~ /static/ {
....
}
#或者
location ~* \.(gif|jpg|jpeg|png|css|js|ico)$ {
.....
}

多应用配置
location ~* /app1 {
......
}
location ~* /app2 {
......
}

Nginx四层访问控制

访问控制基于模块ngx_http_access_module实现,可以通过匹配客户端源IP地址进行限制。

1
2
3
4
5
6
7
8
9
location /about {
alias /data/nginx/html/pc;
index index.html;
deny 192.168.1.1;
allow 192.168.1.0/24;
allow 10.1.1.0/16;
allow 2001:0db8::/32;
deny all; #先允许小部分, 再拒绝大部分
}

Nginx账户认证功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@s2 ~]# yum install httpd-tools -y #Centos
[root@s2 ~]# apt install apache2-utils #Ubuntu
[root@s2 ~]# htpasswd -cbm /apps/nginx/conf/ .htpasswd user1 123456
Adding password for user user1
[root@s2 -]# htpasswd -bm /apps/nginx/conf/ .htpasswd user2 123456
Adding password for user user2
[root@s2 ~]# tail /apps/nginx/conf/.htpasswd
user1:$apr$SRjm0u2Kr$VHvkAIc50Yg.3ZoaGwaGq/
user2:$apr1$nIqnxoJBSLR9W1DTJT.viDJhXa6wHV.
[root@s2 ~]# vim /apps/nginx/conf/conf.d/pc.conf
location = /login/ {
root /data/nginx/html/pc;
index index.html;
auth_basic "login password";
auth_basic_user_file /apps/nginx/conf/.htpasswd;
}

自定义错误页面

1
2
3
4
5
6
7
listen 80;
server_name www.kinmfer.com;
error_page 500 502 503 504 404 /error.html;
location = /error.html {
root html;
}
重启nginx并访问不存在的页面进行测试

自定义访问日志

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
[root@s2 -]# mkdir /data/nginx/logs
listen 80;
server_name www.kinmfer.com;
error_page 500 502 503 504 404 /error.html; #默认目录下面创建error.html页面
access_log /data/nginx/logs/www-kinmfer-com-access.log;
error_log /data/nginx/logs/www-kinmfer-com-error.log;
location = /error.html {
root html;
}
也可以单独放在location中
location = /error.html {
root html;
access_log /data/nginx/logs/www-kinmfer-com-access.log;
error_log /data/nginx/logs/www-kinmfer-com-error.log;
}
重启nginx并访问不存在的页面进行测试并验证是在指定目录生成新的日志文件

检测文件是否存在

try_files会按顺序检查文件是否存在,返回第一个找到的文件或文件夹 (结尾加斜线表示为文件夹),如果所有文件或文件夹都找不到,会进行一个内部重定向到最后一个参数。只有最后一个参数可以引起一个内部重定向,之前的参数只设置内部URI的指向。最后一个参数是回退URI且必须存在,否则会出现内部500错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
location /about {
root /data/nginx/html/pc ;
#alias /data/nginx/html/pc;
index index.html;
#try_files $uri /about/default.html;
#try_files $uri $uri/index.html $uri.html/about/default.html;
try_files $uri $uri/index.html $uri.html =489;
}
[root@s2 ~]# echo "default">/data/nginx/html/pc/about/default .html
重启nginx并测试,当访问到http://www.kinmfer.com/about/xx.html等不存在的uri会显示default,如果是自定义的状态码则会显示在返回数据的状态码中,如:
[root@s2 about]# curl --head http://www.kinmfer.com/about/xx.html
HTTP/1.1 489 #489就是自定义的状态返回码
Server: nginx
Date: Thu, 21 Feb 2019 00:11:40 GMT
Content-Length: θ
Connection: keep-alive
Keep-Alive: timeout=65

长连接配置

keepalive_timeout number; #设定保持连接超时时长,0表示禁止长连接,默认为75s, 通常配置在http字段作
为站点全局配置

keepalive_requests number; #在一次长连接上所允许请求的资源的最大数量,默认为100次

keepalive_disable none | browser …;#对哪种浏览器禁用长连接

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
keepalive_requests 200;
keepalive_timeout 65 60;
开启长连接后,返回客户端的会话保持时间为60s,单次长连接累计请求达到指定次数请求或65秒就会被断开,后面的
60为发送给客户端应答报文头部中显示的超时时间设置为60s:如不设置客户端将不显示超时时间。
Keep-Alive: timeout=60
#浏览器收到的服务器返回的报文
如果设置为0表示关闭会话保持功能,将如下显示:
Connection:close #浏览器收到的服务器返回的报文
使用命令测试:
[root@s3 apps]# telnet www.kinmer.com 80
Trying 172.18.200.102...
Connected to www.kinmfer.com.
Escape character is '^]'.
GET / HTTP/1.1
HOST: www.kinmfer.com
#Response Headers(响应头信息):
HTTP/1.1 200 OK
Server: nginx/1.14.2
Date: Thu, 14 Mar 2019 17:23:46 GMT
Content-Type: text/html
Content-Length: 7
Last-Modified: Thu, 14 Mar 2020 14:54:50 GMT
Connection: keep- alive
Keep-Alive: timeout=60
ETag: "5c8a6b3a-7"
Accept-Ranges: bytes
#页面内容
pc web

作为下载服务器

1
2
3
4
5
6
7
8
9
10
11
[root@s2 about]# mkdir /data/nginx/html/pc/download
#download不需要index.html文件
[root@s2 about]# vim /apps/nginx/conf/conf.d/pc.conf
location /download {
root /data/nginx/html/pc;
autoindex on; #自动索引功能
autoindex_exact_size on; #计算文件确切大小(单位bytes) ,off只显示大概大小(单位kb、 mb、gb)
autoindex_localtime on; #显示本机时间而非GMT(格林威治)时间
#limit_rate 10k; ##限制响应给客户端的传输速率,单位是bytes/second, 默认值0表示无限制
[root@s2 pc]# cp /root/anaconda-ks.cfg /data/nginx/html/pc/download/
重启Nginx并访问测试下载页面

作为上传服务器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
client_max_body_size 1m; #设置允许客户端上传单个文件的最大值,默认值为1m
client_body_buffer_size size;#用于接收每个客户端请求报文的body部分的缓冲区大小;默认16k;超出此大小时,其将被暂存到磁盘上的由下面client_body_temp_path指令所定义的位置
client_body_temp_path path [level1 [level2 [1evel3]]];
#设定存储客户端请求报文的body部分的临时存储路径及子目录结构和数量,目录名为16进制的数字,使用hash之后的值从后往前截取1位、2位、2位作为文件名:
[root@s3 ~]# md5sum /data/nginx/html/pc/index.html
95f6f65f498c74938064851b1bb963d4 /data/nginx/html/pc/index.html
1级目录占1位16进制,即2^4=16个目录0-f
2级目录占2位16进制,即2^8=256个目录00-ff
3级目录占2位16进制,即28=256个目录00-ff

配置示例:
client_max_body_size 10m;
client_body_buffer_size 16k;
client_body_temp_path /apps/nginx/temp 1 2 2; #reload Nginx会自动创建temp目录

image-20200523202125465

其他配置

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
limit_except method ... { ... },仅用于location
限制客户端使用除了指定的请求方法之外的其它方法
method:GET, HEAD, POST, PUT, DELETE, MKCOL, COPY, MOVE, OPTIONS, PROPFIND,PROPPATCH,LOCK,UNLOCK,PATCH
limit_except GET {
allow 192.168.0.0/24;
allow 192.168.7.101;
deny all;
}
#除了GET和HEAD之外其它方法仅允许192.168.1.0/24网段主机使用
[root@s2 about]# mkdir /data/nginx/html/pc/upload
[root@s2 about]# echo "upload" > /data/nginx/html/pc/upload/index.html
[root@s2 about]# vim /apps/nginx/conf/conf.d/pc.conf
location /upload {
root /data/kinmfer/pc;
index index.html;
limit_except GET {
allow 172.18.200.101;
deny all;
}
}
1
2
3
4
5
6
7
8
aio on| off #是否启用asynchronous file I/O(AIO)功能,需要编译开启
linux 2.6以上内核提供以下几个系统调用来支持aio:
1、SYS_io_setup:建立aio的context
2、SYS_io_submit: 提交I/O操作请求
3、SYS_i0_getevents:获取已完成的I/O事件
4、SYS_i0_cancel:取消I/O操作请求
5、SYS_i0_destroy: 毁销aio的context

1
directio size | off; #操作完全和aio相反, aio是读取文件而directio是写文件到磁盘,启用直接I/O,默认为关闭,当文件大于等于给定大小时,例如directio 4m,同步(直接) 写磁盘,而非写缓存。
1
2
3
4
5
6
7
8
9
open_file_cache off; #是否缓存打开过的文件信息
open_file_cache max=N [inactive=time];
nginx可以缓存以下三种信息:
(1)文件元数据:文件的描述符、文件大小和最近一次的修改时间
(2)打开的目录结构
(3)没有找到的或者没有权限访问的文件的相关信息
max=N:可缓存的缓存项上限数量;达到上限后会使用LRU(Least recently used, 最近最少使用)算法实现
管理
inactive=time:缓存项的非活动时长,在此处指定的时长内未被命中的或命中的次数少于open_file_cache_min_uses指令所指定的次数的缓存项即为非活动项,将被删除
1
2
3
open_file_cache_errors on | off;
是否缓存查找时发生错误的文件一类的信息
默认值为off
1
open_file_cache_min_uses number ;指令的inactive参数指定的时长内,至少被命中此处指定的次数方可被归类为活动项默认值为1
1
2
3
4
5
6
open_file_cache_valid time;
缓存项有效性的检查验证频率,默认值为60s
open_file_cache max=10000 inactive=60s; #最大缓存10000个文件, 非活动数据超时时长605
open_file_cache_valid 60s; #每间隔60s检查一下缓存 数据有效性
open_file_cache_min_uses 5; #60秒内至少被命中访问5次才被标记为活动数据
open_file_cache_errors on; #缓存错误信息
1
server_tokens off; #隐藏Nginx server版本。

Nginx高级配置

Nginx状态页

在编译安装nginx的时候需要添加编译参数–with-http-stub-status-module,否则配置完成之后监测会是提示语法错误。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
配置示例: 
location /nginx_status {
stub_status;
access_log off;
allow 192.168.0.0/16;
allow 127.0.0.1;
deny all;
状态页用于输出nginx的基本状态信息:
输出信息示例:
Active connections: 291
server accepts handled requests
16630948 16630948 31070465
上面三个数字分别对应accepts, handled, requests三个值
Reading: 6 Writing: 179 waiting: 106

Active connections: 当前处于活动状态的客户端连接数, 包括连接等待空闲连接数。
accepts:统计总值,Nginx自启动后已经接受的客户端请求的总数。
handled:统计总值,Nginx自启动后已经处理完成的客户端请求的总数,通常等于accepts,除非有因
worker_connections限制等被拒绝的连接。
requests:统计总值,Nginx自启动后客户端发来的总的请求数。
Reading:当前状态,正在读取客户端请求报文首部的连接的连接数。
Writing:当前状态,正在向客户端发送响应报文过程中的连接数。
Waiting:当前状态,正在等待客户端发出请求的空闲连接数,开启keep-alive的情况下, 这个值等于active-
(reading+writing),

Nginx第三方模块

第三模块是对nginx的功能扩展,第三方模块需要在编译安装Nginx的时候使用参数–add-module=PATH指定路径添加,有的模块是由公司的开发人员针对业务需求定制开发的,有的模块是开源爱好者开发好之后上传到github进行开源的模块,nginx支持第三方模块需要从源码重新编译支持,比如开源的echo模块https://github.com/openresty/echo-nginx-module:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
[root@s2 pc]# systemctl stop nginx
[root@s2 pc]# vim /apps/nginx/conf/conf.d/pc.conf
location /main {
index index. html;
default_type text/html;
echo "hello world, main-->";
echo_reset_timer ;
echo_location /sub1;
echo_location /sub2;
echo "took $echo_timer_elapsed sec for total.";
}
location /sub1 {
echo_sleep 1;
echo sub1;
}
location /sub2 {
echo_sleep 1;
echo sub2;
}
[root@s2 pc]# /apps/nginx/sbin/nginx -t
nginx: [emerg] unknown directive "echo_reset_timer" in
/apps/nginx/conf/conf.d/pc.conf:86
nginx: configuration file /apps/nginx/conf/nginx.conf test failed

#解决以上报错问题
[root@s2 src]# yum install git -y
[root@s2 src]# git clone https://github.com/openresty/echo-nginx-module.git
[root@s2 src]# cd nginx-1.12.2/
[root@s2 src]# ./configure \
--prefix=/apps/nginx \
--user-nginx --group=nginx \
--with-http_ss1_module \
--with-http_v2_module \
--with-http_realip_module \
--with-http_stub_status_module \
--with-http_gzip_static_module \
--with-pcre \
--with-stream \
--With-stream_ssl_module \
--with-stream_realip_module \
--with-http_perl_module \
--add-module=/usr/local/src/echo-nginx-module
[root@s2 src]# make && make install
#确认语法检测通过
[root@s2 pc]# /apps/nginx/sbin/nginx -t
nginx: the configuration file /apps/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /apps/nginx/conf/nginx.conf test is successful
#重启nginx访问测试:
[root@s2 pc]# systemctl restart nginx
[root@s2 pc]# curl http://www.kinmfer.com/main
hello world, main-->
sub1
sub2
took 2.010 sec for total.

Nginx变量使用

Nginx的变量可以在配置文件中引用,作为功能判断或者日志等场景使用,变量可以分为内置变量和自定义变量,内置变量是由nginx模块自带,通过变量可以获取到众多的与客户端访问相关的值。

内置变量

1
2
$remote_addr;
#存放了客户端的地址,注意是客户端的公网IP,也就是一家人访问一个网站,则会显示为路由器的公网IP。
1
2
$args;
#变量中存放了URL中的指令,例如http://www.kinmfer.com/main/index.do?id=20190221&partner=search中的id=20190221&partner=search
1
2
$document_root;
#保存了针对当前资源的请求的系统根目录,如/apps/nginx/html
1
2
3
$document_uri;
#保存了当前请求中不包含指令的URI,注意是不包含请求的指令,比如
http://www.kinmfer.com/main/index.do?id=20190221&partner-search会被定为/main/index.do
1
2
$host;
#存放了请求的host名称。
1
2
$http_user_agent;
#客户端浏览器的详细信息
1
2
$http_cookie; 
#客户端的cookie信息
1
2
3
limit_rate 10240;
echo $limit_rate;
#如果nginx服务器使用limit_rate配置了显示网络速率,则会显示,如果没有设置,则显示0。
1
2
$remote_port;
#客户端请求Nginx服务器时随机打开的端口,这是每个客户端自己的端口。
1
2
$remote_user;
#已经经过Auth Basic Module验证的用户名。
1
2
$request_body_file;
#做反向代理时发给后端服务器的本地资源的名称。
1
2
$request_method;
#请求资源的方式,GET/PUT/DELETE等
1
2
3
$request_filename;
#当前请求的资源文件的路径名称,由root或alias指令 与URI请求生成的文件绝对路径,
#如/apps/nginx/html/main/index.html
1
2
$request_uri; 
#包含请求参数的原始URI,不包含主机名,如: /main/index.do?id=20190221&par tner=search
1
2
$scheme;
#请求的协议,如ftp, https, http等。
1
2
$server_protocol;
#保存了客户端请求资源使用的协议的版本,如HTTP/1.0, HTTP/1.1, HTTP/2.0等。
1
2
$server_addr;
#保存了服务器的IP地址。
1
2
$server_name;
#请求的服务器的主机名。
1
2
$server_port;
#请求的服务器的端口号。

自定义变量

假如需要自定义变量名称和值,使用指令set $variable value;,则方法如下:
Syntax: set ​$variable value;
Default: -
Context: server, location, if

1
2
3
4
5
set $name kinmfer;
echo $name;
set $my_port $server_port;
echo $my_port;
echo "$server_name:$server_port";

Nginx自定义访问日志

访问日志是记录客户端即用户的具体请求内容信息,全局配置模块中的error_log是记录nginx服务器运行时的日志保存路径和记录日志的level, 因此有着本质的区别,而且Nginx的错误日志一般只有一个,但是访问日志可以在不同server中定义多个,定义一个日志需要使用access_log指定日志的保存路径,使用log_format指定日志的格式,格式中定义要保存的具体日志内容。

自定义默认格式

如果是要保留日志的源格式,只是添加相应的日志内容,则配置如下:

1
2
3
4
5
6
7
8
9
10
log_format nginx_format1 '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
"$http_user_agent" "$http_x_forwarded_for"'
'$server_name:$server_port';
access_log logs/access.log nginx_format1;
#重启nginx并访问测试日志格式
==> /apps/nginx/logs/access.log <==
192.168.0.1.。[22/Feb/2019:08:44:14 +0800] "GET /favicon.ico HTTP/1.1”404 162 "。”
"Mozilla/5.0 (Windows NT 6.1; Win64; x64; rV:65.0) Gecko/2
0100101 Firefox/78.0" ". "www.kinmfer.com:80

自定义json格式日志

Nginx的默认访问日志记录内容相对比较单一, 默认的格式也不方便后期做日志统计分析,生产环境中通常将
nginx日志转换为json日志,然后配合使用ELK做日志收集-统计-分析。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
log_format access_json '{"@timestamp" :"$time iso8601", '
'"host":"$server_addr", '
'"clientip":"$remote_addr", '
'"size":$body_bytes_sent,
'"responsetime":$request_time, '
'"upstreamtime" :"$upstream_response_time", '
'"upstreamhost": "$upstream_addr", '
'"http_host": "$host", '
'"uri":"$uri",
' domain": "$host", '
'"xff":"$http_x_forwarded_for", '
'"referer": "$http_referer",
'"tcp_xff": "$proxy_protocol_addr", '
'"http_user_agent": "$http_user_agent", '
'"status":"$status"}';
access_log /apps/nginx/1ogs/access_json.log access_json;

#重启Nginx并访问测试日志格式
{"@timestamp" :"2019-02-22T08:55:32+08:00", "host":"192.168.7.102", "clientip":"192.168.0.1", "size" :162, "responsetime" :0.000, "upstreamtime":" -", "upstreamhost" :"-", "http_host" : "www.kinmfer.com" , "uri":"/favicon.ico" , "domain": "www.kinmfer.com" , "xff":"-", "referer":"-","tcp_xff":"", "http_user_agent" :"Mozilla/5.0 (Windows NT 6.1; Win64;x64; rv:65.0) Gecko/20100101 Firefox/65", "status":"404"}

json格式的日志访问统计

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
#!/usr/bin/env python
#coding:utf-8
status_200= []
status_404= []
with open("access_ json.log") as f:
for line in f.readlines():
line = eval(line)
if line.get("status") == "200":
status_200.append(line.get)
elif line.get("status") == "404":
status_404.append(line.get)
else:
print("状态码ERROR" )
print ("状态码200的有--:", len(status_200))
print ("状态码404的有--:", len(status_404))
#保存日志文件到指定路径并进行测试:
[root@s2 -]# python nginx_json. py
状态码200的有--: 1910
状态码404的有--: 13

Nginx压缩功能

Nginx支持对指定类型的文件进行压缩然后再传输给客户端,而且压缩还可以设置压缩比例,压缩后的文件大小将比源文件显著变小,这样有助于降低出口带宽的利用率,降低企业的IT支出,不过会占用相应的CPU资源。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#启用或禁用gzip压缩,默认关闭
gzip on | off;

#压缩比由低到高从1到9,默认为1
gzip_comp_level level;

#禁用IE6 gzip功能
gzip_disable "MSIE [1-6]\.";

#gzip压缩的最小文件,小于设置值的文件将不会压缩
gzip_min_length 1k;

#启用压缩功能时,协议的最小版本,默认HTTP/1.1
gzip_http_version 1.0 | 1.1;

#指定Nginx服务需要向服务器申请的缓存空间的个数*大小,默认32 4k|16 8k;
gzip_buffers number size;

#指明仅对哪些是型的资源执行压缩操作;默认为gzip_ .types text/html, 不用显示指定,否则出错
gzip_types mime-type ...:

#如果启用压缩,是否在响应报文首部插入"Vary: Accept - Encoding"
gzip_vary on| off;

#重启nginx并进行访问测试压缩功能
[root@s2 pc]# cp /apps/nginx/logs/access.log /data/nginx/html/pc/test.html
[root@s2 pc]# echo "test1" > /data/nginx/html/pc/test1.html #小于1k的文件测试是否会压缩
[root@s2 pc]# vim /apps/nginx/conf/nginx.conf
gzip on;
gzip_comp_level 3;
gzip_min_length 1k;
gzip_types text/plain application/javascript application/x-javascript text/cssapplication/xml text/javascript application/x-httpd-php image/jpeg image/gif
image/png;
gzip_vary on;

重启Nginx并访问测试:
[root@s2 pc]# curl --head --compressed http://www.kinmfer.com
HTTP/1.1 200 OK
Server: nginx
Date: Thu, 21 Feb 2019 13:06:18 GMT
Content-Type: text/html
Content-Length: 7
Last-Modified: Tue, 19 Feb 2019 04:09:32 GMT
Connection: keep- alive
Keep-Alive: timeout=65
ETag: "5c6b817c-7"
Accept-Ranges: bytes

[root@s2 -]# curl --head - -compressed http://www.kinmfer.com/test.html
HTTP/1.1 200 OK
Server: nginx
Date: Fri, 22 Feb 2019 01:52:23 GMT
Content-Type: text/html
Last-Modified: Thu, 21 Feb 2019 10:31:18 GMT
Connection: keep-alive
Keep-Alive: timeout=65
Vary: Accept-Encoding
ETag: W/"5c6e7df6-171109"
Content-Encoding: gzip #压缩传输

Https功能

Web网站的登录页面都是使用https加密传输的,加密数据以保障数据的安全,HTTPS能够加密信息,以免敏感
信息被第三方获取,所以很多银行网站或电子邮箱等等安全级别较高的服务都会采用HTTPS协议,HTTPS其实是有两部分组成: HTTP+SSL/TLS, 也就是在HTTP上又加了一层处理加密信息的模块。服务端和客户端的信息传
输都会通过TLS进行加密,所以传输的数据都是加密后的数据。

image-20200524093251561

ssl配置参数

nginx的https功能基于模块ngx_https_ssl_module实现,因此如果是编译安装的nginx要使用参数ngx_https_ssl_module开启ssI功能,但是作为nginx的核心功能,yum安装的nginx默认就是开启的,编译安装的nginx需要指定编译参数–with-https_ssl_module开启。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ssl on| off;
#为指定的虚拟主机配置是否启用ssl功能,此功能在1.15.0废弃,使用listen [ssl]替代。
ssl_certificate /path/to/file;
#当前虚拟主机使用使用的公钥文件,一般是crt文件
ssl_certificate_key /path/to/file;
#当前虚拟主机使用的私钥文件,一般是key文件
ssl_protocols [SSLv2] [SSLv3] [TLSV1] [TLSV1.1] [TLSV1.2];
#支持ssl协议版本,早期为ssl, 现在是TSL,默认为后三个
ssl_session_cache off | none | [builtin[:size]] [shared:name:size];
#配置ssl缓存
off:关闭缓存
none:通知客户端支持ssl session cache, 但实际不支持
builtin[:size]:使用openSSL内建缓存,为每worker进程私有
[shared:name:size]:在各worker之间使用一个共享的缓存,需要定义一个缓存名称和缓存空间大小,一兆可以存储4000个会话信息,多个虚拟主机可以使用相同的缓存名称。
ssl_session_timeout time;#客户端连接可以复用ssl session cache中缓存的有效时长,默认5m

签署证书请看此篇博客:

https://kinmfer.github.io/2020/05/05/Linux%E5%9F%BA%E7%A1%80-14-%E5%8A%A0%E5%AF%86%E5%92%8C%E5%AE%89%E5%85%A8/#CA和申请证书

1
2
3
4
5
6
7
8
listen 80;
listen 443 ssl;
#不能放在location中
ssl_certificate /apps/nginx/certs/www.kinmfer.com.crt;
ssl_certificate_key /apps/nginx/certs/www.kinmfer.com.key;
ssl_session_cache shared:sslcache:20m;
ssl_session_timeout 10m;
#重启Nginx并访问验证

实现多域名https

Nginx支持基于单个IP实现多域名的功能,并且还支持单IP多域名的基础之上实现HTTPS,其实是基于Nginx的SNI (Server Name Indication)功能实现,SNI是为了解决个Nginx服务器内使用一个IP绑定多个域名和证书的功能,其具体功能是客户端在连接到服务器建立SSL链接之前先发送要访问站点的域名(Hostname) , 这样服务器再根据这个域名返回给客户端一个合适的证书。

步骤与以上类似

关于favicon.ico

favicon.ico文件是浏览器收藏网址时显示的图标,当客户端使用浏览器问页面时,浏览器会自己主动发起请求获取页面的favicon.ico文件,但是当浏览器请求的favicon.ico文件不存在时,服务器会记录404日志,而且浏览器也会显示404报错。

解决办法

1
2
3
4
5
6
7
8
9
10
11
#一:服务器不记录访问日志:
#location = /favicon.ico {
# log_not_found off;
# access_log off;
#}
#二:将图标保存到指定目录访问:
#location ~ ^/favicon\.ico$
location = /favicon.ico {
root /data/nginx/html/pc/images;
expires 90d; #设置文件过期时间
}

安全选项

自定义Nginx版本号

更改Nginx源码信息并重新编译Nginx

1
2
# vim src/http/ngx_http_header_filter_module.c
49 static u_char ngx_http_server_string[] = "Server: kinmfer666" CRLF; #定义响应报文中的server字段信息

升级OpenSSL版本

心脏出血(英语: Heartbleed),也简称为心血漏洞,是一个出现在加密程序库OpenSSL的安全漏洞,该程序库广泛用于实现互联网的传输层安全(TLS) 协议。它于2012年被引入了软件中,2014年4月首次向公众披露。只要使用的是存在缺陷的OpenSSL实例,无论是服务器还是客户端,都可能因此而受到攻击。此问题的原因是在实现TLS的心跳扩展时没有对输入进行适当验证(缺少边界检查),因此漏洞的名称来源于”心跳”(heartbeat) 。该程序错误属于缓冲区过读,即可以读取的数据比应该允许读取的还多。

1
2
3
4
5
6
7
8
9
10
11
12
13
准备OpenSSL源码包:
# pwd
/usr/1ocal/src
# tar xvf openssl-1.1.1d
编译安装Nginx并制定新版本OpenSSL路径:
# cd /usr/1ocal/src/nginx-1.16.1/
#./configure --prefix=/apps/nginx --user=nginx --group=nginx --with-http_ss1_module --with-http_v2_module --with-http_realip_module --with-http_stub_status_module --with-http_gzip_static_module --with-pcre --with-stream --with-stream_ssl_module --with-stream_realip_module --with-select_module --with-file-aio -- add-module=/usr/1ocal/src/echo-nginx-module --with-openssl=/usr/1ocal/src/openssl-1.1.1d
# make && make install
验证并启动Nginx:
# /apps/nginx/sbin/nginx -t
nginx: the configuration file /apps/nginx/conf/nginx.conf syntax is ok
nginx: configuration file /apps/nginx/conf/nginx.conf test is successful
# /apps/nginx/sbin/nginx

rewrite 功能

通过正则表达式的匹配来改变URI,可以同时存在一个或多个指令,按照顺序依次对URI进行匹配,rewrite主要是针对用户请求的URL或者是URI做具体处理。

 用法:rewrite regex replacement [flag];

 (1) rewrite flag

  利用nginx的rewrite的指令,可以实现url的重新跳转,rewrtie有四种不同的flag,分别是redirect(临时重定向)、permanent(永久重定向)、break和last。其中前两种是跳转型的flag,后两种是代理型,跳转型是指由客户端浏览器重新对新地址进行请求,代理型是在WEB服务器内部实现跳转的。

1
2
3
4
redirect;         #临时重定向,重写完成后以临时重定向方式直接返回重写后生成的新URL给客户端,由客户端重新发起请求;使用相对路径,或者http://或https://开头,状态码:302
permanent; #重写完成后以永久重定向方式直接返回重写后生成的新URL给客户端,由客户端重新发起请求,状态码:301
last; #重写完成后停止对当前URI在当前location中后续的其它重写操作,而后对新的URL启动新一轮重写检查,不建议在location中使用
break; #重写完成后停止对当前URL在当前location中后续的其它重写操作,而后直接跳转至重写规则配置块之后的其它配置;结束循环,建议在location中使用

 (2) 临时重定向与永久重定向

1
2
3
4
5
6
location / {
root /usr/local/nginx/html/pc;
index index.html;
rewrite / http://www.aaa.com permanent;
#rewrite / http://www.aaa.com redirect;
}

 (3) last与break

1
2
3
4
5
6
7
8
9
10
11
12
13
location /break {
rewrite ^/break/(.*) /test$1 break; #break不会跳转到其他的location
return 666 "break";
}

location /last {
rewrite ^/last/(.*) /test$1 last;    #last会跳转到其他的location继续匹配新的URI
return 888 "last";
}

location /test {
return 999 "test";
}

 (4) 自动跳转https

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
server {
listen 80;
listen 443 ssl;
ssl_certificate /usr/local/nginx/certs/www.aaa.com.crt;
ssl_certificate_key /usr/local/nginx/certs/www.aaa.com.key;
ssl_session_cache shared:sslcache:20m;
ssl_session_timeout 10m;
server_name www.aaa.com;
location / {
root html/aaa.com;
index index.html index.htm;
if ($scheme = http){ #不加条件判断,会导致死循环
rewrite / https://www.aaa.com permanent;
}
}
}

#还一种是对监听的80端口进行跳转
server {
listen 80 default_server;
server_name www.aaa.com;
rewrite ^(.*)$ https://$server_name$1 permanent;
}

 (5) 判断文件是否存在

1
2
3
4
5
6
7
8
9
例:当用户访问到公司网站的时输入了一个错误的URL,可以将用户重定向至官网首页
location / {
root /usr/local/nginx/html/pc;
index index.html;
if (!-f $request_filename) {
#return 404 "No Page";
rewrite (.*) http://www.aaa.net/index.html;
}
}

 (6) 防盗链

  防盗链基于客户端携带的referer实现,referer是记录打开一个页面之前记录是从哪个页面跳转过来的标记信息,如果别人只链接了自己网站图片或某个单独的资源,而不是打开了网站的整个页面,这就是盗链,referer就是之前的那个网站域名,正常的referer信息有以下几种:

1
2
3
4
5
none:              #请求报文首部没有referer首部,比如用户直接在浏览器输入域名访问web网站,就没有referer信息。
blocked:    #请求报文有referer首部,但无有效值,比如为空。
server_names:   #referer首部中包含本主机名及即nginx 监听的server_name。
arbitrary_string: #自定义指定字符串,可使用*作通配符。
regular expression: #被指定的正则表达式模式匹配到的字符串,要使用~开头,例如:~.*\.aaa\.com
1
2
3
4
5
6
7
8
location ^~ /images {
root /usr/local/nginx/pc;
index index.html;
valid_referers none blocked server_names *.aaa.com www.aaa.* api.online.test/v1/hostlist ~\.google\. ~\.baidu\.; #定义有效的referer
if ($invalid_referer) { #假如是使用其他的无效的referer访问:
return 403; #返回状态码403
}
}

代理功能

Nginx除了可以在企业提供高性能的web服务之外,另外还可以将本身不具备的请求通过某种预定义的协议转发至其它服务器处理,不同的协议就是Nginx服务器与其他服务器进行通信的一种规范,主要在不同的场景使用以下模块实现不同的功能:

  ngx_http_proxy_module:   将客户端的请求以http协议转发至指定服务器进行处理。
  ngx_stream_proxy_module: 将客户端的请求以tcp协议转发至指定服务器处理。
  ngx_http_fastcgi_module:  将客户端对php的请求以fastcgi协议转发至指定服务器助理。
  ngx_http_uwsgi_module:  将客户端对Python的请求以uwsgi协议转发至指定服务器处理。

配置参数:
  proxy_pass; #用来设置将客户端请求转发给的后端服务器的主机,可以是主机名、IP地址:端口的方式,也可以代理到预先设置的主机群组;

1
2
3
4
5
6
location /web {
index index.html;
proxy_pass http://192.168.7.103:80;
#不带斜线将访问的/web,等于访问后端服务器 http://192.168.7.103:80/web/index.html,即后端服务器配置的站点根目录要有web目录才可以被访问,这是一个追加/web到后端服务器 http://servername:port/web/index.html的操作
proxy_pass http://192.168.7.103:80/; #带斜线,等于访问后端服务器的http://192.168.7.103:80/index.html 内容返回给客户端
}

 (1) 正向代理配置

1
2
3
4
5
6
7
8
9
10
11
12
13
#代理服务器上配置
http {
resolver 8.8.8.8;
server {
listen 8088;
location / {
proxy_pass http://$http_host$request_uri;
}
}
}
#客户端配置
export "http_proxy=http://[user]:[pass]@host:port/"
如:export http_proxy=http://192.168.145.27:8088

 (2) 反向代理配置

1
2
3
4
5
6
7
8
9
10
11
server {
listen 80;
server_name www.xxx.net;
location / {
proxy_pass http://192.168.145.7:80/;
}
}
#也可以对指定location进行代理
location /web {
proxy_pass http://192.168.7.103:80/; #注意有后面的/
}

 (3) 代理缓存功能

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#代理缓存功能默认关闭,可通过下面参数配置:
proxy_cache zone | off;   #指明调用的缓存,或关闭缓存机制;默认off;Context:http, server, location
proxy_cache_key string; #缓存中用于"键"的内容,默认值:proxy_cache_key $scheme$proxy_host$request_uri;
proxy_cache_valid [code ...] time; #定义对特定响应码的响应内容的缓存时长,定义在http{...}中示例:
proxy_cache_valid 200 302 10m;
proxy_cache_valid 404 1m;

proxy_cache_path; #定义可用于proxy功能的缓存;Context:http
proxy_cache_path path [levels=levels] [use_temp_path=on|off]
keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
例:在http配置定义缓存信息
proxy_cache_path /var/cache/nginx/proxy_cache #定义缓存保存路径,proxy_cache会自动创建
levels=1:2:2 #定义缓存目录结构层次,1:2:2可以生成2^4x2^8x2^8=1048576个目录
keys_zone=proxycache:20m #指内存中缓存的大小,主要用于存放key和metadata(如:使用次数)
inactive=120s #缓存有效时间
max_size=1g; #最大磁盘占用空间,磁盘存入文件内容的缓存空间最大值
#调用缓存功能,需要定义在相应的配置段,如server{...};或者location等
proxy_cache proxycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 301 1h;
proxy_cache_valid any 1m;

proxy_cache_use_stale;    #在被代理的后端服务器出现哪种情况下,可直接使用过期的缓存响应客户端
proxy_cache_use_stale error | timeout | invalid_header | updating | http_500 | http_502 | http_503 | http_504 | http_403 | http_404 | off ;   #默认是off

proxy_cache_methods GET | HEAD | POST ...;
#对哪些客户端请求方法对应的响应进行缓存,GET和HEAD方法总是被缓存

proxy_set_header field value;
#设定发往后端主机的请求报文的请求首部的值
Context: http, server, location
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
请求报文的标准格式如下:
X-Forwarded-For: client1, proxy1, proxy2

[root@www ~]# vim /usr/local/nginx/conf/nginx.conf
#配置在nginx.conf http配置段
proxy_cache_path /usr/local/nginx/proxycache levels=1:1:1 keys_zone=proxycache:20m inactive=120s max_size=1g;
[root@www ~]# vim /usr/local/nginx/conf/conf.d/pc.conf
#要缓存的URL 或者放在server配置项对所有URL都进行缓存
location /web {
proxy_pass http://192.168.27.7:80/;
proxy_set_header clientip $remote_addr;
proxy_cache proxycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 301 1h;
proxy_cache_valid any 1m;
}

 (4) 添加头部报文信息

  Syntax: add_header name value [always];
  Default: —
  Context: http, server, location, if in location

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#添加自定义首部
add_header name value [always];
add_header X-Via $server_addr;
add_header X-Cache $upstream_cache_status;
add_header X-Accel $server_name;

#添加自定义响应信息的尾部, 1.13.2版后支持
add_trailer name value [always];

#nginx配置:
location /web {
proxy_pass http://192.168.27.7:80/;
proxy_set_header clientip $remote_addr;
proxy_cache proxycache;
proxy_cache_key $request_uri;
proxy_cache_valid 200 302 301 1h;
proxy_cache_valid any 1m;
add_header X-Via $server_addr;
add_header X-Cache $upstream_cache_status;
add_header X-Accel $server_name;
}

负载均衡

(1) http的负载均衡-upstream

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
配置参数:
upstream name { #自定义一组服务器,配置在http内
server address [parameters]; #配置一个后端web服务器,配置在upstream内,至少要有一个server服务器配置。
}
#server支持的parameters如下:
weight=number #设置权重,默认为1
max_conns=number #给当前server设置最大活动链接数,默认为0表示没有限制
max_fails=number #对后端服务器连续监测失败多少次就标记为不可用
fail_timeout=time #对后端服务器的单次监测超时时间,默认为10秒
backup #设置为备份服务器,当所有服务器不可用时将重新启用次服务器
down #标记为down状态
resolve #当server定义的是主机名的时候,当A记录发生变化会自动应用新IP而不用重启Nginx

hash KEY consistent;
#基于指定key做hash计算,使用consistent参数,将使用ketama一致性hash算法,适用于后端是Cache服务器(如varnish)时使用,consistent定义使用一致性hash运算,一致性hash基于取模运算。所谓取模运算,就是计算两个数相除之后的余数,比如10%7=3, 7%4=3
hash $request_uri consistent; #基于用户请求的uri做hash
ip_hash; #源地址hash调度方法,基于的客户端的remote_addr(源地址)做hash计算,以实现会话保持
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
调度算法:
rr:      轮询,默认的调度方式,将所有请求都按照时间顺序分配到不同的服务上;
weight:   权重,指定每个服务的权重比例,weight和访问比率成正比;
ip_hash:    指定负载均衡器按照基于客户端IP的分配方式,这个方法确保了相同的客户端的请求一直发送到相同的服务器,以保证session会话。这样每个访客都固定访问一个后端服务器,可以解决session不能跨服务器的问题;
least_conn:  把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果
fair:      按照服务器端的响应时间来分配请求,响应时间短的优先分配;
url_hash:   按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费;而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取;
例:
upstream webserver {
#hash $request_uri consistent;
#ip_hash;
#least_conn;
server 192.168.27.7:80 weight=1 fail_timeout=5s max_fails=3; #后端服务器状态监测
server 192.168.27.17:80 weight=1 fail_timeout=5s max_fails=3 backup;
}
server {
listen 80;
server_name www.aaa.net;
location / {
index index.html index.php;
root /usr/local/nginx/html/pc;
}
location /web {
index index.html;
proxy_pass http://webserver/;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for; #客户端IP透传,添加客户端IP到报文头部
}
}

 (2) tcp的负载均衡

  Nginx在1.9.0版本开始支持tcp模式的负载均衡,在1.9.13版本开始支持udp协议的负载,udp主要用于DNS的域名解析,其配置方式和指令和http代理类似,其基于ngx_stream_proxy_module模块实现tcp负载,另外基于模块ngx_stream_upstream_module实现后端。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
配置参数:
stream {     #定义stream
upstream backend { #定义后端服务器
hash $remote_addr consistent; #定义调度算法
server backend1.example.com:12345 weight=5; #定义具体server
server 127.0.0.1:12345 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
upstream dns { #定义后端服务器
server 192.168.0.1:53535; #定义具体server
server dns.example.com:53;
}
server { #定义server
listen 12345; #监听IP:PORT
proxy_connect_timeout 1s; #连接超时时间
proxy_timeout 3s; #转发超时时间
proxy_pass backend; #转发到具体服务器组
}
server {
listen 127.0.0.1:53 udp reuseport;
proxy_timeout 20s;
proxy_pass dns;
}
server {
listen [::1]:12345;
proxy_pass unix:/tmp/stream.socket;
}
}
实例1:Redis负载均衡
stream {
upstream redis_server {
#hash $remote_addr consistent;
server 192.168.145.27:6379 max_fails=3 fail_timeout=30s;
}
server {
listen 192.168.145.7:6379;
proxy_connect_timeout 3s;
proxy_timeout 3s;
proxy_pass redis_server;
}
}

实例2:mysql负载均衡
stream {
upstream mysql_server {
least_conn;
server 192.168.145.27:3306 max_fails=3 fail_timeout=30s;
}
server {
listen 192.168.145.7:3306;
proxy_connect_timeout 6s;
proxy_timeout 15s;
proxy_pass mysql_server;
}
}

FastCGI配置

Nginx基于模块ngx_http_fastcgi_module实现通过fastcgi协议将指定的客户端请求转发至php-fpm处理,其配置参数如下:

1
2
3
4
5
6
7
8
fastcgi_pass address;        #转发请求到后端服务器,address为后端的fastcgi server的地址,可用位置:location, if in location 
fastcgi_index name; #fastcgi默认的主页资源,示例:fastcgi_index index.php;
fastcgi_param parameter value [if_not_empty]; #设置传递给FastCGI服务器的参数值,可以是文本,变量或组合,可用于将Nginx的内置变量赋值给自定义key
fastcgi_param REMOTE_ADDR $remote_addr; #客户端源IP
fastcgi_param REMOTE_PORT $remote_port; #客户端源端口
fastcgi_param SERVER_ADDR $server_addr; #请求的服务器IP地址
fastcgi_param SERVER_PORT $server_port; #请求的服务器端口
fastcgi_param SERVER_NAME $server_name; #请求的server name
1
2
3
4
5
6
7
8
9
Nginx默认配置示例:
location ~ \.php$ {
root html; #$document_root 调用root目录
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name; #默认脚本路径
#fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name; #或用$document_root,
include fastcgi_params;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
fastcgi缓存定义:
fastcgi_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];
path #缓存位置为磁盘上的文件系统路径
max_size=size #磁盘path路径中用于缓存数据的缓存空间上限
levels=levels: #缓存目录的层级数量,以及每一级的目录数量,levels=ONE:TWO:THREE,示例:leves=1:2:2
keys_zone=name:size #设置缓存名称及k/v映射的内存空间的名称及大小
inactive=time #缓存有效时间,默认10分钟,需要在指定时间满足fastcgi_cache_min_uses 次数被视为活动缓存

fastcgi缓存调用:
fastcgi_cache zone | off; #调用指定的缓存空间来缓存数据,可用位置:http, server, location
fastcgi_cache_key string; #定义用作缓存项的key的字符串,示例:fastcgi_cache_key $request_uri;
fastcgi_cache_methods GET | HEAD | POST ...; #为哪些请求方法使用缓存
fastcgi_cache_min_uses number; #缓存空间中的缓存项在inactive定义的非活动时间内至少要被访问到此处所指定的次数方可被认作活动项
fastcgi_keep_conn on | off; #收到后端服务器响应后,fastcgi服务器是否关闭连接,建议启用长连接
fastcgi_cache_valid [code ...] time; #不同的响应码各自的缓存时长
fastcgi_hide_header field; #隐藏响应头指定信息
fastcgi_pass_header field; #返回响应头指定信息,默认不会将Status、X-Accel-...返回

Nginx与php-fpm实现:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
(1) 安装php-fpm
[root@www ~]# yum install php-fpm php-mysql -y
[root@www ~]# systemctl start php-fpm
(2) 准备php测试页
[root@www ~]# vim /usr/local/nginx/html/aaa.com/index.php
<?php
phpinfo();
?>
(3) nginx配置转发
[root@www ~]# vim /usr/local/nginx/conf/conf.d/aaa.conf
server {
listen 80;
server_name www.aaa.com;
location / {
root html/aaa.com;
index index.html index.htm;
}
location ~ \.php$ {
root html/aaa.com;
fastcgi_pass 127.0.0.1:9000;
fastcgi_index index.php;
fastcgi_param SCRIPT_FILENAME $document_root$fastcgi_script_name;
include fastcgi_params;
}
}
(3) 重启nginx并使用浏览器测试
[root@www ~]# nginx -s reload

php相关配置优化:
[root@www ~]# grep "^[a-Z]" /etc/php-fpm.conf
include=/etc/php-fpm.d/*.conf
pid = /run/php-fpm/php-fpm.pid
error_log = /var/log/php-fpm/error.log
daemonize = yes #是否后台启动

[root@www ~]# grep "^[a-Z]" /etc/php-fpm.d/www.conf
listen = 127.0.0.1:9000 #监听地址及IP
listen.allowed_clients = 127.0.0.1 #允许客户端从哪个源IP地址访问,要允许所有则在行首加 ; 注释即可
user = nginx #php-fpm启动的用户和组,会涉及到后期文件的权限问题
group = nginx
pm = dynamic #动态模式进程管理
pm.max_children = 500 #静态方式下开启的php-fpm进程数量,在动态方式下他限定php-fpm的最大进程数
pm.start_servers = 100 #动态模式下初始进程数,必须大于等于pm.min_spare_servers和小于等于pm.max_children的值
pm.min_spare_servers = 100 #最小空闲进程数
pm.max_spare_servers = 200 #最大空闲进程数
pm.max_requests = 500000 #进程累计请求回收值,会重启
pm.status_path = /pm_status #状态访问URL
ping.path = /ping #ping访问动地址
ping.response = ping-pong #ping返回值
slowlog = /var/log/php-fpm/www-slow.log #慢日志路径
php_admin_value[error_log] = /var/log/php-fpm/www-error.log #错误日志
php_admin_flag[log_errors] = on
php_value[session.save_handler] = files #phpsession保存方式及路径
php_value[session.save_path] = /var/lib/php/session #当时使用file保存session的文件路径

系统参数优化

默认的Linux内核参数考虑的是最通用场景,不符合用于支持高并发访问的Web服务器的定义,根据业务特点来进行调整,当Nginx作为静态web内容服务器、反向代理或者提供压缩服务器的服务器时,内核参数的调整都是不同的,此处针对最通用的、使Nginx支持更多并发请求的TCP网络参数做简单的配置,修改/etc/sysctl.conf来更改内核参数。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
fs.file-max = 1000000
#表示单个进程较大可以打开的句柄数

net.ipv4.tcp_tw_reuse = 1
#参数设置为 1 ,表示允许将TIME_WAIT状态的socket重新用于新的TCP链接,这对于服务器来说意义重大,因为总有大量TIME_WAIT状态的链接存在

net.ipv4.tcp_keepalive_time = 600
#当keepalive启动时,TCP发送keepalive消息的频度;默认是2小时,将其设置为10分钟,可更快的清理无效链接

net.ipv4.tcp_fin_timeout = 30
#当服务器主动关闭链接时,socket保持在FIN_WAIT_2状态的较大时间

net.ipv4.tcp_max_tw_buckets = 5000
#表示操作系统允许TIME_WAIT套接字数量的较大值,如超过此值,TIME_WAIT套接字将立刻被清除并打印警告信息,默认为8000,过多的TIME_WAIT套接字会使Web服务器变慢

net.ipv4.ip_local_port_range = 1024 65000
#定义UDP和TCP链接的本地端口的取值范围

net.ipv4.tcp_rmem = 10240 87380 12582912
#定义了TCP接受缓存的最小值、默认值、较大值

net.ipv4.tcp_wmem = 10240 87380 12582912
#定义TCP发送缓存的最小值、默认值、较大值

net.core.netdev_max_backlog = 8096
#当网卡接收数据包的速度大于内核处理速度时,会有一个列队保存这些数据包。这个参数表示该列队的较大值

net.core.rmem_default = 6291456
#表示内核套接字接受缓存区默认大小

net.core.wmem_default = 6291456
#表示内核套接字发送缓存区默认大小

net.core.rmem_max = 12582912
#表示内核套接字接受缓存区较大大小

net.core.wmem_max = 12582912
#表示内核套接字发送缓存区较大大小

注意:以上的四个参数,需要根据业务逻辑和实际的硬件成本来综合考虑

net.ipv4.tcp_syncookies = 1
#与性能无关。用于解决TCP的SYN攻击

net.ipv4.tcp_max_syn_backlog = 8192
#这个参数表示TCP三次握手建立阶段接受SYN请求列队的较大长度,默认1024,将其设置的大一些可使出现Nginx繁忙来不及accept新连接时,Linux不至于丢失客户端发起的链接请求

net.ipv4.tcp_tw_recycle = 1
#这个参数用于设置启用timewait快速回收

net.core.somaxconn=262114
#选项默认值是128,这个参数用于调节系统同时发起的TCP连接数,在高并发的请求中,默认的值可能会导致链接超时或者重传,因此需要结合高并发请求数来调节此值。

net.ipv4.tcp_max_orphans=262114
#选项用于设定系统中最多有多少个TCP套接字不被关联到任何一个用户文件句柄上。如果超过这个数字,孤立链接将立即被复位并输出警