概述
一、nginx是一款轻量级的web服务器/反向代理服务器及电子邮件imap/pop3/smtp服务器,在bsd-like协议下发行。其特点是占有内存少,并发能力强。
二、核心应用场景:
- 高性能的静态web服务器:只能处理静态资源
静态资源:图片、样式、flash等不会随着用户的请求改变而改变的资源
动态资源:账户余额、商品剩余货源等,这些依赖于后端应用服务器在数据库查询、处理后返回的结果
- 反向代理:客户端点击一个网页后,本质上是向nginx发送了一个http/https请求,nginx可以将静态资源返回客户端,虽然nginx无法返回动态资源,但是他可以将请求代理的发送给后端的应用服务器,由应用服务器查询数据库并处理完数据后返回给nginx,nginx将动态资源、静态资源通过http/https返回给客户端,由客户端在浏览器展示。在这个过程中,对于动态资源来说,nginx就是一个反向代理,反向代理的过程中就有负载均衡,比如在应用服务器是多台的情况下,nginx能够将这些请求负载均衡的以此转发给每个应用服务器。后面讲到的限速、动静分离都是nginx的辅助能力
应用服务器:有很多种类型,如果后端是基于Java开发的,那么应用服务器可能是tomcat,还有些商用的应用服务器:weblogic
三、nginx为什么出现并流行:
主要应用场景
一、一个http请求的全流程
用户在浏览器上输入一个网站的地址,客户端会发送一个http请求到nginx,nginx会将动态资源请求转发给应用服务器,应用服务器与数据库交互进行业务逻辑处理,处理完后,将数据返回给nginx,nginx将动态资源和自身的静态资源进行封装后返回给客户端。
二、nginx处理请求的过程
说明:
- 红色箭头表示客户端发送到nginx的请求,静态资源放在本地文件系统(Linux系统下的某个目录),nginx对于静态资源的响应非常高,但是应用服务器对动态资源的响应急剧下降,数据库对请求的处理就更低了,为了缓解nginx、应用服务器、数据库之间的处理差异,我们需要部署一个缓存服务器(具体还是需要根据系统的规模决定要不要部署缓存服务器)。
- 反向代理代理中还有缓存加速、负载均衡:假如应用服务器有多台,反向代理就不能代理给某一台应用服务器了,而需要通过一些算法将请求进行分发,以达到负载均衡;nginx可以将应用服务器从数据库获取到的数据缓存起来,下次请求过来时,就不需要再去应用服务器获取结果,可以直接返回结果
- api服务:nginx诞生初期是没有这个能力的,随着nginx不断发展,有了openResty后才有的能力。openResty是nginx和弱脚本的结合,可以通过api服务自己开发一个高性能的web服务器
nginx核心优势
一、高并发、高性能:随着互联网数据和用户量的增长,之前使用的apache的web服务器已无法满足。高并发的场景下我们只用增加进程数就能解决,依赖于事件驱动模型、进程处理请求模型,他是用一个进程来处理多个请求;高性能依赖于良好的设计;
二、扩展性好:
- nginx自身是模块化设计的,模块化设计最强的优点就是解耦,所有功能的耦合性是很低的,意味着一个模块出现问题,是不会对其他模块造成影响的;
- nginx允许开发第三方模块,如openResty、阿里巴巴的tngine
- 生态圈发展好
三、异步非阻塞的事件驱动模型:比如餐厅点餐这一场景,在阻塞式场景中,第一个顾客点完餐,就在那里等,等厨师做完并交付给这个顾客后,顾客走了,老板才能接待下一个客户,对于异步来说,顾客点完餐后拿了个号牌,下一个顾客可以继续上前点单,对于老板来说,他可以接待的人更多了(处理的请求更多了),后续老板可以增加个叫号器,这就是编程开销,所以,对于异步非阻塞的事件驱动模型增加了编程难度、编程开销,但是处理更高效
四、高可靠性:nginx通常运行在系统的边缘节点,用于接收用户的请求,nginx连续运行几年可能都不需要重启服务,但是对于其他服务器(如apache),可能运行几周就需要重启服务器,为保证业务连续性,我们是不能接受这种情况的。
五、热部署、平滑升级:不需要关机就能对后端应用程序进行更换,同时不中断连接请求的处理
六、bsd许可:允许nginx使用者根据自己的需求对nginx进行二开,并将二开代码运用到nginx中,同时能运用到商业场景
安装第一个rpm包形式的nginx
nginx进程结构与热部署
nginx的进程结构
nginx采用多进程的进程结构,不采用多线程是因为nginx设计之初就是为了高可用性、高可靠性而设计的:多线程间最大的问题是,某一个线程因为代码不够健壮导致某一段地址空间越界,出现段错位时,可能导致整个线程的不可用,我们知道多线程间是通过采用共享同一个地址空间来实现的
说明:
- master process不处理请求,而是管理各模块,如下方的缓存模块、worker process子进程(处理请求),用于监控各模块是否宕掉,如果宕掉了,会启动一个新的worker process来维持原有的进程结构,假如我们在配置文件定义了worker process启动4个,而有一个突然宕掉了,在Linux系统中,子进程会自动发送一个信号给主进程,主进程收到后会自动检测到这个worker process已经宕掉了,他会自动再启一个,从而来维持4个worker process这一结构;假如我们修改了配置文件,加了一些静态资源、重写了url等,master process检测到了后,会通知worker process拿新的配置文件并生效,这就是热部署;
- 前面我们说过,nginx具有缓存功能,其实真正需要缓存哪些内容(缓存这一动作)是worker process做的,cache loader用于加载缓存,cache manager用于管理缓存
- 所有的子进程(cm、cl、worker process)使用共享内存进行通信
- 在实际生产环境中worker process不一定是4个,这一点稍后说明
- nginx是模块化设计的,虽然nginx允许修改master process,但是在开发第三方模块时,尽量不修改master process主程序模块,因为(由进程结构图可知)master process挂了整个服务就挂了
Linux的信号量管理机制
Linux操作系统中,通过信号量进行进程管理,比如我们可以通过q命令向某一进程的pid发送很多信号量,使用kill pid
相当于给Linux发送了一个kill -15
命令(-15
是一个代号)
在执行kill pid
实际上是发送了一个send sigterm信号给Linux的进程,Linux的进程收到信号后,先结束自己的工作,再关闭这个进程,同理,kill -9
实际上是发送了sigkill
信号,该信号不管当前的进程处于什么状态,不管是在处理数据还是在等待磁盘的io,都会被无条件终止,所以这个信号可能会对处理数据的进程有影响
说明:
- sigchld:用于父子进程间通信,某一子进程宕掉或者终止掉了,会自动发送sigchld给父进程,让父进程采取相应的措施,比如重新启动一个新的子进程
- sigterm:等进程处理完数据后再终止。
kill [-15] pid
- sigkill:使用
kill [-15] pid
终止不掉数据时使用该信号立即杀死进程。在使用shutdown关闭操作系统时,操作系统对正在运行的进程也是使用kill -9杀死,但是他是先向所有进程发送kill命令,等待应用程序自己处理完数据后关闭,操作系统会有个等待时间,假如这个等待时间到了还有进程没有关闭,操作系统才会发送kill -9
给应用程序立即终止程序,然后操作系统关机 - sighup:重读配置文件,关闭之前的worker子进程,重新生成新的worker子进程
- sigusr1、sigusr2:Linux提供给用户自定义的信号量
利用信号量管理nginx
一、nginx的哪些子进程可以使用信号量进行管理:
- master进程:我们给master发送信号量,让master管理所有子进程
- worker进程:虽然我们可以给worker进程发送信号量,但是不推荐这么做
- 命令行:通过nginx的二进制程序和一些参数对命令行进行管理,可用参数:
(1)reload:重新加载配置文件,本质上是给master进程发送hup信号
(2)reopen:重新打开日志文件,本质上是给master进程发送user1信号
(3)stop:停止nginx,本质上是给master进程发送term信号
(4)quit:停止nginx,本质上是给master进程发送quit信号
通常在启动nginx后,pid会保存在nginx.pid文件中,当执行reload命令时,其实也是通过Kill发送hup信号给nginx.pid文件中的进程
二、nginx采用事件驱动模型处理机制,该机制要求nginx的worker子进程从始至终都要和CPU绑定在一块(cpu有多少核,就会启动多少个worker子进程,通常在nginx.conf里面配置:worker_processea auto;),从而减少cpu的切换,提高cpu缓存命中效率
三、代码演示
终止26733进程:
sighup:重读26747master进程的配置文件,关闭其之前的worker子进程(26748、26749、26759、26751),重新生成新的worker子进程(26754、26755、26756、26757)
终止26754子进程,主进程为保持配置文件里既定的结构,会自动重启一个子进程26761
通过/opt/nginx/sbin/nginx -h
查看可以使用的命令行
说明:
-s
用于发送信号,也就是说可以使用/opt/nginx/sbin/nginx -s 参数
(本质上还是通过给master进程发送信号量进行管理)
- -v:查看nginx版本
- -V:查看nginx版本号、nginx编译安装时的一些参数(configure argument),如安装目录(-- prefix)、用户(–user)、组(–group)、程序目录(–sbin-path)、配置文件目录(-- config-path)
- -t:检查配置文件nginx.conf语法是否正确
配置文件重载的原理
一、reload重载配置文件的流程
- 向master进程发送
kill -s sighup
或者使用/opt/nginx/sbin/nginx -s reload
命令 - master进程检查nginx.config配置语法是否正确,如果nginx.config引入了其他配置文件,还会检查其他配置文件语法是否正确
- 配置语法正确后,如果配置文件中增加了端口,master进程会打开监听端口,如果没增加则不会打开
- master进程使用新的配置文件启动新的worker子进程
- 新的worker子进程启动完成后,master进程向老的worker子进程发送quit信号,从而保障用户请求始终能得到处理
- 旧的worker子进程继续处理当前的用户请求(新的请求由新的子进程处理),等当前连接处理完请求后,关闭监听句柄,处理完当前连接后关闭进程,从而实现nginx平滑过度的过程
二、不停机生效新的配置
nginx的热部署
一、热部署:在不中断现有业务的情况下,平滑的将新开发的应用程序版本升级完成。
二、nginx热升级的流程
- 将旧的nginx文件(主程序文件,即sbin文件夹下的nginx文件)替换成新的nginx文件,这要求li b库目录、配置文件目录、log目录都不变
- 向master进程发送usr2信号:
(1)旧的master进程主动修改自己的pid文件,给pid文件(/logs/nginx.pid)加后缀.oldbin
(2)master进程使用新的nginx文件生效新的配置,启动一个新的master进程 - 此时服务器上,旧的master进程和新的master进程同时存在,两个都会接收用户的请求并处理,此时需要验证新版本nginx是否满足预期,运行一段时间后发现能够满足,再向旧的master进程发送winch信号,旧的worker子进程退出,如果发送quit信号,旧的master进程及其worker子进程都会退出,这里用winch信号是为了避免接收新的用户请求,保证新的用户请求都由新的master进程生成的新的worker子进程处理,确保新的请求都是由新版本的nginx处理,之所以这样做,是为了保证回滚
- 如果新版本的nginx不满足预期的话,可以将新版本nginx下线,让旧版本nginx上线:向旧master发送hup,sighup信号发送后,旧的master进程会根据旧的配置文件把旧的worker子进程拉起来,旧的用户子进程会开始处理用户请求,同时需要向新master发送quit让其下线
nginx热部署完整步骤演示
- 备份nginx程序,替换nginx程序
- 向旧的master进程发送sur2信号,可以看到有两个master进程,每个master进程下面又有各自的worker子进程,此时新旧进程并存
- 查看/logs目录,可以看到nginx.pid.oldbin,里面保存着旧的master进程的pid,而新的nginx.pid里面保存着新的master进程的pid
- 给旧的master进程发送winch信号,显然旧的master下面没有worker子进程了,但是旧master主进程依旧存在,此时可以测试新的nginx是否满足预期
- 给旧的master进程发送quit信号,可以看到,旧的master进程及其worker子进程全部退出
而logs文件夹下面的nginx.pid.oldbin也被删除了
如果想回滚,本步骤应为:向旧的master进程发送hup信号
给新master进程发送quit信号
nginx的模块化管理机制
nginx编译安装的配置参数
说明:
- –prefix指定的是总的目录
- –user:指定由谁来启动worker子进程
- –group:指定启动nginx的worker子进程的组
- –conf-path、–error-log-path、–pid-path、–http-log-path默认值放在–prefix下面
- –with-pcre:如果配置文件为某些特定的url写了正则表达式,必须安装pcre库,该参数用于指定指定pcre库源代码的位置。下载pcre库源码并解压,编译安装nginx时,将pcre库的解压地址配置到这个参数中即可
- –with-lib:gzip模块会用到
内置参数默认原则:
- –with表示nginx不内置,不加这个参数,这个模块不会被编译进nginx
- –without表示nginx内置了该模块,该模块默认会被编译进ngixn,加上该参数,该模块就不会被编译进nginx
定制编译安装第一个nginx
- 解压pcre、gzip、nginx:
- 进入nginx文件夹,查看配置参数:
./configure --help
- 指定编译参数(path可以不
用指定),并指定哪些模块要编译进来,哪些不用
- 如果有报错,则安装相应的模块
yum install 模块名
,因为ssl相关的模块有两个,所以这里安装了openssl和openssl-devel
- 输入
make
进行编译完成后,使用make install
进行安装
- 安装完毕,启动nginx
- 查看日志
nginx配置文件结构
一、nginx.conf文件整体可以分为三大模块:events模块、http模块、server模块
说明:
- main模块:定义全局设置,如nginx运行的用户是谁,该用户属于哪个组,应该启动几个worker进程
- events模块:配置影响nginx服务器与用户的网络连接,如配置与nginx或者Linux事件驱动模型处理有关的指令
worker_connections:每个worker子进程可以处理的并发连接请求数,具体值需要根据对服务器进行测试得到,不是随意写的
- http模块:配置http核心指令。对http端来说,可以嵌套多个server,server对应的可能是虚拟主机。假如有3个不同域名部署了不同的网站服务,这3个网站服务可以部署在同一台nginx上,只要我们在server中定义对应的域名,就可以解析到对应的server端。也可以配置缓存、反向代理、第三方模块、日志定义等绝大多数功能。
- server模块:location端只能嵌套在server中,主要用于配置请求的路由、各种页面的处理情况,location可以理解为,访问某个url,应该去location指定的磁盘路径下获取静态资源,相当于是静态文件的映射路由
二、配置文件示例
三、
虚拟主机
虚拟主机:利用单台服务器构筑出多个不同类型的网站
分类
一、基于多ip的虚拟主机:单台服务器通常有多个网卡,在每个网卡上配置对应的ip地址,每个IP地址都可以给他构建个虚拟主机
二、基于多端口的虚拟主机:单台服务器部署,网卡只有一块,基于不同的端口提供不同的服务
三、基于域名的虚拟主机:一台物理主机上使用不同的域名来区分不同的网站服务
基于多网卡/ip的虚拟主机实现
基于端口的虚拟主机实现
基于域名的虚拟主机实现
配置文件参数
配置文件main段核心参数用法
配置文件中除了events段、http段之外的内容被称为main段
worker_rlimit_nofile:每个worker子进程可以打开的最大文件句柄数。如果不设置,那么就使用属主默认的最大句柄数。对于任何一个用户请求发送过来后,都是worker子进程在处理请求,对于worker子进程来说,每一个连接都会发起一个tcp连接,tcp连接会发送很多报文,对每个连接来说,都需要一个套接字来维持连接,一个套接字就可以理解为一个文件句柄,所以说,如果nginx想承载更大的并发量的话,肯定要把这个参数调高。
假如我们指定worker_rlimit_nofile为20480、worker_processes为4,那整个nginx服务器所能承载的最大并发数为4*20480,但是这只是理论上的数值,实际上操作系统最大能打开65535个文件句柄数,所以当worker_rlimit_nofile * worker_processes>65535之后,是没有意义的
working_directory:指定core文件存放目录,假如在前面使用user字段指定了nginx用户,但是master是root用户运行的,而真正处理用户请求的是worker子进程(即nginx用户)运行的,所以working_directory指定的目录nginx必须有写权限
worker_precesses auto:worker_precesses会自动启动跟当前物理cpu核心的个数一致的子进程
对于当前nginx进程来说,他会启动一个master process用于管理worker子进程,master process更新配置文件,将新的配置文件信息传递给子进程,master process不会处理用户请求,真正处理用户请求的是子进程,假如我们现在有4个子进程,不管是什么进程,在cpu上都不可能永远运行,也就是说这个cpu不可能永远归某个进程所有,每个进程需要去cpu上申请资源运行的时候,通常会根据自己当前的优先级在cpu上申请一个时间片,然后等cpu调度,在某个时刻,cpu调度进程并运行,进程使用cpu的资源进行计算,时间片完了之后,进程必须从cpu上撤还下来进入睡眠状态,或者去做其他事,cpu会去调度其他的进程。假如现在有4个物理核心,子进程也有4个,子进程1在CPU1上运行了一段时间后,从CPU1上切换下来,CPU1被其他进程调度了,可能不是子进程,因为服务器上除了nginx自身来说,运行Linux也需要启动很多服务,一段时间后,子进程1又到时间片了,又要运行了,此时可能不是CPU1运行子进程1了,可能子进程1申请到的是CPU3,子进程1在CPU3上运行一段时间后下来,下一次子进程1可能又调度到CPU4上了,子进程1就这样来回的在每个CPU上切换,这样频繁的进程切换是有性能开销的,并且在子进程在被不同的CPU调度,CPU缓存是失效的(相当于CPU之间的缓存是不共享的),为了减少这一问题,我们应该减少进程切换并合理利用cpu缓存,那现在我们可以把cpu和进程进行绑定,假如把子进程1和CPU1绑定在一起,CPU2和子进程2绑定在一起,CPU3和子进程3绑定在一起,CPU4和子进程4绑定在一起,这样子进程1就永远只能被CPU1调度,但是不意味着,子进程1完全永远拥有CPU1的计算资源,也就是说,子进程1被CPU1调度,时间片到了后,依旧要从CPU1下来,CPU1调度其他进程,然后子进程1永远等CPU1的调度,他不会被其他CPU调度,这就使得每个子进程所使用的CPU缓存永远是有效的
2个物理核心分别为01、10
4个物理核心分别为0001、0010、0100、1000
worker_priority -10:子进程优先级为120-10=110。一般生产上会将worker_priority设为负数。
worker_shutdown_timeout:热部署的时候,更新配置文件、程序文件信息后,需要给子进程发送退出的指令,子进程不会主动关闭客户端发送过来的连接,假如客户端因为一些情况出现异常,客户端发起请求后与nginx建立了tcp连接后,再也不发送请求,或者客户端得到服务端的响应后,就是不返回确认的数据包,如果客户端不主动向nginx发送关闭连接的指令的话,这个连接就不会被释放,这会严重影响nginx的性能。为了应对这一情景,防止nginx服务器被很多恶意程序所攻击,这就需要设定worker_shutdown_timeout,如果在worker_shutdown_timeout指定时间内,nginx没有收到客户端的响应,nginx会主动断开连接
timer_resolution interval:interval也是一个时间(time),只不过他是一个时间间隔。在nginx自身运行的过程中,会向内核态(即Linux内核)发起很多系统调用,比如获取时间计时器,用户进程需要向内核态发起系统调用的间隔时间,每次用户状态向内核态的切换都会降低性能。
右边的图是我们nginx服务器,启动了4个worker子进程用来响应客户端发过来的请求,nginx自身运行在用户态,系统操作系统有内核态,内核态用来处理tcp/ip协议数据包、获取磁盘io、跟操作系统获取系统时间、获取磁盘上的文件信息。用户发送tcp连接到网卡,网卡收到后对tcp连接进行拆包,拆完包后给tcp/ip协议,tcp/ip协议解开数据包判断是否当前ip,如果不是,则将数据包丢掉,如果是,则tcp/ip协议继续拆包,判断访问哪个端口,然后tcp/ip协议将数据包扔给用户态空间的worker子进程,由worker子进程继续处理,此时请求已经到用户态了,假如需要频繁的获取系统时间或者跟磁盘打交道等等,用户态必须把请求发送给内核态,由内核来获取系统时间。用户态和内核态频繁的切换会影响性能,所有timer_resolution就是用来减少切换时间的,如果应用对时间精度没那么高,timer_resolution可以设大一点。
daemon:默认为on,意为运行在后台;开发调试时设为off可以在前台打印日志,但需要配合调试参数一起使用。(用的比较少)
lock_file:主进程每次需要分配新的请求给子进程时,会把相关的信息写在锁文件中,这个字段用来制定锁文件地址
实践
在配置文件中以上参数配置如下:
配置文件events段核心参数用法
method指操作系统底层的io处理模型(事件驱动模型)采用的参数,常用epoll
每个workers子进程能够处理的最大并发连接数。每个Linux操作系统自身最大能打开套接字的个数为65535,所以worker_connections设为10万、20万没有用的,建议设为65535/子进程个数
,或者直接配为65535。如果你有多台nginx服务器,但是有些性能比较差,有些性能比较好,前端可能还有一台负载均衡器把用户的请求分发到多台nginx服务器上,那么可以给性能好的nginx服务器分配更多的连接。这个值的大小还是视情况而定。
客户端发送http请求到主进程,主进程会去找一个子进程来处理这个请求,那么,主进程是找哪个子进程呢?他是只找一个,把请求发给他呢,还是去找所有的子进程呢?accept_mutex代表的就是互斥、加锁的意思,默认为off,表示主进程会给所有的子进程发送事件,唤醒所有子进程,假如现在每个子进程都在处理其他连接,那么他们还会(额外)处理主进程发送的这个请求,这样其实会耗费性能(因为子进程会去抢这个请求的优先级)。将accept_mutex设为on,会在每个子进程上加一个负载均衡锁,当新的请求到来后,主进程会轮流给其中一个子进程发送连接请求。
accept_mutex设为on才能设置accept_mutex_delay。客户端发送http请求到主进程,主进程会轮流找一个子进程来处理这个请求,假如本次请求由第二个子进程处理,但是第二个子进程在处理其他的请求,如果子进程在accept_mutex_delay时间内没有响应主进程(要不要处理这个请求),那么主进程会把这个请求分发给其他子进程,从而避免新的请求进来了,该处理这个请求的子进程特别繁忙,导致新请求得不到处理这一情况
默认情况下,子进程在某一时刻只能处理一个连接请求,如果将multi_accept设为on,表示子进程在某一时刻可以处理多个连接请求。打开对性能影响较少
实践
在配置文件中以上参数配置如下:
视频内容有误,将lock_file放在events中讲了,实际上他是main的字段
server_name指令用法
在hosts中配置本台电脑的域名,之后在浏览器中访问以下三个域名都能打开本台电脑的html/nginx-test/index.html:
root:指定根目录
location:匹配规则,此处为将/映射到index.html
server_name指令用法优先级
只有一个虚拟主机时,可以通过server_name指定虚拟主机域名匹配,那在多域名虚拟主机的情况下,通常是通过定义多个server来区分:
在同一台机器上布了4台虚拟主机,在同一个nginx配置文件中写了4个server段,假如现在用户访问www.nginx.org,上述每个虚拟主机定义的server段都能匹配,像这样,存在多个虚拟主机都可以匹配的时候,应该以哪个为准呢?哪个能真正的响应请求呢?上述四种写法分别为:精确匹配、右侧通配符匹配、左侧通配符匹配、正则表达式匹配,优先级由高到低为:精确匹配、左侧通配符匹配、右侧通配符匹配、正则表达式匹配。
root和alias的区别
他们都是用来定义uri到磁盘文件的映射关系的,但是root会将定义路径和uri叠加,alias只取定义路径。区别示例:
- 客户端请求www.test.com/picture/1.jpg,则对应磁盘映射路径/opt/nginx/html/picture/picture/1.jpg
location /picture {
root /opt/nginx/html/picture
}
- 客户端请求www.test.com/picture/1.jpg,则对应磁盘映射路径/opt/nginx/html/picture/1.jpg
location /picture {
alias /opt/nginx/html/picture/
}
注意:
- 使用alias时,末尾一定要加/
- alias只能位于location块中
location的基础用法
优先级由高到低:=、^~、~、~*、不带任何字符
location中url结尾的反斜线
/test:作为目录来处理,nginx会去找root目录下是否有test文件夹,如果有这个文件夹,他还会去找对应的test文件夹下是否有index.html,即使不定义location里面的内容,也会去找test/index.html,如果找不到test文件夹的话,nginx会把test当作文件。查找root目录下是否有test文件,如果有,直接返回test文件的内容。
/test/:作为目录来处理,nginx会去找root目录下是否有test文件夹,如果有这个文件夹,他还会去找对应的test文件夹下是否有index.html,如果有,则将index.html的内容返回,如果没有,不会将test作为文件来查找,
stub_status模块用法
stub_status:能够给nginx提供监控页面的模块,该模块可以实现在web页面中,实时查看nginx服务运行状态,比如当前正在处理的用户请求连接数,已经处理的所有的客户端的请求数量,包括在接受的、正在处理的等各种状态监控的web页面
uri尽可能安全一些、保密一些,不能被用户访问到
在编译的时候,需要加上--with-http_stub_status_module
将这个模块编译进去,因为默认情况下,nginx是不是编译这个模块的。通过./configure --help
可以查看相应的指令,其中--with-http_stub_status_module
表示默认没有编译进去的模块,--without-http_xx
表示默认编译进去的模块,如果想不编译进去,需要在编译时添加--without-http_xx
通过/opt/nginx/sbin/nginx -V
可以查看这个nginx的编译指令,其中/opt/nginx/sbin/ngin是nginx安装路径
nginx核心模块
connection和request
三次握手建立tcp连接后,才可以发请求
limit_conn模块
用于限制客户端并发连接数,默认编译进nginx,通过--without-http_limit_conn_module
禁用。该模块使用共享内存,对所有worker子进程生效,在需要对客户端的并发连接进行限制的情况下,对于某个客户端来说,如果这个客户端发起一个http请求,这个请求被某个子进程处理,假如该客户端下一次再发起一个http请求,这个请求由另一个子进程处理,如果想对所有的客户端连接做统一的控制,那么不同的子进程间必须知道某一个客户端发送过来的连接有几个,就必须有一块共享内存
常用指令
limit_conn_zone:定义共享内存
key:客户端唯一标识,$binary_remote_addr指客户端ip地址,默认占用4字节,占用空间小
limit_conn_status:定义限制行为发生时,nginx回送给客户端的状态
limit_conn_log_level:定义限制行为发生时,nginx日志记录的等级
limit_conn:定义对客户端限制并发连接数的
limit_req模块
常见指令
limit_req_zone:用于定义共享内存。对客户端ip($binary_remote_addr)进行限速,限制他每分钟最多处理2个请求,即使正好处理两个请求也是平均的,即前30s处理一个,后30s处理另一个,哪怕第二秒就已经把第一个请求处理完了,他也会等到31s再处理第二个请求,这就是我们说limit_req是限定客户端处理请求的的平均速率的原因。实际用的时候,根据高并发系统能够承载的并发量来设定,可以设为每秒处理7000或10000个请求
limit_req:burst定义桶的大小,如果限定了请求处理速率,每一分钟只能处理两个请求,那就是30s处理一个请求,当第1s处理第一个请求时肯定会返回,按理说第二个请求得等到31s后处理完才返回,但是如果burst=7,那么第二秒处理完第2个请求也会返回,直到第7s处理完第7个请求,第八个请求就不会再返回了。如果希望处理完立即返回,需要设置nodelay
实践
60/12=5s,每5s处理一个请求,假如我们第一秒发送了第一个请求,此时会给我们返回内容,第2秒发送了第2个请求,此时会给我们返回504,第3秒发送了第3个请求,此时会给我们返回504,第4秒发送了第4个请求,此时会给我们返回504,第5秒发送了第5个请求,此时会给我们返回内容。
限制行为发生时打印的日志如上
leaky_bucket
限制特定ip或网段访问的access模块
允许特定ip或网段访问
限制特定ip或网段访问
192.168.1.0/24的范围比192.168.1.1的大,所以先拒绝掉小范围的,再允许大范围的,后面三行就是先允许小范围的,再拒绝所有的网段
限制特定用户访问的auth_basic模块
使用auth_basic模块让特定的url必须通过用户名、密码访问。
浏览器会给nginx发送请求,输入用户名、密码后,浏览器将用户名、密码封装后发送给nginx,由nginx验证。
string就是用户访问受限的url的时候,你弹出给用户的信息
auth_basic_user_file:指定密钥文件
第一次生成密码文件时:htpasswd -bc 文件名 用户名 密码
,在文件中,用户名是明文保存,密码是加密的。后面添加新用户时,不用加c
,加了会覆盖之前的文件
实践
- 安装:将httpd-tools-2.4.6-90.el7.centos.x86_64.rpm包上传到服务器,然后在此包所在文件夹下安装相关依赖:
yum install apr apr-util
,然后安装httpd包:rpm -ivhhttpd-tools-2.4.6-90.el7.centos.x86_64.rpm
。 - 生成密码文件:随便在哪个文件夹下,生成密码文件:
htpasswd -bc encrypt_pass jack 123456
- 添加新的用户名密码:执行:
htpasswd -b encrypt_pass mike 123456
- 在nginx.conf中配置:
基于http响应状态码做权限控制的auth_request模块
在某些场景下,基于安全的考虑,通常会有一些需要鉴权的服务器,当你访问特定的应用模块时,需要先向鉴权服务器发起请求,鉴权服务器鉴权通过后,你才能正常访问业务系统。
auth_request模块最核心的能力就是当他基于特定的uri发起请求时,他会生成一个子请求,子请求才是鉴权模块实现的能力,鉴权模块处理完请求后会返回一个正常的http的响应码,然后根据http响应码做相应的防护控制,比如,非200的都是鉴权失败。
uri即需要鉴权的模块,当用户访问这个uri时,会把请求转发到鉴权服务器,鉴权通过后才会访问这个uri
第一个location:当用户访问/private/时,将请求转发到/auth鉴权模块。
第二个location:当访问/auth时,使用proxy_pass将请求反向代理到真正的鉴权服务器上进行鉴权,鉴权成功才能返回/private/下相应的内容,如果鉴权失败,返回反向代理响应的内容。
实践
添加参数:--with-http_auth_request_module
,然后执行make
编译,然后使用make install
安装
然后在nging的配置文件中进行配置:
rewrite模块中的return指令
rewrite模块实现对url的重写
return 200 [text]
中的text
会被封装到响应体中,但如果code非200,text是没有意义的,因为非200是没有返回结果的
return code url
:主要用于重定向
return url
:URL必须以http或https开头的
rewrite模块中的rewrite指令
rewrite指令会根据指定正则表达式匹配规则,重写url
将images下的所有jpg文件重定向到pic文件夹下,$1
表示前向引用,即(.*\.jpg)$
return和rewrite指令执行顺序
将上一节后3个location都添加个return语句:
- 访问images文件夹中的内容时,会重写到pics文件夹下,因为有last指令,所以会立马发起新的请求访问/pics,但是/pics没有last指令,所以重写到photos文件夹下后,会继续执行return,然后返回200。【按顺序执行】
- 如果我们给/pics添加break指令,那么/pics重写到/photos后,会直接去/photos中找对应的文件,不会访问/photos location块,不会返回200。
- 如果直接访问/photos下的文件,则location块会返回200 ,而不是请求的文件。
rewrite模块中if指令
$remote_addr表示ip
rewrite (.*) /pics/ break:访问/images/下的资源,重定向到访问/pics/下的资源,不管在/pics/下有没有找到相应资源,都会继续执行return语句,返回200
auto index模块用法
auto index模块:用户请求以/结尾时,列出目录结构
autoindex:是否开启
autoindex_exact_size:列出来的目录是否展示大小,会精确到字节
autoindex_format:返回的目录结构以哪种形式返回,一般是html
autoindex_localtime:是否展示文件的时间格式
如果/opt/source/download/a.html存在,则返回该页面,如果不存在,执行后面的代码,返回/opt/source/download/下的所有文件列表。如果不配置index a.html
,那么默认去找/opt/source/download/index.html
nginx变量分类
在生产环境中,业务部门可能需要对用户的行为(nginx的数据)进行分析,掌握nginx变量能定制日志,打印用户行为、nginx运行状况、nginx运行参数进行分析。
用户发起请求,经三次握手后和nginx建立tcp连接,然后用户发送http请求到nginx,nginx收到请求后拆开请求报文开始处理,处理完后封装报文响应给用户。除了这些阶段,nginx自身也会产生一些变量,如nginx版本号、哪个worker子进程在处理请求等。
tcp连接相关变量
服务端指的是nginx,而不是nginx转发给的那个服务端
nginx默认端口80,默认协议微http 1.0/1.1
binary_remote_addr:固定4个字节,前面的remote_addr在不同服务器上占用字节不一样,但固定7-8个字节,所使用内存较多
connect:发起第一个请求建立tcp连接时,序号由1往上增加,重启nginx服务后,connect归零
nginx配置文件如下:
访问域名:
也可以在浏览器中直接访问域名,也会返回上述内容
发送http请求变量
args指的url中的变量
nginx处理后,这些特殊的变量的值可能被nginx改掉
请求行、请求头、server_name里都有host信息,查找顺序:请求行、请求头、server_name
实践特殊变量:
-H设置一下请求行,请求返回的就是设置的
处理http请求变量
request_filename:url在磁盘上对应哪个文件
反向代理基础原理
反向代理服务器介于用户和真实服务器之间,提供请求和响应的中转服务,对于用户而言,访问反向代理服务器就是访问真实服务器。反向代理可以有效降低服务器的负载消耗,提升效率。
假如互联网用户想访问你在内网中真实的服务器,用户是访问不到的,所以通常在中间部署一台代理服务器,这台代理服务器位于公司业务系统的边缘节点,这个边缘节点能和内网联通,我们还会在上面配置公网ip地址,确保域名可以解析到代理服务器上对应的ip地址,从而确保用户的请求能到达代理服务器。此时,用户想访问真实的服务器的ip时,他并不知道真实服务器在哪里,用户是给代理服务器发请求,然后代理服务器将请求转发给真实服务器,真实服务器处理完请求后,将处理结果封包再响应给代理服务器,代理服务器再将包传给用户。
反向代理的优势:
- 隐藏真实的服务器
- 便于横向扩充后端动态服务:如果后台服务器不够时,可以提供多台,所以反向代理和负载均衡相辅相成,如果一台服务器就够了那就没必要负载均衡
- 动静分离,提升系统健壮性:后端服务器提供动态服务,nginx自身处理静态服务比较有优势
动静分离
动静分离是指在web服务器架构中,将静态页面与动态页面或者静态内容接口和到昂台内容接口分开不同系统访问的架构设计方法,进而提升整个服务访问性能和可维护性。
但我们在浏览器中输入一个url需要请求某一个页面时,其实页面中所有的信息可能包含两类资源:动态资源、静态资源。如果你的网站只提供一些展示性的信息,可能只有某些静态资源,但对于某些交互性/社交性的网站,如论坛,通常都是有些动态资源的。
静态资源:任何用户访问的资源相同,如图片、html等
动态资源:不同用户访问的资源不同,如电商网站(用户的可用余额)
用户访问某个url,会给nginx发送一个请求,对于静态资源请求,nginx自身具有很强的处理能力,nginx会在自身某个文件目录下放上所有的静态资源,但是这个url中包含的动态请求nginx无法处理,会将其反向代理转发给后端服务器(即动态资源服务器)。假如nginx收到的并发请求越来越多了,并且这些并发请求很多都是动态请求,而后端服务器在处理动态资源请求的能力比较差,如果一台后端服务器不够用,可以再多增加几台,从而提升系统的吞吐量。
使用nginx作为反向代理所支持的协议
用于定义上游服务的upstream模块
upstream模块用于定义上游服务的相关信息。ng作为反向代理的场景下,他需要将一些动态请求转发给上游的应用程序服务器,上游的应用程序服务器是哪个端口提供的,url、ip、域名等信息需要定义在nginx中,upstream模块就是用来定义上游服务提供服务的相关信息的。
upstream默认已被编译进nginx,禁用项需通过--without-http_upstream_module
在upstream段中使用server指令指定后端应用服务的地址
address:ip:端口,后面还可以跟一些url
weight:权重越大,处理能力越强,nginx作为反向代理,可以响应的给权重越大的服务器分配更多的请求
max_conns:定义上游服务器最大并发连接数,超过这个值会被服务器拒绝
假如fail_timeout=10s,max_fails=3,那么在10s内如果出现3次,上游服务器没有给nginx返回结果,那么nginx认为上游服务器不可用,在本次10s内nginx不会再把请求调度给该服务器,等10s后才会再把请求调度给服务器。每次请求成功后,fail_timeout会重置。
如果将server指定为backup,表示当前服务器为备份服务器,正常情况下,nginx不会调度给他,只有当upstream中定义的其他服务器都不可用时,nginx才会把请求调度给这台服务器
建立一个长连接后,不可能无限制的允许请求的发生,也不可能允许这个长连接一直存在,假如设置keepalive_requests 100
,那么该长连接最多发送100个http请求后就会被强制关闭了,如果客户端还有请求发送过来,再启动一个新的连接来处理
所有上游服务器不可用时,请求到达nginx时,nginx不会直接给客户端响应5xx,而是把请求放到nginx在内存中开辟的队列,queue就是用来定义队列的长度,如果请求的放置时间超过timeout,也会超时。该指令只在商用版可用
配置nginx反向代理实例
所有对nginx上/proxy的访问都会转发到http://192.168.184.20:8080/proxy
proxy_pass指令用法常见误区
proxy_pass指令有两种常见用法:proxy_pass http://192.168.184.20:8080
和proxy_pass http://192.168.184.20:8080/
。不带/意味着nginx不会修改用户url,而是直接透穿给上游的应用服务器,带/意味着nginx会修改用户url,修改方法:将location后的url从用户url中删除。
代理场景下nginx接收用户请求包体的处理方式
nginx接收请求包体的两种方式:接收完全部包体再发送,一边接收包体一边发送。
- 设为on,nginx会在内存中开启一段缓冲区缓存用户发过来的请求包体,等nginx全部收到后再一次性转发给上游应用程序。适应场景:吞吐量要求高、上游服务并发处理能力低。如果一收到请求就转发给上游服务器,会建立很多连接,无形中增加了上游服务器的压力。
- 设为off,就不会开启缓冲区,nginx一收到请求包体就转发给上游服务器。使用场景:更及时的响应,减少nginx磁盘io。如果在nginx内存中开辟了一块缓冲区,如果请求包体的大小大于缓存区的大小,那么这个请求包体还是会被存储到磁盘上,只要到磁盘上了,就会有很多io操作。
client_max_body_size:nginx可以处理的请求体的大小。如果请求体大于这个值,nginx不会处理,会报错。
client_body_buffer_size:nginx开启的缓冲区的大小。32位机器上默认为8k,64位机器上默认为16k。
如果client_body_buffer_size < 请求体 < client_max_body_size
,请求体存储到磁盘指定目录:
client_body_in_single_buffer:设为on,请求体会尽可能分配在缓存区上连续的空间中,而不是离散的存储,连续的存储能提高性能,如果请求体大于缓存区的大小,请求体还是会被存在磁盘上
client_body_temp_path:指定磁盘路径,写相对路径,相对于nginx部署时的根目录
client_body_in_file_only:设为on,请求体大小不管是否大于缓存区大小时,请求体都会存在磁盘中。设为clean,请求体大小不管是否大于缓存区大小时,请求体都会存在磁盘中,但是等请求完成后,会删除缓存到磁盘上的文件。设为off,请求体大小大于缓存区大小时,请求体才会存在磁盘中
client_body_timeout:用户和nginx建立完连接后,如果长时间没有发送请求体的超时时间
代理场景下nginx如何更改发往上游的用户请求
filed指头部字段名,后面的value对应该字段要修改的值
如果想要nginx和上游服务器建立长连接,需要将proxy_http_version设为1.1,并且将proxy_set_header Connection设为keep-alive
proxy_pass_request_header:设为on,用户发过来的http请求中所有的请求头都会原封不动的转发到后端服务器,设为off,用户发过来的http请求中所有的请求头都不会转发到后端服务器
proxy_pass_request_body:设为on,用户发过来的http请求中所有的请求体都会原封不动的转发到后端服务器,设为off,用户发过来的http请求中所有的请求体都不会转发到后端服务器
使用-H添加请求头变量,使用-d添加请求体变量,得到的响应如下(注意:应该去掉【以下是请求体变量】这行字,因为content_length和content_type都是请求体变量):
修改nginx的配置:
此时的响应:
nginx设置proxy_pass_request_body off
:
使用-d添加请求体,此时请求体是不会被nginx转发给后端服务器的
但是如果将nginx配置中proxy_set_body那行放开,此时proxy_set_body设置的body还是会被nginx转发给后端服务器,proxy_pass_request_body
限制的只是用户传过来的请求体
代理场景下nginx与上游服务器建立连接细节
在nginx作为反向代理时,长连接有两个概念:客户端到nginx、nginx到上游服务
timeout设为0表示关闭
nginx到上游服务的指令都是用在upstream模块
nginx连接后端服务器的超时时间,也就是三次握手的超时时间,nginx发起三次握手后,在60s内没有和后端服务器成功建立连接,就会报超时错误
设为on,复用tcp层的长连接,tcp层的长连接是由内核来提供的
nginx在proxy_send_timeout时间内没有向后端服务器发送任何内容时的超时时间
用户发送到nginx的请求如果用户主动把连接断开了,nginx是否会忽略他已经发送给后端服务器的请求,设为on,nginx也会放弃他已经发送给后端服务器的请求,设为off,nginx与后端服务器正常响应后再关闭
负载均衡
负载均衡基础原理
将请求代理到多台服务器区执行,就称之为负载均衡,这里的多台服务器通常承担一样的功能任务。
配置实现nginx对上游服务负载均衡
nginx配置三台服务器,用户访问/balance/会被分发到这三台服务器上
服务器配置的响应:
请求会轮询打在每台服务器上(不是说顺序打在每台服务器上,而是在可预见周期内,尽可能的分配):
配置两台服务器,并为每台服务器配置不同的权重
请求会有两次打在第一台服务器上,一次打在第二台服务器上:
负载均衡算法—哈希算法
哈希算法是将任意长度的二进制值映射为较短的固定长度的二进制值,这个小的二进制值我们称之为哈希值。散落明文到哈希值的映射是不可逆的,即无法根据哈希值推算出内容。常用于验证文件唯一性。
key是内容,可以是变量,也可以是请求头的内容等,反正他是个内容
使用轮询策略时,请求会被分发到不同的后端服务器,假如第一次请求nginx分发给第一台服务器,然后nginx缓存了响应,然后nginx第二次把请求分发给了第二台服务器,那缓存的第一台服务器的信息就不可用了,所有,有时候需要保证,来自客户端的请求总是能分配到指定的服务器上,可以使用hash算法实现
只要uri不变,永远把请求分配到某台服务器上
负载均衡算法-ip_hash算法
ip_hash算法:根据ip进行hash运算,只要ip不变,请求会永远被分配到后端某一台固定的应用服务器上。主要用于解决nginx服务器和后端服务器session保持的,某些客户端在请求服务器后,通常会有些cookie信息在客户端保存,但是对应到服务端的时候,他会有一些固定的session信息,这些session信息对于用户来说是不同的,这时,对于固定的客户端来说,使用ip_hash算法进行session保持
负载均衡算法-最少连接数算法
前面讲的两种算法都没有将后端应用服务器当前的负荷状况考虑进去,他只是根据从用户发来的请求做一些负载均衡算法的挑选,加权轮询就更简单粗暴了,他不会将任何因素考虑在内,只是简单的将请求逐个分发,hash和ip_hash只能根据某些特定的属性对请求进行分发,但是这些属性也仅仅是来自于用户的请求,其实这样的场景是不合理的,假如应用服务器一的处理能力很强,应用服务器二、三的处理能力特别的弱,如果此时还采用加权轮询算法,应用服务器二已经不堪重负了,这时如果nginx仍然分发请求给他,其实是不合理的。所以本节引入最少连接算法,从上游服务器挑选一台当前已经建立连接数最少的分配请求,极端情形下(每台应用服务器在处理相同个数的请求)退化为rr算法(轮询算法)
假如nginx只有1个子进程,每次请求都会到这个子进程,这个子进程将请求转发到后端服务器,他总是能拿到后端服务器对应信息,比如,第一次分配给服务器一,他可以去探测服务器一当前处理多少个请求,到第二个时候他去探测服务器二,到第三个的时候他去探测服务器三,从而实现最小连接。但是nginx通常运行在多核cpu上,会对应启动多个子进程,那在多个子进程前景下,用户请求可能第一次在第一个子进程,第二次到第二个子进程……,也就是每次请求会被分配到不同的子进程,对于子进程来说,此时如果你想要启用最小连接算法,不同的子进程是怎么知道每个应用服务器当前正在处理的请求有多少个?包括当前的响应状态是什么样的,也就是说,后端应用服务器的具体状态信息无法在不同子进程间共享,所以我们会在nginx上开辟一块内存空间作为共享内存来使用,也就是,子进程在处理用户请求的时候,都会将后端对应的应用服务器的当前状态,如果说当前正在处理的用户连接数,处理失败的次数等服务器的状态信息保存在共享内存中,我们都知道共享内存是子进程共同访问的,那第二次调度到第二个子进程时,第二个子进程可以去共享内存中读取对应服务器的信息,从而实现不同子进程可以共享后端应用服务器的信息,此时能实现最少连接的算法
nginx针对上游服务返回异常时的容错机制
用户请求到达nginx后,nginx会依据一定的负载均衡算法将请求转发到上游的某一台应用服务器上,假如这台应用服务器由于内部的某些原因或者网络原因导致请求迟迟没有处理完返回给nginx,此时我们是否可以做一些容错措施?比如将失败的请求转发给另外一台服务器,其实在反向代理的场景中也有一些指令是用来定义这些功能的。
proxy_next_upstream:在规定时间内应用服务器未响应,nginx会把请求转发给其他服务器
post方法就是非幂等请求
ngixn将请求转发给应用服务器一,在规定时间内,应用服务器没有响应,规定的超时时间应该等待多久,为0表示可以无限制等待
失败时会转发proxy_next_upstream_tries次,如果超过proxy_next_upstream_tries次则nginx直接给客户端返回错误,若为0,则不限制转发次数
后端服务器响应:
nginx配置:
如果使用error,如果后端服务器响应报错,请求会被nginx转发到另一台应用服务器
如果使用timeout,对于error的场景就不会转发了,只有响应超时了,才会把请求转发到另一台服务器
对后台服务器4050端口响应给nginx时进行限速,1s只返回1个字节
nginx配置在5s内必须将应用服务器的响应全部读取回来,如果读不完,则返回timeout响应错误
实际结果:当请求转发到4050端口时,会等5s才会响应,响应的内容是4040的内容,以此验证了超时转发
应用服务器响应503:
nginx配置,如果应用服务器响应503,则转发
请求永远会转发到4040应用服务器
设为on,打开响应处理流程,将上游请求按照nginx定义的error_page响应给客户端,设为off,对与响应码大于300的请求,nginx不会将其转发到其他服务器,而是直接将结果返回给客户端
缓存
缓存基础
用户发起request请求,请求到达nginx之后,静态内容会直接返回,但是动态内容会交给后端服务器处理,所有nginx会将请求转发给后端,应用服务器在处理完请求后,封装响应包体返回给nginx,nginx收到response包体后返回给用户。在整个过程中,nginx的处理能力是非常强的,但是处理动态内容的应用服务器,可能还会跟数据库服务器交互再返回内容,那应用服务器的处理时间是比较长的,那就有个矛盾,用户发起请求,请求很快到达nginx服务器,但是应用服务器处理结果返回给nginx需要很长时间,用户感觉也就慢了,如果想提升用户体验,nginx提供了一些缓存能力,当请求第一次到达nginx时,nginx可以依据一定的缓存策略发送请求给应用服务器,应用服务器处理完请求封装包体返回给nginx时,nginx在返回给客户端之前,他会依据缓存策略将某一部分信息缓存到自身的cache(在nginx内存中开辟的一段空间)中,因此,用户第一次请求时,缓存中是没有对应的内容的,这时nginx必须发起请求给应用服务器获取,但是当第一次获取到这些内容后,nginx会把这些内容放到cache中,等下次用户请求时,nginx发现这个URL请求的是同样的内容,nginx不会再向应用服务器发起请求了,而是直接将自身内存中的cache存的信息返回给客户端,从这个角度来说,请求就不需要再转发给应用服务器了。
缓存分类:
- 客户端缓存(用户浏览器):当nginx响应内容给客户端后,客户端也能缓存,当用户刷新浏览器时,如果浏览器自身有缓存,那这个请求就不需要发给nginx了,直接在本地磁盘或者文件夹中拿到内容,在用户浏览器中解析呈现。
(1)优势:直接在本地获取内容,没有网络消耗,响应最快
(2)缺点:仅对单一用户生效 - 服务端缓存(nginx)
(1)优点:对所有用户生效;有效降低上游应用服务器压力
(2)缺点:用户仍然有网络消耗
最佳实践:同时启用客户端缓存和服务端缓存
缓存相关指令用法
设为zone会开启对应用服务器的返回进行缓存
path:缓存的内容信息保存在磁盘上的路径。nginx决定缓存应用服务器的响应时,他首先会把缓存放在内存中,但他同时也会将这部分信息写到磁盘上,这个path就用来指定我们在文件系统中的某个目录。写在磁盘里还可以避免nginx重启时,缓存内容失效,nginx重启时,会将缓存从磁盘中载入内存中
keys_zone:size:用来指定共享内存的名称(随机定义个字符串即可),和可用内存空间的大小(在nginx中,1m大约可以缓存8000个key)。
use_temp_path:设为on时,要保证proxy_temp_path和上面的path在同一个磁盘文件系统中,不要跨磁盘,否则会有性能开销,导致并发能力下降。这个参数不常用,设为off即可
inactive:缓存在inactive时间内没有被访问就会被清理
cm:cache manager缓存管理器进程
cl:cache load缓存加载进程
缓存内容时所保存的key信息
决定对哪些内容进行响应
code:http返回状态码,后面跟一个time,表示在这段时间内,缓存是有效的
上游服务器缓存状态,用来看缓存是否命中,在nginx配置中把该变量加入头部信息中,这样客户端请求url时,可以看到是否命中缓存
缓存用法配置示例
配置nginx不缓存特定内容
proxy_no_cache后面可以跟一个变量(变量返回的就是字符串),如果这个变量有值的话,就不缓存,否则会被缓存
proxy_cache_bypass:不缓存,将用户的请求直接转发到上游服务器
服务器设置:
nginx设置,请求的uri以txt或者test结尾,就将cookie_name设为“no cahe”:
location块中,假如引用的cookie_name有值,这部分内容都不会被缓存,而是直接去上游应用服务器获取新的内容
第二次请求txt文件,nginx-cache-status还是为Miss(第一次请求为Miss是正常的),表示没有命中缓存
第二次请求html文件,nginx-cache-status还是为hit,表示命中缓存
可以从缓存文件路径下看到,确实缓存了123.html
缓存失效降低上游压力机制
合并源请求
缓存失效时,如何降低请求穿透nginx从而导致上游服务器压力大的场景,第一种机制可以合并源请求。当用户发了很多并发请求到nginx,假如此时nginx刚重启,由于缓存全部失效导致请求必须由nginx透出给应用服务器,在并发请求很大的情况下,由于nginx的处理能力比应用服务器处理能力强的多,那么nginx透传的这些并发请求很可能让应用服务器瘫痪,为了避免这种情况,nginx也采取了一些机制
proxy_cache_lock设为off,并发请求会透传给应用服务器,设为on,并发请求只会有一个转发到应用服务器,其余请求进入等待状态,直到应用服务器响应了请求,并把响应内容返回给nginx时,nginx会把资源缓存到本地,此时nginx会先响应客户端这个请求,然后,对于等待中的请求,nginx会直接从缓存中取内容响应
proxy_cache_lock_timeout:设置第一个请求超时时间。假如第一个请求经过proxy_cache_lock_timeout应用服务器都还没有响应,那么等待的请求会全部透传给应用服务器,这样其实也会给应用服务器造成压力
proxy_cache_lock_age:假如第一个请求经过proxy_cache_lock_timeout应用服务器都还没有响应,那么会发送第二个请求,如果第二个请求经过proxy_cache_lock_age都还没有响应,那么再发第三个请求,如果第三个请求经过proxy_cache_lock_age都还没有响应,那么再发第四个请求……这样一个一个请求发
启用陈旧缓存
对于某些对数据一致性要求不高的应用来说,能够返回一个结果远远比直接返回404给用户的体验好的多,但是如果是对数据实时性要求很高的网站,比如支付性的网站,那不适合开启陈旧缓存
在nginx缓存失效的情况下,用户发起了很多并发请求,nginx发现有缓存,只不过缓存失效了,并且此时第一个请求应用服务器还没有响应结果时,nginx会直接把失效的缓存返回给其余没有转发给应用服务器的并发请求,等nginx转发给应用服务器的第一个请求已经响应了,nginx会更新缓存,然后把响应内容返回给第一个请求,后面新的请求过来都正常访问了。
updating:第一个请求已经由nginx转发给应用服务器了,此时应用服务器还没有响应,返回陈旧缓存
error:服务器宕掉或者nginx自身发生了什么错误,返回陈旧缓存
timeout:nginx自身压力很大了,并发请求已经处理不过来了,转发出去的请求也迟迟得不到结果,自己内部的请求头部的定时器也超时了,返回陈旧缓存
默认对4xx、5xx开头的响应码不做缓存,这通常是客户端/服务端错误,但是如果想缓存,也可以通过http_状态码
进行缓存
proxy_cache_background_update:在后台执行新的缓存更新。设为off,第一个请求过来,nginx发现缓存失效,第一个请求由nginx交由上游服务器,此时对于第一个请求来说是得不到结果的,他需要等待应用服务器响应。设为on,nginx不管收到的是第几个请求,都会把失效的缓存返回给用户,然后自身发起一个http请求给应用服务器,请求更新资源,他不是客户端请求更新资源了,而是nginx自身发起请求更新缓存。
第三方缓存清除模块ngx_cache_purge
在nginx商业版本中会有一些清除缓存的模块,但是我们使用的都是开源版本,我们只能通过第三方的缓存清除模块来完成我们对指定的缓存进行清除的功能
ngx_cache_purge:根据接受的http请求立即清除缓存。使用–add-module指令添加到nginx中
前面在讲到proxy_cache_key时,用来指定根据哪些key来做缓存,那我们都知道,nginx在对静态资源做缓存时,他需要根据资源的某一部分来做hash计算,可以使用这个指令指定对哪一部分做hash计算。与proxy_cache_purge相似,也是指定缓存的key
应用服务器配置:
ngx_cache_purge接收的就是http请求,当用户发送这个http请求时,nginx会清除某些特定的缓存,
用户请求cache.kutian.edu/cache_purge/下的资源时,如:cache.kutian.edu/cache_purge/123.html,nginx会清除该资源的缓存。proxy_cache_purge后面指定的cache_zone就是proxy_cache指定的cache_zone,当用户访问cache.kutian.edu/cache_purge/123.html时,(/.*)
指向的就是/123.html这个变量,proxy_cache_purge后面跟的key就是$host$1
,$1
引用的就是(/.*)
指向的变量,此时他会去找$host
后面跟一个/123.html对应的key,因为写了proxy_cache_key $host$uri
,所以这个key会被缓存在文件中,nginx直接就可以找到
用户第一次访问资源,拿到的是没有缓存的响应。然后,查看nginx缓存资源的文件夹,发现已经缓存了资源
用户第二次访问资源,命中缓存
查看nginx缓存的资源,确实是123.html
用户请求清除缓存,然后查看nginx缓存资源的文件夹,发现缓存被清除:
https原理基础
http协议存在的问题:
- 数据使用明文传输,可能被黑客盗取
- 报文的完整性无法验证,可能被黑客篡改
- 无法验证通信双方的身份,可能被黑客伪装
对称加密算法实现信息加密,通过散列函数实现信息完整性校验,非对称加密算法实现身份认证
https如何解决信息被窃听的问题
https通过信息加密来解决http数据明文传输,可能被黑客窃取的问题。
加密算法:
- 对称加密算法:DES、AES、3DES
- 非对称加密算法:RSA、DSA、ECC
对称加密算法:客户端和服务器使用同一个密钥,那么约定同一个密钥就成了问题,密钥在网络上传输很容易被监听和窃取,一旦密钥被第三者拿到,客户端和服务器传输数据就是明文传输了。
优势:解密效率高
劣势:
- 密钥无法实现安全传输
- 密钥的数目难以管理:每个客户端和服务器进行数据传输都需要约定一个密钥,有多少个客户端服务器就需要维护多少个密钥
- 无法提供信息完整性校验
非对称加密:
优势:服务器仅维护一个私钥即可
劣势:
- 公钥是公开的
- 非对称加密算法加解密过程中会耗费一些时间
- 公钥并不包含服务器信息,存在中间人攻击的可能性
https加密原理:https混合使用对称加密和非对称加密。连接建立阶段使用非对称加密,内容传输阶段使用对称加密算法
客户端向服务器发送请求,服务器收到请求后,生成一对证书,将公钥返回给客户端,客户端拿到公钥后,验证证书的有效性,假如证书是有效的,客户端利用伪随机数算法(对称加密算法)生成对称加密密钥,客户端使用对称加密密钥加密收到的服务器的公钥证书,然后客户端将公钥证书和加密后的公钥证书一起发送给服务器,这样就算黑客拿到了公钥信息也没关系,因为在后续数据传输过程中,使用的是客户端的对称加密密钥,客户端的对称加密密钥会使用服务器的公钥再次加密,服务器使用私钥解密,得到客户端的对称加密密钥。后续客户端和服务器使用对称加密密钥进行数据传输
https如何解决报文被篡改问题
服务器在返回内容给客户端之前,会先使用hash算法对内容进行计算生成消息摘要,服务器使用私钥加密消息摘要后生成数字签名,服务器返回数字报文给客户端时,会返回两部分,一部分是这个报文内容的数字签名,另一部分是内容数据报文,客户端拿到后如何验证内容没有被篡改呢?
客户端会对数据报文重新做hash计算生成消息摘要,服务器返回客户端数据报文时,也会返回这个数据报文的数字签名,客户端使用服务器公钥对数字签名进行解密,得到消息摘要,客户端比较两个消息摘要,如果一致,表明客户端收到的报文没有被篡改,否则被篡改。在这个过程中,客户端如何验证数字签名就是服务器返回的呢?为了解决身份认证问题,引入了ca,也就是验证证书是谁颁发的。
我们有了一个网站域名后,域名一般归属于某个组织,需要我们去注册的,所以在申请ca证书时,通常会将组织信息、域名信息等提交给ca证书颁发机构,证书颁发机构通过某些手段验证信息,验证完了后给域名颁发ca证书,ca证书包括域名的组织信息、ca证书颁发机构、证书有效期、证书序列号等,同时会包含一个数字签名。我们给服务器配上ca证书后,客户端向服务器发送请求时,服务器会将ca证书返回给客户端,客户端读取证书的明文信息,采用hash算法计算信息摘要,然后使用ca的公钥解密数据,对比信息摘要,对比一致则认为信息合法,否则信息非法。