反向代理、负载均衡服务器Nginx入门到精通
¶一、Nginx简介
¶1、背景介绍
Nginx(“engine x”)一个具有高性能的【HTTP】和【反向代理】的【WEB服务器】,同时也是一个【POP3/SMTP/IMAP代理服务器】,是由伊戈尔·赛索耶夫(俄罗斯人)使用C语言编写的,Nginx的第一个版本是2004年10月4号发布的0.1.0版本。另外值得一提的是伊戈尔·赛索耶夫将Nginx的源码进行了开源,这也为Nginx的发展提供了良好的保障。
¶2、名词解释
¶WEB服务器
WEB服务器也叫网页服务器,英文名叫Web Server,主要功能是为用户提供网上信息浏览服务。
¶HTTP
HTTP是超文本传输协议的缩写,是用于从WEB服务器传输超文本到本地浏览器的传输协议,也是互联网上应用最为广泛的一种网络协议。HTTP是一个客户端和服务器端请求和应答的标准,客户端是终端用户,服务端是网站,通过使用Web浏览器、网络爬虫或者其他工具,客户端发起一个到服务器上指定端口的HTTP请求。
¶POP3/SMTP/IMAP
POP3(Post Offic Protocol 3)邮局协议的第三个版本,
SMTP(Simple Mail Transfer Protocol)简单邮件传输协议,
IMAP(Internet Mail Access Protocol)交互式邮件存取协议,
通过上述名词的解释,我们可以了解到Nginx也可以作为电子邮件代理服务器。
¶反向代理
正向代理
反向代理
¶3、常见服务器对比
¶IIS
全称(Internet Information Services)即互联网信息服务,是由微软公司提供的基于windows系统的互联网基本服务。windows作为服务器在稳定性与其他一些性能上都不如类UNIX操作系统,因此在需要高性能Web服务器的场合下,IIS可能就会被"冷落".
¶Tomcat
Tomcat是一个运行Servlet和JSP的Web应用软件,Tomcat技术先进、性能稳定而且开放源代码,因此深受Java爱好者的喜爱并得到了部分软件开发商的认可,成为目前比较流行的Web应用服务器。但是Tomcat天生是一个重量级的Web服务器,对静态文件和高并发的处理比较弱。
¶Apache
Apache的发展时期很长,同时也有过一段辉煌的业绩。从上图可以看出大概在2014年以前都是市场份额第一的服务器。Apache有很多优点,如稳定、开源、跨平台等。但是它出现的时间太久了,在它兴起的年代,互联网的产业规模远远不如今天,所以它被设计成一个重量级的、不支持高并发的Web服务器。在Apache服务器上,如果有数以万计的并发HTTP请求同时访问,就会导致服务器上消耗大量能存,操作系统内核对成百上千的Apache进程做进程间切换也会消耗大量的CUP资源,并导致HTTP请求的平均响应速度降低,这些都决定了Apache不可能成为高性能的Web服务器。这也促使了Lighttpd和Nginx的出现。
¶Lighttpd
Lighttpd是德国的一个开源的Web服务器软件,它和Nginx一样,都是轻量级、高性能的Web服务器,欧美的业界开发者比较钟爱Lighttpd,而国内的公司更多的青睐Nginx,同时网上Nginx的资源要更丰富些。
¶其他的服务器
Google Servers,Weblogic, Webshpere(IBM)…
经过各个服务器的对比,种种迹象都表明,Nginx将以性能为王。这也是我们为什么选择Nginx的理由。
¶4、Nginx的优点
¶速度更快、并发更高
单次请求或者高并发请求的环境下,Nginx都会比其他Web服务器响应的速度更快。一方面在正常情况下,单次请求会得到更快的响应,另一方面,在高峰期(如有数以万计的并发请求),Nginx比其他Web服务器更快的响应请求。Nginx之所以有这么高的并发处理能力和这么好的性能原因在于Nginx采用了多进程和I/O多路复用(epoll)的底层实现。
¶配置简单,扩展性强
Nginx的设计极具扩展性,它本身就是由很多模块组成,这些模块的使用可以通过配置文件的配置来添加。这些模块有官方提供的也有第三方提供的模块,如果需要完全可以开发服务自己业务特性的定制模块。
¶高可靠性
Nginx采用的是多进程模式运行,其中有一个master主进程和N多个worker进程,worker进程的数量我们可以手动设置,每个worker进程之间都是相互独立提供服务,并且master主进程可以在某一个worker进程出错时,快速去"拉起"新的worker进程提供服务。
¶热部署
现在互联网项目都要求以7*24小时进行服务的提供,针对于这一要求,Nginx也提供了热部署功能,即可以在Nginx不停止的情况下,对Nginx进行文件升级、更新配置和更换日志文件等功能。
¶成本低、BSD许可证
BSD是一个开源的许可证,世界上的开源许可证有很多,现在比较流行的有六种分别是GPL、BSD、MIT、Mozilla、Apache、LGPL。这六种的区别是什么,我们可以通过下面一张图来解释下:
Nginx本身是开源的,我们不仅可以免费的将Nginx应用在商业领域,而且还可以在项目中直接修改Nginx的源码来定制自己的特殊要求。这些点也都是Nginx为什么能吸引无数开发者继续为Nginx来贡献自己的智慧和青春。OpenRestry [Nginx+Lua] Tengine[淘宝]
¶5、Nginx的功能特性及常用功能
Nginx提供的基本功能服务从大体上归纳为"基本HTTP服务"、“高级HTTP服务”和"邮件服务"等三大类。
¶基本HTTP服务
Nginx可以提供基本HTTP服务,可以作为HTTP代理服务器和反向代理服务器,支持通过缓存加速访问,可以完成简单的负载均衡和容错,支持包过滤功能,支持SSL等。
- 处理静态文件、处理索引文件以及支持自动索引;
- 提供反向代理服务器,并可以使用缓存加上反向代理,同时完成负载均衡和容错;
- 提供对FastCGI、memcached等服务的缓存机制,,同时完成负载均衡和容错;
- 使用Nginx的模块化特性提供过滤器功能。Nginx基本过滤器包括gzip压缩、ranges支持、chunked响应、XSLT、SSI以及图像缩放等。其中针对包含多个SSI的页面,经由FastCGI或反向代理,SSI过滤器可以并行处理。
- 支持HTTP下的安全套接层安全协议SSL.
- 支持基于加权和依赖的优先权的HTTP/2
¶高级HTTP服务
- 支持基于名字和IP的虚拟主机设置
- 支持HTTP/1.0中的KEEP-Alive模式和管线(PipeLined)模型连接
- 自定义访问日志格式、带缓存的日志写操作以及快速日志轮转。
- 提供3xx~5xx错误代码重定向功能
- 支持重写(Rewrite)模块扩展
- 支持重新加载配置以及在线升级时无需中断正在处理的请求
- 支持网络监控
- 支持FLV和MP4流媒体传输
¶邮件服务
Nginx提供邮件代理服务也是其基本开发需求之一,主要包含以下特性:
- 支持IMPA/POP3代理服务功能
- 支持内部SMTP代理服务功能
¶Nginx常用的功能模块
1 |
|
¶Nginx的核心组成
1 |
|
¶二、Nginx环境准备
¶1、Nginx版本介绍
Nginx的官方网站为: https://nginx.org,Nginx的官方下载网站为 https://nginx.org/en/download.html
¶2、获取Nginx源码
http://nginx.org/download/ 打开上述网站,就可以查看到Nginx的所有版本,选中自己需要的版本进行下载。
¶3、准备服务器系统
确认centos的内核:
准备一个内核为2.6及以上版本的操作系统,因为linux2.6及以上内核才支持epoll,而Nginx需要解决高并发压力问题是需要用到epoll,所以我们需要有这样的版本要求。
我们可以使用uname -a
命令来查询linux的内核版本。
确认停用selinux:
selinux(security-enhanced linux),美国安全局对于强制访问控制的实现,在linux2.6内核以后的版本中,selinux已经成功内核中的一部分。可以说selinux是linux史上最杰出的新安全子系统之一。虽然有了selinux,我们的系统会更安全,但是对于我们的学习Nginx的历程中,会多很多设置,所以这块建议大家将selinux进行关闭。
sestatus查看状态:
如果查看不是disabled状态,我们可以通过修改配置文件来进行设置,修改SELINUX=disabled,然后重启下系统即可生效。
1 |
|
¶4、Nginx安装方式即依赖介绍
Nginx的安装方式有两种分别是:
1 |
|
如果通过Nginx源码安装需要提前安装以下依赖:
GCC编译器:
Nginx是使用C语言编写的程序,因此想要运行Nginx就需要安装一个编译工具。GCC就是一个开源的编译器集合,用于处理各种各样的语言,其中就包含了C语言。
使用命令yum install -y gcc
来安装
安装成功后,可以通过gcc --version
来查看gcc是否安装成功
PCRE:
Nginx在编译过程中需要使用到PCRE库(perl Compatible Regular Expressoin 兼容正则表达式库),因为在Nginx的Rewrite模块和http核心模块都会使用到PCRE正则表达式语法。
可以使用命令yum install -y pcre pcre-devel
来进行安装
安装成功后,可以通过rpm -qa pcre pcre-devel
来查看是否安装成功
zlib:
zlib库提供了开发人员的压缩算法,在Nginx的各个模块中需要使用gzip压缩,所以我们也需要提前安装其库及源代码zlib和zlib-devel
可以使用命令yum install -y zlib zlib-devel
来进行安装
安装成功后,可以通过rpm -qa zlib zlib-devel
来查看是否安装成功
OpenSSL:
OpenSSL是一个开放源代码的软件库包,应用程序可以使用这个包进行安全通信,并且避免被窃听。
SSL:Secure Sockets Layer安全套接协议的缩写,可以在Internet上提供秘密性传输,其目标是保证两个应用间通信的保密性和可靠性。在Nginx中,如果服务器需要提供安全网页时就需要用到OpenSSL库,所以我们需要对OpenSSL的库文件及它的开发安装包进行一个安装。
可以使用命令yum install -y openssl openssl-devel
来进行安装
安装成功后,可以通过rpm -qa openssl openssl-devel
来查看是否安装成功
上述命令,一个个来的话比较麻烦,我们也可以通过一条命令来进行安装:
yum install -y gcc pcre pcre-devel zlib zlib-devel openssl openssl-devel
进行全部安装。
¶5、Nginx的源码简单安装
1、进入官网查找需要下载版本的链接地址,然后使用wget命令进行下载
1 |
|
2、建议大家将下载的资源进行包管理
1 |
|
3、解压缩
1 |
|
4、进入资源文件中,发现configure
1 |
|
5、编译
1 |
|
¶6、yum安装
使用源码进行简单安装,我们会发现安装的过程比较繁琐,需要提前准备GCC编译器、PCRE兼容正则表达式库、zlib压缩库、OpenSSL安全通信的软件库包,然后才能进行Nginx的安装。官方文档yum安装介绍:https://nginx.org/en/linux_packages.html#RHEL-CentOS
1、安装yum-utils
1 |
|
2、添加yum源文件
1 |
|
1 |
|
3、查看是否配置成功
1 |
|
4、使用yum进行安装
1 |
|
5、查看nginx的安装位置
1 |
|
¶7、源码简单安装和yum安装的差异
这里先介绍一个命令: ./nginx -V
, 通过该命令可以查看到所安装Nginx的版本及相关配置信息。
简单安装:
yum安装:
¶8、Nginx的源码复杂安装
这种方式和简单的安装配置不同的地方在第一步,通过./configure
来对编译参数进行设置,需要我们手动来指定。那么都有哪些参数可以进行设置,接下来我们进行一个详细的说明。
·/configure --help
可以获得一些配置项参数:
- PATH:是和路径相关的配置信息
- with:是启动模块,默认是关闭的
- without:是关闭模块,默认是开启的
一些简单的路径配置已经通过这些配置来完成一个简单的编译:
–prefix=PATH
1 |
|
–sbin-path=PATH
1 |
|
–modules-path=PATH
1 |
|
–conf-path=PATH
1 |
|
–error-log-path=PATH
1 |
|
–http-log-path=PATH
1 |
|
–pid-path=PATH
1 |
|
–lock-path=PATH
1 |
|
要想使用可以通过如下命令:
这里的值就是源码简单安装的默认值!
1 |
|
在使用上述命令之前,需要将之前服务器已经安装的nginx进行卸载,卸载的步骤分为三步骤:
步骤一:需要将nginx的进程关闭
1 |
|
步骤二:将安装的nginx进行删除
1 |
|
步骤三:将安装包之前编译的环境清除掉
1 |
|
¶9、解压Nginx目录介绍
执行tar -zxvf nginx-1.16.1.tar.gz
对下载的资源进行解压缩,进入压缩后的目录,可以看到如下结构:
内容解释:
auto:存放的是编译相关的脚本
CHANGES:版本变更记录
CHANGES.ru:俄罗斯文的版本变更记录
conf:默认的配置文件
configure:nginx软件的自动脚本程序,是一个比较重要的文件,作用如下:
- 检测环境及根据环境检测结果生成C代码(会在当前目录下多一个objs文件夹)
- 生成编译代码需要的Makefile文件(会在当前目录下多一个Makefile文件)
contrib:存放的是几个特殊的脚本文件,其中README中对脚本有着详细的说明
html:存放的是Nginx自带的两个html页面,访问Nginx的首页和错误页面
LICENSE:许可证的相关描述文件
man:nginx的man手册
README:Nginx的阅读指南
src:Nginx的源代码
¶10、Nginx目录结构分析
在使用Nginx之前,我们先对安装好的Nginx目录文件进行一个分析,在这块给大家介绍一个工具tree,通过tree我们可以很方面的去查看centos系统上的文件目录结构,当然,如果想使用tree工具,就得先通过yum install -y tree
来进行安装,安装成功后,可以通过执行tree /usr/local/nginx
(tree后面跟的是Nginx的安装目录),获取的结果如下:
conf:nginx所有配置文件目录
-
CGI(Common Gateway Interface)通用网关【接口】,主要解决的问题是从客户端发送一个请求和数据,服务端获取到请求和数据后可以调用调用CGI【程序】处理及相应结果给客户端的一种标准规范。
-
fastcgi.conf:fastcgi相关配置文件
-
fastcgi.conf.default:fastcgi.conf的备份文件
-
fastcgi_params:fastcgi的参数文件
-
fastcgi_params.default:fastcgi的参数备份文件
-
scgi_params:scgi的参数文件
-
scgi_params.default:scgi的参数备份文件
-
uwsgi_params:uwsgi的参数文件
-
uwsgi_params.default:uwsgi的参数备份文件
-
mime.types:记录的是HTTP协议中的Content-Type的值和文件后缀名的对应关系
-
mime.types.default:mime.types的备份文件
-
nginx.conf:这个是Nginx的核心配置文件,这个文件非常重要,也是我们即将要学习的重点
-
nginx.conf.default:nginx.conf的备份文件
-
koi-utf、koi-win、win-utf:这三个文件都是与编码转换映射相关的配置文件,用来将一种编码转换成另一种编码
html:存放nginx自带的两个静态的html页面
- 50x.html:访问失败后的失败页面
- index.html:成功访问的默认首页
logs:记录入门的文件,当nginx服务器启动后,这里面会有 access.log error.log 和 nginx.pid 三个文件出现。
sbin:是存放执行程序文件nginx。nginx是用来控制Nginx的启动和停止等相关的命令。
¶11、Nginx服务器启停命令
Nginx安装完成后,接下来我们要学习的是如何启动、重启和停止Nginx的服务。
对于Nginx的启停在linux系统中也有很多种方式:
- Nginx服务的信号控制
- Nginx的命令行控制
¶Nginx服务的信号控制
前面在提到Nginx的高性能,其实也和它的架构模式有关。Nginx默认采用的是多进程的方式来工作的,当将Nginx启动后,我们通过ps -ef | grep nginx
命令可以查看到如下内容:
从上图中可以看到,Nginx后台进程中包含一个master进程和多个worker进程。
master进程主要用来管理worker进程,包含接收外界的信息,并将接收到的信号发送给各个worker进程,监控worker进程的状态,当worker进程出现异常退出后,会自动重新启动新的worker进程。
而worker进程则是专门用来处理用户请求的,各个worker进程之间是平等的并且相互独立,处理请求的机会也是一样的。nginx的进程模型,我们可以通过下图来说明下:
我们现在作为管理员,只需要通过给master进程发送信号就可以来控制Nginx,这个时候我们需要有两个前提条件,一个是要操作的master进程,一个是信号。
1、要想操作Nginx的master进程,就需要获取到master进程的进程号ID。获取方式简单介绍两个,
方式一:通过ps -ef | grep nginx
;
方式二:在讲解nginx的./configure
的配置参数的时候,有一个参数是--pid-path=PATH
默认是/usr/local/nginx/logs/nginx.pid
,所以可以通过查看该文件来获取nginx的master进程ID.
2、信号
信号 | 作用 |
---|---|
TERM/INT | 立即关闭整个服务 |
QUIT | "优雅"地关闭整个服务(等待worker处理完毕任务后再关闭) |
HUP | 重读配置文件并使用服务对新配置项生效 |
USR1 | 重新打开日志文件,可以用来进行日志切割 |
USR2 | 平滑升级到最新版的nginx |
WINCH | 所有子进程不在接收处理新连接,相当于给work进程发送QUIT指令 |
调用命令为kill -signal PID
signal:即为信号;PID即为获取到的master线程ID
- 发送TERM/INT信号给master进程,会将Nginx服务立即关闭:
1 |
|
- 发送QUIT信号给master进程,master进程会控制所有的work进程不再接收新的请求,等所有请求处理完后,在把进程都关闭掉:
1 |
|
- 发送HUP信号给master进程,master进程会把控制旧的work进程不再接收新的请求,等处理完请求后将旧的work进程关闭掉,然后根据nginx的配置文件重新启动新的work进程:
1 |
|
- 发送USR1信号给master进程,告诉Nginx重新开启日志文件
1 |
|
- 发送USR2信号给master进程,告诉master进程要平滑升级,这个时候,会重新开启对应的master进程和work进程,整个系统中将会有两个master进程,并且新的master进程的PID会被记录在
/usr/local/nginx/logs/nginx.pid
而之前的旧的master进程PID会被记录在/usr/local/nginx/logs/nginx.pid.oldbin
文件中,接着再次发送QUIT信号给旧的master进程,让其处理完请求后再进行关闭
1 |
|
- 发送WINCH信号给master进程,让master进程控制不让所有的work进程在接收新的请求了,请求处理完后关闭work进程。注意master进程不会被关闭掉
1 |
|
¶Nginx的命令行控制
此方式是通过Nginx安装目录下的sbin下的可执行文件nginx来进行Nginx状态的控制,我们可以通过nginx -h
来查看都有哪些参数可以用:
-?和-h:显示帮助信息
-v:打印版本号信息并退出
-V:打印版本号信息和配置信息并退出
-t:测试nginx的配置文件语法是否正确并退出
-T:测试nginx的配置文件语法是否正确并列出用到的配置文件信息然后退出
-q:在配置测试期间禁止显示非错误消息
-s:signal信号,后面可以添加
- stop:快速关闭,类似于TERM/INT信号的作用
- quit:优雅的关闭,类似于QUIT信号的作用
- reopen:重新打开日志文件类似于USR1信号的作用
- reload:类似于HUP信号的作用
-p:prefix,指定Nginx的prefix路径,(默认为: /usr/local/nginx/)
-c:filename,指定Nginx的配置文件路径,(默认为: /usr/local/nginx/conf/nginx.conf)
-g:用来补充Nginx配置文件,向Nginx服务指定启动时应用全局的配置
¶12、Nginx服务器版本升级和新增模块
如果想对Nginx的版本进行更新,或者要应用一些新的模块,最简单的做法就是停止当前的Nginx服务,然后开启新的Nginx服务。但是这样会导致在一段时间内,用户是无法访问服务器。为了解决这个问题,我们就需要用到Nginx服务器提供的平滑升级功能。这个也是Nginx的一大特点,使用这种方式,就可以使Nginx在7*24小时不间断的提供服务了。接下来我们分析下需求:
1 |
|
为了应对上述的需求,提供两种解决方案:
- 使用Nginx服务信号完成Nginx的升级
- 使用Nginx安装目录的make命令完成升级
环境准备:
1、先准备两个版本的Nginx分别是 1.14.2和1.16.1
2、使用Nginx源码安装的方式将1.14.2版本安装成功并正确访问
1 |
|
3、将Nginx1.16.1进行参数配置和编译,不需要进行安装。
1 |
|
¶使用Nginx服务信号进行升级
1、将1.14.2版本的sbin目录下的nginx进行备份
1 |
|
2、将Nginx1.16.1安装目录编译后的objs目录下的nginx文件,拷贝到原来/usr/local/nginx/sbin
目录下
1 |
|
3、发送信号USR2给Nginx的1.14.2版本对应的master进程
1 |
|
4、发送信号QUIT给Nginx的1.14.2版本对应的master进程
1 |
|
¶使用Nginx安装目录的make命令完成升级
1、将1.14.2版本的sbin目录下的nginx进行备份
1 |
|
2、将Nginx1.16.1安装目录编译后的objs目录下的nginx文件,拷贝到原来/usr/local/nginx/sbin
目录下
1 |
|
3、进入到安装目录,执行make upgrade
会发现其实底层使用的也是信号控制的!
4、查看是否更新成功
1 |
|
¶三、Nginx核心配置文件结构
Nginx的核心配置文件默认位置:/usr/local/nginx/conf/nginx.conf
¶1、配置文件介绍
读取Nginx自带的Nginx配置文件,我们将其中的注释部分删除掉后,就剩下下面内容:
1 |
|
简单小结下:
- nginx.conf配置文件中默认有三大块:全局块、events块、http块
- http块中可以配置多个server块,每个server块又可以配置多个location块。
¶2、全局块
¶user指令
user用于配置运行Nginx服务器的worker进程的用户和用户组。
语法 | user user [group] |
---|---|
默认值 | nobody |
位置 | 全局块 |
该属性也可以在编译的时候指定,语法如下./configure --user=user --group=group
,如果两个地方都进行了设置,最终生效的是配置文件中的配置。
该指令的使用步骤:
1、设置一个用户信息"www"
1 |
|
此时使用命令./nginx -t
检查会发现报错,原因是本机目前还没有www用户!
2、Linux创建一个用户
1 |
|
3、创建/root/html/index.html
页面,添加如下内容
4、修改nginx.conf
1 |
|
5、测试启动访问
1 |
|
6、将文件创建到 /home/www/html/index.html
,修改配置
1 |
|
7、再次测试正常启动访问
综上所述,使用user指令可以指定启动运行工作进程的用户及用户组,这样对于系统的权限访问控制的更加精细,也更加安全。
¶work process指令
master_process:用来指定是否开启工作进程。(修改该值需要重启nginx)
语法 | master_process on|off; |
---|---|
默认值 | master_process on; |
位置 | 全局块 |
worker_processes:用于配置Nginx生成工作进程的数量,这个是Nginx服务器实现并发处理服务的关键所在。理论上来说workder process的值越大,可以支持的并发处理量也越多,但事实上这个值的设定是需要受到来自服务器自身的限制,建议将该值和服务器CPU的内核数保存一致。
语法 | worker_processes num/auto; |
---|---|
默认值 | 1 |
位置 | 全局块 |
如果将worker_processes设置成2,则会看到如下内容:
¶其他指令
1、daemon:设定Nginx是否以守护进程的方式启动。
守护式进程是linux后台执行的一种服务进程,特点是独立于控制终端,不会随着终端关闭而停止。(也需要重启生效)
语法 | daemon on|off; |
---|---|
默认值 | daemon on; |
位置 | 全局块 |
2、pid:用来配置Nginx当前master进程的进程号ID存储的文件路径。
语法 | pid file; |
---|---|
默认值 | 默认为:/usr/local/nginx/logs/nginx.pid |
位置 | 全局块 |
该属性可以通过./configure --pid-path=PATH
来指定
3、error_log:用来配置Nginx的错误日志存放路径
语法 | error_log file [日志级别]; |
---|---|
默认值 | error_log logs/error.log error; |
位置 | 全局块、http、server、location |
该属性可以通过./configure --error-log-path=PATH
来指定
其中日志级别的值有:debug|info|notice|warn|error|crit|alert|emerg,翻译过来为试|信息|通知|警告|错误|临界|警报|紧急,这块建议大家设置的时候不要设置成info以下的等级,因为会带来大量的磁盘I/O消耗,影响Nginx的性能。
4、include:用来引入其他配置文件,使Nginx的配置更加灵活
语法 | include file; |
---|---|
默认值 | 无 |
位置 | any |
¶3、events块
1、accept_mutex:用来设置Nginx网络连接序列化
语法 | accept_mutex on|off; |
---|---|
默认值 | accept_mutex on; |
位置 | events |
这个配置主要可以用来解决常说的**"惊群"问题。大致意思是在某一个时刻,客户端发来一个请求连接,Nginx后台是以多进程的工作模式,也就是说有多个worker进程会被同时唤醒,但是最终只会有一个进程可以获取到连接**,如果每次唤醒的进程数目太多,就会影响Nginx的整体性能。如果将上述值设置为on(开启状态),将会对多个Nginx进程接收连接进行序列号,一个个来唤醒接收,就防止了多个进程对连接的争抢。(高并发时关闭更好!)
2、multi_accept:用来设置是否允许同时接收多个网络连接
语法 | multi_accept on|off; |
---|---|
默认值 | multi_accept off; |
位置 | events |
如果multi_accept被禁止了,nginx一个工作进程只能同时接受一个新的连接。否则,一个工作进程可以同时接受所有的新连接
3、worker_connections:用来配置单个worker进程最大的连接数
语法 | worker_connections number; |
---|---|
默认值 | worker_commections 512; |
位置 | events |
这里的连接数不仅仅包括和前端用户建立的连接数,而是包括所有可能的连接数。另外,number值不能大于操作系统支持打开的最大文件句柄数量。
4、use:用来设置Nginx服务器选择哪种事件驱动来处理网络消息。
语法 | use method; |
---|---|
默认值 | 根据操作系统定 |
位置 | events |
注意:此处所选择事件处理模型是Nginx优化部分的一个重要内容,method的可选值有select/poll/epoll/kqueue等,之前在准备centos环境的时候,我们强调过要使用linux内核在2.6以上,就是为了能使用epoll函数来优化Nginx。
另外这些值的选择,我们也可以在编译的时候使用
--with-select_module
、--without-select_module
、
--with-poll_module
、 --without-poll_module
来设置是否需要将对应的事件驱动模块编译到Nginx的内核。
events指令配置实例:
打开Nginx的配置文件 nginx.conf,添加如下配置
1 |
|
启动测试
1 |
|
¶4、http块
MIME-Type:
我们都知道浏览器中可以显示的内容有HTML、XML、GIF等种类繁多的文件、媒体等资源,浏览器为了区分这些资源,就需要使用MIME Type。所以说MIME Type是网络资源的媒体类型。Nginx作为web服务器,也需要能够识别前端请求的资源类型。
在Nginx的配置文件中,默认有两行配置:
1 |
|
default_type:用来配置Nginx响应前端请求默认的MIME类型。
语法 | default_type mime-type; |
---|---|
默认值 | default_type text/plain; |
位置 | http、server、location |
在default_type之前还有一句include mime.types
,include之前我们已经介绍过,相当于把mime.types文件中MIMT类型与相关类型文件的文件后缀名的对应关系加入到当前的配置文件中。
举例来说明:
有些时候请求某些接口的时候需要返回指定的文本字符串或者json字符串,如果逻辑非常简单或者干脆是固定的字符串,那么可以使用nginx快速实现,这样就不用编写程序响应请求了,可以减少服务器资源占用并且响应性能非常快。
例子:
1 |
|
¶5、自定义服务日志
Nginx中日志的类型分access.log、error.log。
- access.log:用来记录用户所有的访问请求。
- error.log:记录nginx本身运行时的错误信息,不会记录用户的访问请求。
Nginx服务器支持对服务日志的格式、大小、输出等进行设置,需要使用到两个指令,分别是access_log和log_format指令。
1、access_log:用来设置用户访问日志的相关属性。
语法 | access_log path[format[buffer=size]] |
---|---|
默认值 | access_log logs/access.log combined; |
位置 | http , server , location |
2、log_format:用来指定日志的输出格式。
语法 | log_format name [escape=default|json|none] string…; |
---|---|
默认值 | log_format combined “…”; |
位置 | http |
¶6、其他配置指令
1、sendfile:用来设置Nginx服务器是否使用sendfile()传输文件,该属性可以大大提高Nginx处理静态资源的性能
语法 | sendfile on|off; |
---|---|
默认值 | sendfile off; |
位置 | http、server、location |
2、keepalive_timeout:用来设置长连接的超时时间。
为什么要使用keepalive?
1 |
|
语法 | keepalive_timeout time; |
---|---|
默认值 | keepalive_timeout 75s; |
位置 | http、server、location |
3、keepalive_requests:用来设置一个keep-alive连接使用的次数。
语法 | keepalive_requests number; |
---|---|
默认值 | keepalive_requests 100; |
位置 | http、server、location |
¶7、server块和location块
server块和location块,非常重要,后面介绍!
1 |
|
¶四、Nginx服务器基础配置实例
¶1、目标需求
需求:
1 |
|
准备相关文件,目录如下:
¶2、配置内容
1 |
|
server1.conf
1 |
|
server2.conf
1 |
|
¶五、Nginx服务操作的问题
经过前面的操作,我们会发现,如果想要启动、关闭或重新加载nginx配置文件,都需要先进入到nginx的安装目录的sbin目录,然后使用nginx的二级制可执行文件来操作,相对来说操作比较繁琐,这块该如何优化?另外如果我们想把Nginx设置成随着服务器启动就自动完成启动操作。
¶1、Nginx配置成系统服务
把Nginx应用服务设置成为系统服务,方便对Nginx服务的启动和停止等相关操作。
在/usr/lib/systemd/system
目录下添加nginx.service,内容如下:
1 |
|
1 |
|
添加完成后如果权限有问题需要进行权限设置:
1 |
|
使用系统命令来操作Nginx服务:
1 |
|
¶2、Nginx命令配置到系统环境
Nginx安装目录下的二级制可执行文件nginx
的很多命令,要想使用这些命令前提是需要进入sbin目录下才能使用,我们可以将该二进制可执行文件加入到系统的环境变量,这样的话在任何目录都可以使用nginx对应的相关命令。
1、修改/etc/profile
文件
1 |
|
2、使之立即生效
1 |
|
3、执行nginx命令
1 |
|
¶六、Nginx静态资源部署
¶1、Nginx静态资源概述
上网去搜索访问资源对于我们来说并不陌生,通过浏览器发送一个HTTP请求实现从客户端发送请求到服务器端获取所需要内容后并把内容回显展示在页面的一个过程。这个时候,我们所请求的内容就分为两种类型,一类是静态资源、一类是动态资源。
- 静态资源即指在服务器端真实存在并且能直接拿来展示的一些文件,比如常见的html页面、css文件、js文件、图 片、视频等资源;
- 动态资源即指在服务器端真实存在但是要想获取需要经过一定的业务逻辑处理,根据不同的条件展示在页面不同这 一部分内容,比如说报表数据展示、根据当前登录用户展示相关具体数据等资源;
Nginx处理静态资源的内容,我们需要考虑下面这几个问题:
1 |
|
¶2、Nginx静态资源的配置指令
¶listen指令
listen:用来配置监听端口。
语法 | listen address[:port] [default_server]…; listen port [default_server]…; |
---|---|
默认值 | listen *:80 | *:8000 |
位置 | server |
listen设置几种方式:
1 |
|
default_server:是标识符,用来将此虚拟主机设置成默认主机。所谓的默认主机指的是如果没有匹配到对应的address:port,则会默认执行的。如果不指定默认使用的是第一个server。
一个问题,server_name设置的是127.0.0.1,但是我们访问的是192内网地址,为何还能访问的到?
因为default_server的默认值就是找第一个server的配置进行访问,即访问了server_name的配置!我们可以在listen后面添加default_server来指定默认server,即可以不按照第一个默认server去找!按照我们指定的顺序去访问!
1 |
|
¶server_name指令
server_name:用来设置虚拟主机服务名称。127.0.0.1 、 localhost 、域名。
语法 | server_name name …; name可以提供多个中间用空格分隔 |
---|---|
默认值 | server_name “”; |
位置 | server |
配置方式一:精确匹配
1 |
|
配置方式二:使用通配符配置
server_name中支持通配符"*", 但需要注意的是通配符不能出现在域名的中间,只能出现在首段或尾段,如:
1 |
|
配置三:使用正则表达式配置
server_name中可以使用正则表达式,并且使用~
作为正则表达式字符串的开始标记。
配置如下:
1 |
|
匹配执行顺序
由于server_name指令支持通配符和正则表达式,因此在包含多个虚拟主机的配置文件中,可能会出现一个名称被多个虚拟主机的server_name匹配成功,当遇到这种情况,当前的请求交给谁来处理呢?
1 |
|
结论:
1 |
|
¶location指令
location:用来设置请求的URI
语法 | location [ = | ~ | ~* | ^~ |@ ] uri{…} |
---|---|
默认值 | — |
位置 | server,location |
uri变量是待匹配的请求字符串,可以不包含正则表达式,也可以包含正则表达式,那么nginx服务器在搜索匹配location的时候,是先使用不包含正则表达式进行匹配,找到一个匹配度最高的一个,然后在通过包含正则表达式的进行匹配,如果能匹配到直接访问,匹配不到,就使用刚才匹配度最高的那个location来处理请求。
不带符号,要求必须以指定模式开始:
1 |
|
=
: 用于不包含正则表达式的uri前,必须与指定的模式精确匹配
1 |
|
~
: 用于表示当前uri中包含了正则表达式,并且区分大小写
~*
: 用于表示当前uri中包含了正则表达式,并且不区分大小写
1 |
|
^~
: 用于不包含正则表达式的uri前,功能和不加符号的一致,唯一不同的是,如果模式匹配,那么就停止搜索其他模式了。
1 |
|
¶root / alias指令
root:设置请求的根目录
语法 | root path; |
---|---|
默认值 | root html; |
位置 | http、server、location |
path为Nginx服务器接收到请求以后查找资源的根目录路径。
alias:用来更改location的URI
语法 | alias path; |
---|---|
默认值 | — |
位置 | location |
path为修改后的根路径。
以上两个指令都可以来指定访问资源的路径,那么这两者之间的区别是什么?
在/usr/local/nginx/html
目录下创建一个 images目录,并在目录下放入一张图片mv.png
图片
1 |
|
总结:
- root的处理结果是:root路径+location路径。
- alias的处理结果是:使用alias路径替换location路径。
- alias是一个目录别名的定义,root则是最上层目录的含义。
- 如果location路径是以/结尾,则alias也必须是以/结尾,root没有要求。(可以在error.log中查看日志得到访问的路径!)
解决:
1 |
|
¶index指令
index:设置网站的默认首页。
index后面可以跟多个设置,如果访问的时候没有指定具体访问的资源,则会依次进行查找,找到第一个为止。
语法 | index file …; |
---|---|
默认值 | index index.html; |
位置 | http、server、location |
举例说明:
1 |
|
¶error_page指令
error_page:设置网站的错误页面。当出现对应的响应code后,如何来处理。
语法 | error_page code … [=[response]] uri; |
---|---|
默认值 | — |
位置 | http、server、location… |
1、可以指定具体跳转的地址
1 |
|
2、可以指定重定向地址
1 |
|
3、使用location的@符合完成错误信息展示
1 |
|
4、可选项=[response]
的作用是用来将相应代码更改为另外一个
1 |
|
¶3、静态资源优化配置语法
¶sendfile
用来开启高效的文件传输模式。
语法 | sendfile on |off; |
---|---|
默认值 | sendfile off; |
位置 | http、server、location… |
请求静态资源的过程:
使用了sendfile后少了进程切换和两次copy!http底层使用tcp,tcp使用socket传输!
¶tcp_nopush
该指令必须在sendfile打开的状态下才会生效,主要是用来提升网络包的传输’效率’。
语法 | tcp_nopush on|off; |
---|---|
默认值 | tcp_nopush off; |
位置 | http、server、location |
¶tcp_nodelay
该指令必须在keep-alive连接开启的情况下才生效,来提高网络包传输的’实时性’。
语法 | tcp_nodelay on|off; |
---|---|
默认值 | tcp_nodelay on; |
位置 | http、server、location |
tcp_nopush和tcp_nodelay区别:
¶总结
经过刚才的分析,“tcp_nopush"和”tcp_nodelay“看起来是"互斥的”,那么为什么要将这两个值都打开?
这个大家需要知道的是在linux2.5.9以后的版本中两者是可以兼容的,三个指令都开启的好处是:
- sendfile可以开启高效的文件传输模式
- tcp_nopush开启可以确保在发送到客户端之前数据包已经充分“填满”, 这大大减少了网络开销,并加快了文件发送的速度。
- 然后,当它到达最后一个可能因为没有“填满”而暂停的数据包时,Nginx会忽略tcp_nopush参数, 然后,tcp_nodelay强制套接字发送数据。
由此可知,TCP_NOPUSH可以与TCP_NODELAY一起设置,它比单独配置TCP_NODELAY具有更强的性能。所以我们可以使用如下配置来优化Nginx静态资源的处理
1 |
|
¶4、Nginx静态资源压缩实战
在Nginx的配置文件中可以通过配置gzip来对静态资源进行压缩,相关的指令可以配置在http块、server块和location块。
相关指令依赖如下模块进行解析:
1 |
|
¶Gzip模块配置指令
接下来的指令都来自ngx_http_gzip_module模块,该模块会在nginx安装的时候内置到nginx的安装环境中,也就是说我们可以直接使用这些指令。
1、gzip指令:该指令用于开启或者关闭gzip功能
语法 | gzip on|off; |
---|---|
默认值 | gzip off; |
位置 | http、server、location… |
注意只有该指令为打开状态,下面的指令才有效果
1 |
|
2、gzip_types指令:该指令可以根据响应页的MIME类型选择性地开启Gzip压缩功能
语法 | gzip_types mime-type …; |
---|---|
默认值 | gzip_types text/html; |
位置 | http、server、location |
所选择的值可以从mime.types文件中进行查找,也可以使用"*"代表所有。(不建议使用*
浪费服务器资源,例如视频和图片)
1 |
|
3、gzip_comp_level指令:该指令用于设置Gzip压缩程度,级别从1-9,1表示要是程度最低,要是效率最高,9刚好相反,压缩程度最高,但是效率最低最费时间。
建议设置为6! 级别更高,压缩效率慢,压缩的也不会更加厉害!
语法 | gzip_comp_level level; |
---|---|
默认值 | gzip_comp_level 1; |
位置 | http、server、location |
1 |
|
4、gzip_vary指令:该指令用于设置使用Gzip进行压缩发送是否携带“Vary:Accept-Encoding”头域的响应头部。主要是告诉接收方,所发送的数据经过了Gzip压缩处理
语法 | gzip_vary on|off; |
---|---|
默认值 | gzip_vary off; |
位置 | http、server、location |
5、gzip_buffers指令:该指令用于处理请求压缩的缓冲区数量和大小。
语法 | gzip_buffers number size; |
---|---|
默认值 | gzip_buffers 32 4k|16 8k; |
位置 | http、server、location |
- number:指定Nginx服务器向系统申请缓存空间个数,size指的是每个缓存空间的大小。主要实现的是申请number个每个大小为size的内存空间。这个值的设定一般会和服务器的操作系统有关,所以建议此项不设置,使用默认值即可。
1 |
|
6、gzip_disable指令:针对不同种类客户端发起的请求,可以选择性地开启和关闭Gzip功能。
语法 | gzip_disable regex …; |
---|---|
默认值 | — |
位置 | http、server、location |
regex:根据客户端的浏览器标志(user-agent)来设置,支持使用正则表达式。指定的浏览器标志不使用Gzip。该指令一般是用来排除一些明显不支持Gzip的浏览器。
1 |
|
7、gzip_http_version指令:针对不同的HTTP协议版本,可以选择性地开启和关闭Gzip功能。
语法 | gzip_http_version 1.0|1.1; |
---|---|
默认值 | gzip_http_version 1.1; |
位置 | http、server、location |
该指令是指定使用Gzip的HTTP最低版本,该指令一般采用默认值即可。
8、gzip_min_length指令:该指令针对传输数据的大小,可以选择性地开启和关闭Gzip功能
Gzip压缩功能对大数据的压缩效果明显,但是如果要压缩的数据比较小的化,可能出现越压缩数据量越大的情况,因此我们需要根据响应内容的大小来决定是否使用Gzip功能,响应页面的大小可以通过头信息中的
Content-Length
来获取。但是如何使用了Chunk编码动态压缩,该指令将被忽略。建议设置为1K或以上。
语法 | gzip_min_length length; |
---|---|
默认值 | gzip_min_length 20; |
位置 | http、server、location |
1 |
|
9、gzip_proxied指令:该指令设置是否对服务端返回的结果进行Gzip压缩。
语法 | gzip_proxied off|expired|no-cache| no-store|private|no_last_modified|no_etag|auth|any; |
---|---|
默认值 | gzip_proxied off; |
位置 | http、server、location |
off:关闭Nginx服务器对后台服务器返回结果的Gzip压缩
expired:启用压缩,如果header头中包含 “Expires” 头信息
no-cache:启用压缩,如果header头中包含 “Cache-Control:no-cache” 头信息
no-store:启用压缩,如果header头中包含 “Cache-Control:no-store” 头信息
private:启用压缩,如果header头中包含 “Cache-Control:private” 头信息
no_last_modified:启用压缩,如果header头中不包含 “Last-Modified” 头信息
no_etag:启用压缩 ,如果header头中不包含 “ETag” 头信息
auth:启用压缩 , 如果header头中包含 “Authorization” 头信息
any:无条件启用压缩
¶Gzip压缩功能的实例配置
1 |
|
我们可以将这些内容抽取到一个配置文件中,然后通过include指令把配置文件再次加载到nginx.conf配置文件中:
nginx_gzip.conf
1 |
|
nginx.conf
1 |
|
¶Gzip和sendfile共存问题
前面在讲解sendfile的时候,提到过,开启sendfile以后,在读取磁盘上的静态资源文件的时候,可以减少拷贝的次数,可以不经过用户进程将静态文件通过网络设备发送出去,但是Gzip要想对资源压缩,是需要经过用户进程进行操作的。所以如何解决两个设置的共存问题。
可以使用ngx_http_gzip_static_module模块的gzip_static指令来解决。(默认没有这个模块)
¶添加模块到Nginx的实现步骤
1、查询当前Nginx的配置参数(主要用于记录以前配置过的默认信息)
1 |
|
2、将nginx安装目录下sbin目录中的nginx二进制文件进行更名
1 |
|
3、进入Nginx的安装目录
1 |
|
4、执行make clean清空之前编译的内容
1 |
|
5、使用configure来配置参数
1 |
|
6、使用make命令进行编译
1 |
|
7、将objs目录下的nginx二进制执行文件移动到nginx安装目录下的sbin目录中
1 |
|
8、执行更新命令(在nginx解压目录下执行)
1 |
|
¶gzip_static指令
注意:需要关了gzip开关,无需在应用程序中压缩,gzip off;
gzip_static: 检查与访问资源同名的.gz文件时,response中以gzip相关的header返回.gz文件的内容。先进行服务器上的压缩为.gz文件,再进行资源传输。
语法 | gzip_static on | off | always; |
---|---|
默认值 | gzip_static off; |
位置 | http、server、location |
¶gzip_static测试使用
1、直接访问http://192.168.200.133/jquery.js
2、使用gzip命令进行压缩
1 |
|
3、再次访问http://192.168.200.133/jquery.js
多了Content-Encoding:gzip 和 Vary:Accept-Encoding
¶5、静态资源的缓存处理
¶什么是缓存
缓存(cache),原始意义是指访问速度比一般随机存取存储器(RAM)快的一种高速存储器,通常它不像系统主存那样使用DRAM技术,而使用昂贵但较快速的SRAM技术。缓存的设置是所有现代计算机系统发挥高性能的重要因素之一。
¶什么是web缓存
Web缓存是指一个Web资源(如html页面,图片,js,数据等)存在于Web服务器和客户端(浏览器)之间的副本。缓存会根据进来的请求保存输出内容的副本;当下一个请求来到的时候,如果是相同的URL,缓存会根据缓存机制决定是直接使用副本响应访问请求,还是向源服务器再次发送请求。
比较常见的就是浏览器会缓存访问过网站的网页,当再次访问这个URL地址的时候,如果网页没有更新,就不会再次下载网页,而是直接使用本地缓存的网页。只有当网站明确标识资源已经更新,浏览器才会再次下载网页
¶web缓存的种类
客户端缓存:浏览器缓存
服务端缓存:Nginx / Redis / Memcached等
¶浏览器缓存
是为了节约网络的资源加速浏览,浏览器在用户磁盘上对最近请求过的文档进行存储,当访问者再次请求这个页面时,浏览器就可以从本地磁盘显示文档,这样就可以加速页面的阅览.
是成本最低的一种缓存实现减少网络带宽消耗降低服务器压力减少网络延迟,加快页面打开速度!
¶浏览器缓存的执行流程
HTTP协议中和页面缓存相关的字段:
header | 说明 |
---|---|
Expires | 缓存过期的日期和时间 |
Cache-Control | 设置和缓存相关的配置信息 |
Last-Modified | 请求资源最后修改时间 |
ETag | 请求变量的实体标签的当前值,比如文件的MD5值 |
缓存执行流程示意图:
执行过程如下:
- 用户首次通过浏览器发送请求到服务端获取数据,客户端是没有对应的缓存,所以需要发送request请求来获取数据;
- 服务端接收到请求后,获取服务端的数据及服务端缓存的允许后,返回200的成功状态码并且在响应头上附上对应资源以及缓存信息;
- 当用户再次访问相同资源的时候,客户端会在浏览器的缓存目录中查找是否存在响应的缓存文件
- 如果没有找到对应的缓存文件,则走第二步
- 如果有缓存文件,接下来对缓存文件是否过期进行判断,过期的判断标准是(Expires),
- 如果没有过期,则直接从本地缓存中返回数据进行展示(强缓存)
- 如果Expires过期,接下来需要判断缓存文件是否发生过变化
- 判断的标准有两个,一个是ETag(Entity Tag),一个是Last-Modified
- 判断结果是未发生变化,则服务端返回304,直接从缓存文件中获取数据(弱缓存)
- 如果判断是发生了变化,重新从服务端获取数据,并根据缓存协商(服务端所设置的是否需要进行缓存数据的设置)来进行数据缓存。
弱缓存请求头图示: 记得去掉浏览器disabled cache的勾。
注意:直接刷新页面和F5会导致强缓存失效。一定要在开一个页面,提前打开浏览器控制台的网络选项卡查看!
强缓存图示:
¶浏览器缓存相关指令
¶expires指令
该指令用来控制页面缓存的作用。可以通过该指令控制HTTP应答中的“Expires"和”Cache-Control"。(单位默认秒)
语法 | expires [modified] time expires epoch|max|off; |
---|---|
默认值 | expires off; |
位置 | http、server、location |
-
time:可以整数也可以是负数,指定过期时间。如果是负数,Cache-Control则为no-cache;如果为整数或0,则Cache-Control的值为max-age=time;
- max-age为新的http1.1使用的,和expires类似。expires会由于服务器和本地时间不一致而不使用缓存!
-
epoch:指定Expires的值为’1 January,1970,00:00:01 GMT’(1970-01-01 00:00:00),Cache-Control的值no-cache
-
max:指定Expires的值为’31 December2037 23:59:59GMT’ (2037-12-31 23:59:59) ,Cache-Control的值为10年
-
off:默认不缓存。
给静态资源设置缓存:
1 |
|
分别为没有设置、设置为max,1000,负数 响应头示意图:
¶add_header指令
add_header指令是用来添加指定的响应头和响应值。
语法 | add_header name value [always]; |
---|---|
默认值 | — |
位置 | http、server、location… |
Cache-Control作为响应头信息,可以设置如下值:
缓存响应指令:
1 |
|
指令 | 说明 |
---|---|
must-revalidate | 可缓存但必须再向源服务器进行确认 |
no-cache | 缓存前必须确认其有效性 |
no-store | 不缓存请求或响应的任何内容 |
no-transform | 代理不可更改媒体类型 |
public | 可向任意方提供响应的缓存 |
private | 仅向特定用户返回响应 |
proxy-revalidate | 要求中间缓存服务器对缓存的响应有效性再进行确认 |
max-age=<秒> | 响应最大Age值 |
s-maxage=<秒> | 公共缓存服务器响应的最大Age值 |
¶6、Nginx的跨域问题解决
¶同源策略
浏览器的同源策略:是一种约定,是浏览器最核心也是最基本的安全功能,如果浏览器少了同源策略,则浏览器的正常功能可能都会受到影响。
同源: 协议、域名(IP)、端口相同即为同源
1 |
|
¶跨域问题
简单描述下:有两台服务器分别为A,B,如果从服务器A的页面发送异步请求到服务器B获取数据,如果服务器A和服务器B不满足同源策略,则就会出现跨域问题。
¶跨域问题演示
1、nginx的html目录下新建一个a.html
1 |
|
2、在nginx.conf配置如下内容
1 |
|
3、通过浏览器访问测试
¶解决方案
使用add_header指令,该指令可以用来添加一些头信息:
语法 | add_header name value… |
---|---|
默认值 | — |
位置 | http、server、location |
此处用来解决跨域问题,需要添加两个头信息:Access-Control-Allow-Origin
,Access-Control-Allow-Methods
- Access-Control-Allow-Origin: 直译过来是允许跨域访问的源地址信息,可以配置多个(多个用逗号分隔),也可以使用
*
代表所有源 - Access-Control-Allow-Methods:直译过来是允许跨域访问的请求方式,值可以为 GET POST PUT DELETE…,可以全部设置,也可以根据需要设置,多个用逗号分隔
具体配置方式:
1 |
|
¶7、静态资源防盗链
¶什么是资源盗链
资源盗链指的是此内容不在自己服务器上,而是通过技术手段,绕过别人的限制将别人的内容放到自己页面上最终展示给用户。以此来盗取大网站的空间和流量。简而言之就是用别人的东西成就自己的网站。
效果演示:
1 |
|
我们自己准备一个html页面,在页面上img标签引入这两个图片查看效果:
¶Nginx防盗链原理
了解防盗链的原理之前,我们得先学习一个HTTP的头信息Referer,当浏览器向web服务器发送请求的时候,一般都会带上Referer,来告诉浏览器该网页是从哪个页面链接过来的。
后台服务器可以根据获取到的这个Referer信息来判断是否为自己信任的网站地址,如果是则放行继续访问,如果不是则可以返回403(服务端拒绝访问)的状态信息。
Nginx防盗链的具体实现:
valid_referers:nginx会通就过查看referer自动和valid_referers后面的内容进行匹配,如果匹配到了就将$invalid_referer变量置0,如果没有匹配到,则将$invalid_referer变量置为1,匹配的过程中不区分大小写。
语法 | valid_referers none|blocked|server_names|string… |
---|---|
默认值 | — |
位置 | server、location |
- none:如果Header中的Referer为空,允许访问
- blocked:在Header中的Referer不为空,但是该值被防火墙或代理进行伪装过,如不带"http://" 、"https://"等协议头的资源允许访问。
- server_names:指定具体的域名或者IP
- string:可以支持正则表达式和*的字符串。如果是正则表达式,需要以
~
开头表示,例如
注意: if后面必须有空格
1 |
|
¶针对目录进行防盗链
1 |
|
这样我们可以对一个目录下的所有资源进行了防盗操作。
遇到的问题:Referer的限制比较粗,比如随意加一个Referer,上面的方式是无法进行限制的。那么这个问题改如何解决?
此处我们需要用到Nginx的第三方模块ngx_http_accesskey_module
,后面模块篇内容介绍!
¶七、Rewrite功能配置
Rewrite是Nginx服务器提供的一个重要基本功能,是Web服务器产品中几乎必备的功能。主要的作用是用来实现URL的重写。
注意:Nginx服务器的Rewrite功能的实现依赖于PCRE的支持,因此在编译安装Nginx服务器之前,需要安装PCRE库。
Nginx使用的是ngx_http_rewrite_module模块来解析和处理Rewrite功能的相关配置。(内置,无需安装)
¶1、地址重写与地址转发
- 地址重写浏览器地址会发生变化而地址转发则不变
- 一次地址重写会产生两次请求而一次地址转发只会产生一次请求
- 地址重写到的页面必须是一个完整的路径而地址转发则不需要
- 地址重写因为是两次请求所以request范围内属性不能传递给新页面而地址转发因为是一次请求所以可以传递值
- 地址转发速度快于地址重写
¶2、Rewrite的相关指令
¶set指令
该指令用来设置一个新的变量。
语法 | set $variable value; |
---|---|
默认值 | — |
位置 | server、location、if |
variable:变量的名称,该变量名称要用"$"作为变量的第一个字符,且不能与Nginx服务器预设的全局变量同名。
value:变量的值,可以是字符串、其他变量或者变量的组合等。
¶Rewrite常用全局变量
不一定是nginx的全局变量!
变量 | 说明 |
---|---|
$args | 变量中存放了请求URL中的请求指令。比如 http://192.168.200.133:8080?arg1=value1&args2=value2 中的"arg1=value1&arg2=value2",功能和$query_string一样 |
$http_user_agent | 变量存储的是用户访问服务的代理信息(如果通过浏览器访问,记录的是浏览器的相关版本信息) |
$host | 变量存储的是访问服务器的server_name值 |
$document_uri | 变量存储的是当前访问地址的URI。比如 http://192.168.200.133/server?id=10&name=zhangsan 中的"/server",功能和$uri一样 |
$document_root | 变量存储的是当前请求对应location的root值,如果未设置,默认指向Nginx自带html目录所在位置 |
$content_length | 变量存储的是请求头中的Content-Length的值 |
$content_type | 变量存储的是请求头中的Content-Type的值 |
$http_cookie | 变量存储的是客户端的cookie信息,可以通过add_header Set-Cookie 'cookieName=cookieValue’来添加cookie数据 |
$limit_rate | 变量中存储的是Nginx服务器对网络连接速率的限制,也就是Nginx配置中对limit_rate指令设置的值,默认是0,不限制。 |
$remote_addr | 变量中存储的是客户端的IP地址 |
$remote_port | 变量中存储了客户端与服务端建立连接的端口号 |
$remote_user | 变量中存储了客户端的用户名,需要有认证模块才能获取 |
$scheme | 变量中存储了访问协议 |
$server_addr | 变量中存储了服务端的地址 |
$server_name | 变量中存储了客户端请求到达的服务器的名称 |
$server_port | 变量中存储了客户端请求到达服务器的端口号 |
$server_protocol | 变量中存储了客户端请求协议的版本,比如"HTTP/1.1" |
$request_body_file | 变量中存储了发给后端服务器的本地文件资源的名称 |
$request_method | 变量中存储了客户端的请求方式,比如"GET","POST"等 |
$request_filename | 变量中存储了当前请求的资源文件的路径名 |
$request_uri | 变量中存储了当前请求的URI,并且携带请求参数,比如 http://192.168.200.133/server?id=10&name=zhangsan 中的"/server?id=10&name=zhangsan" |
¶if指令
该指令用来支持条件判断,并根据条件判断结果选择不同的Nginx配置。
语法 | if (condition){…} |
---|---|
默认值 | — |
位置 | server、location |
condition为判定条件,可以支持以下写法:
1、变量名。如果变量名对应的值为空字符串或"0",if都判断为false,其他条件为true。
1 |
|
2、使用"=“和”!="比较变量和字符串是否相等,满足条件为true,不满足为false
1 |
|
注意:
- 此处和Java不太一样的地方是字符串不需要添加引号。
- 浏览器无法直接发送post请求,可以编写form表单提交发送。也可以直接使用
curl -X post 请求地址
来发送!
3、使用正则表达式对变量进行匹配,匹配成功返回true,否则返回false。
~
代表匹配正则表达式过程中区分大小写~*
代表匹配正则表达式过程中不区分大小写!~
和!~
刚好和上面取相反值,如果匹配上返回false,匹配不上返回true
1 |
|
注意: 正则表达式字符串一般不需要加引号,但是如果字符串中包含"}“或者是”;"等字符时,就需要把引号加上。
4、判断请求的文件是否存在使用"-f"和"!-f",
-f
:如果请求的文件存在返回true,不存在返回false。!f
:如果请求文件不存在,但该文件所在目录存在返回true,文件和目录都不存在返回false,如果文件存在返回false
1 |
|
5、判断请求的目录是否存在使用"-d"和"!-d",
6、判断请求的目录或者文件是否存在使用"-e"和"!-e"
7、判断请求的文件是否可执行使用"-x"和"!-x"
¶break指令
该指令用于中断当前相同作用域中的其他Nginx配置。与该指令处于同一作用域的Nginx配置中,位于它前面的指令配置生效,位于后面的指令配置无效。
语法 | break; |
---|---|
默认值 | — |
位置 | server、location、if |
例子:
1 |
|
解释:有了参数后走if,但是直接报错404,因为break会终止此次匹配,直接跳转到location的路径去重定向访问,导致跳转到/usr/local/nginx/html/testbreak目录,该目录下没有默认的index.html,因此报错404。
当我们创建了该目录和默认html文件后,如下:
301表示永久重定向,重定向到?1,状态码为200,响应头多了一个username为JERRY,即break后面的不会执行(同一作用域内)。
¶return指令
该指令用于完成对请求的处理,直接向客户端返回响应状态代码。在return后的所有Nginx配置都是无效的。
语法 | return code [text]; return code URL; return URL; |
---|---|
默认值 | — |
位置 | server、location、if |
- code:为返回给客户端的HTTP状态代理。可以返回的状态代码为0~999的任意HTTP状态代理
- text:为返回给客户端的响应体内容,支持变量的使用
- URL:为返回给客户端的URL地址。重定向,地址栏改变,状态码为302,临时重定向!
1 |
|
¶rewrite指令
该指令通过正则表达式的使用来改变URI。可以同时存在一个或者多个指令,按照顺序依次对URL进行匹配和处理。
-
URI:统一资源标识符
-
URL:统一资源定位符
语法 | rewrite regex replacement [flag]; |
---|---|
默认值 | — |
位置 | server、location、if |
regex:用来匹配URI的正则表达式
replacement:匹配成功后,用于替换URI中被截取内容的字符串。如果该字符串是以"http://"或者"https://"开头的,则不会继续向下对URI进行其他处理,而是直接返回重写后的URI给客户端。
flag:用来设置rewrite对URI的处理行为,可选值有如下:
- last:终止继续在本location块中处理接收到的URI,并将此处重写的URl作为一个新的URI,使用各location块进行处理。该标志将重写后的URI重写在server块中执行,为重写后的URI提供了转入到其他location块的机会。
- break:将此处重写的URI作为一个新的URl,在本块中继续进行处理。该标志将重写后的地址在当前的location块中执行,不会将新的URI转向其他的location块。
- redirect:将重写后的URI返回给客户端,状态码为302,指明是临时重定向URI,主要用在replacement变量不是以"http:/“或者"https:/”"开头的情况。
- permanent:将重写后的URI返回给客户端,状态码为301,指明是永久重定向URl,主要用在replacement变量不是以"http:/"“或者"https:/八”"开头的情况。
1 |
|
补充:301 302和搜索引擎seo优化有关!
¶rewrite_log指令
该指令配置是否开启URL重写日志的输出功能。
语法 | rewrite_log on|off; |
---|---|
默认值 | rewrite_log off; |
位置 | http、server、location、if |
开启后,URL重写的相关日志将以notice级别输出到error_log指令配置的日志文件汇总。
1 |
|
¶3、Rewrite的案例
¶域名跳转
- 准备三个域名:
1 |
|
1 |
|
1 |
|
- 通过Rewrite完成将 www.ithema.com 和 www.itheima.cn 的请求跳转到 www.itcast.com
1 |
|
问题描述:如何在域名跳转的过程中携带请求的URI?
修改配置信息:
1 |
|
¶域名镜像
镜像网站指定是将一个完全相同的网站分别放置到几台服务器上,并分别使用独立的URL进行访问。其中一台服务器上的网站叫主站,其他的为镜像网站。镜像网站和主站没有太大的区别,可以把镜像网站理解为主站的一个备份节点。可以通过镜像网站提供网站在不同地区的响应速度。镜像网站可以平衡网站的流量负载、可以解决网络宽带限制、封锁等。
为其中某一个子目录下的资源做镜像,我们可以在location块中配置rewrite功能:
1 |
|
¶独立域名
一个完整的项目包含多个模块,比如购物网站有商品搜索模块、商品详情模块和购物车模块等,那么我们如何为每一个模块设置独立的域名。
需求:
1 |
|
1 |
|
¶目录自动添加"/"
问题描述
通过一个例子来演示下问题:
1 |
|
通过 http://192.168.200.133:8082/heima
和通过 http://192.168.200.133:8082/heima/
访问的区别?
如果不加斜杠,Nginx服务器内部会自动做一个301的重定向,重定向的地址会有一个指令叫server_name_in_redirect on|off;来决定重定向的地址:
1 |
|
所以就拿刚才的地址来说,http://192.168.200.133:8082/heima 如果不加斜杠,那么按照上述规则,如果指令server_name_in_redirect为on,则301重定向地址变为 http://localhost:8082/heima/ , 如果为off,则301重定向地址变为 http://192.168.200.133:8082/heima/。后面这个是正常的,前面地址就有问题。
注意server_name_in_redirect指令在Nginx的0.8.48版本之前默认都是on,之后改成了off, 所以现在我们这个版本不需要考虑这个问题,但是如果是0.8.48以前的版本并且server_name_in_redirect设置为on,我们如何通过rewrite来解决这个问题?
旧版本解决方案:
我们可以使用rewrite功能为末尾没有斜杠的URL自动添加一个斜杠。
1 |
|
¶合并目录
搜索引擎优化(SEO)是一种利用搜索引擎的搜索规则来提高目的网站在有关搜索引擎内排名的方式。我们在创建自己的站点时,可以通过很多中方式来有效的提供搜索引擎优化的程度。其中有一项就包含URL的目录层级一般不要超过三层,否则的话不利于搜索引擎的搜索也给客户端的输入带来了负担,但是将所有的文件放在一个目录下又会导致文件资源管理混乱并且访问文件的速度也会随着文件增多而慢下来,这两个问题是相互矛盾的,那么使用rewrite如何解决上述问题?
举例:
网站中有一个资源文件的访问路径时 /server/11/22/33/44/20.html, 也就是说20.html存在于第5级目录下,如果想要访问该资源文件,客户端的URL地址就要写成 http://192.168.200.133/server/11/22/33/44/20.html
但是这个是非常不利于SEO搜索引擎优化的,同时客户端也不好记,使用rewrite我们可以进行如下配置:
1 |
|
客户端只需要输入http://www.web.name/server-11-22-33-44-20.html就可以访问到20.html页面了。
¶防盗链
防盗链之前我们已经介绍过了相关的知识,在rewrite中的防盗链和之前的原理其实都是一样的,只不过通过rewrite可以将防盗链的功能进行完善下,当出现防盗链的情况,我们可以使用rewrite将请求转发到自定义的一张图片和页面,给用户比较好的提示信息。
1 |
|
¶八、Nginx反向代理
¶1、Nginx正向代理
正向代理代理的对象是客户端,反向代理代理的是服务端,这是两者之间最大的区别。
Nginx即可以实现正向代理,也可以实现反向代理。
需求: 客服端为192.168.200.1,代理为192.168.200.146,服务端为192.168.133
1、服务端的设置
1 |
|
2、使用客户端访问服务端,打开日志查看结果
3、代理服务器设置
1 |
|
4、查看代理服务器的IP(192.168.200.146)和Nginx配置监听的端口(82)
5、在客户端配置代理服务器
6、设置完成后,再次通过浏览器访问服务端
通过对比,上下两次的日志记录,会发现虽然我们是客户端访问服务端,但是使用了代理,那么服务端能看到的只是代理发送过去的请求,这样的话,就使用Nginx实现了正向代理的设置。
但是Nginx正向代理,在实际的应用中不是特别多!
¶2、Nginx反向代理
Nginx反向代理模块的指令是由ngx_http_proxy_module
模块进行解析,默认已经内置!
¶proxy_pass
该指令用来设置被代理服务器地址,可以是主机名称、IP地址加端口号形式。
语法 | proxy_pass URL; |
---|---|
默认值 | — |
位置 | location |
URL:为要设置的被代理服务器地址,包含传输协议(http
,https://
)、主机名称或IP地址加端口号、URI等要素。
大家在编写proxy_pass的时候,后面的值要不要加"/"?
接下来通过例子来说明刚才我们提到的问题:
1 |
|
¶proxy_set_header
该指令可以更改Nginx服务器接收到的客户端请求的请求头信息,然后将新的请求头发送给代理的服务器
语法 | proxy_set_header field value; |
---|---|
默认值 | proxy_set_header Host $proxy_host; proxy_set_header Connection close; |
位置 | http、server、location |
需要注意的是,如果想要看到结果,必须在被代理的服务器上来获取添加的头信息。
被代理服务器: [192.168.200.146]
1 |
|
代理服务器: [192.168.200.133]
1 |
|
¶proxy_redirect
该指令是用来重置头信息中的"Location"和"Refresh"的值。
语法 | proxy_redirect redirect replacement; proxy_redirect default; proxy_redirect off; |
---|---|
默认值 | proxy_redirect default; |
位置 | http、server、location |
- redirect:目标,Location的值
- replacement:要替换的值
- proxy_redirect default:带有默认规则的替换,将location块的uri变量作为replacement,将proxy_pass变量作为redirect进行替换
- proxy_redirect off:关闭proxy_redirect的功能
为什么要用该指令?
服务端[192.168.200.146]
1 |
|
代理服务端[192.168.200.133]
1 |
|
问题发现:
访问 http://192.168.200.133:8081/abc.html 会先进入133的代理,再进入服务端146的if条件,302状态,地址栏变为146的ip!头信息的Location值也变为146服务端ip!
这时候服务端ip地址已经暴露!
解决:
1 |
|
又有问题:是跳转到了133代理服务器,但是获取的页面是133的默认80端口的值!即133的默认nginx欢迎页面!而不是想要得到的146服务端的nginx欢迎页面!
133代理服务器80端口修改一下即可:
1 |
|
¶3、Nginx反向代理实战
服务器1,2,3存在两种情况:
- 三台服务器的内容不一样。反向代理!
- 三台服务器的内容是一样。负载均衡!
配置:
1 |
|
¶4、Nginx的安全控制
关于web服务器的安全是比较大的一个话题,里面所涉及的内容很多,Nginx反向代理是如何来提升web服务器的安全呢?
安全隔离!
什么是安全隔离?
通过代理分开了客户端到应用程序服务器端的连接,实现了安全措施。在反向代理之前设置防火墙,仅留一个入口供代理服务器访问。
¶5、如何使用SSL对流量进行加密
就是将我们常用的http请求转变成https请求,那么这两个之间的区别简单的来说两个都是HTTP协议,只不过https是身披SSL外壳的http!
HTTPS是一种通过计算机网络进行安全通信的传输协议。它经由HTTP进行通信,利用SSL/TLS建立全通信,加密数据包,确保数据的安全性。
- SSL(Secure Sockets Layer)安全套接层
- TLS(Transport Layer Security)传输层安全
上述这两个是为网络通信提供安全及数据完整性的一种安全协议,TLS和SSL在传输层和应用层对网络连接进行加密。
总结来说为什么要使用https?
http协议是明文传输数据,存在安全问题,而https是加密传输,相当于http+ssl,并且可以防止流量劫持。
Nginx要想使用SSL,需要满足一个条件即需要添加一个模块--with-http_ssl_module
,而该模块在编译的过程中又需要OpenSSL的支持,这个我们之前已经准备好了。
¶nginx添加SSL的支持
1、完成 --with-http_ssl_module
模块的增量添加
1 |
|
¶Nginx的SSL相关指令
ssl:该指令用来在指定的服务器开启HTTPS,可以使用 listen 443 ssl,后面这种方式更通用些。
语法 | ssl on | off; |
---|---|
默认值 | ssl off; |
位置 | http、server |
1 |
|
ssl_certificate:为当前这个虚拟主机指定一个带有PEM格式证书的证书。
语法 | ssl_certificate file; |
---|---|
默认值 | — |
位置 | http、server |
ssl_certificate_key:该指令用来指定PEM secret key文件的路径
语法 | ssl_ceritificate_key file; |
---|---|
默认值 | — |
位置 | http、server |
ssl_session_cache:该指令用来配置用于SSL会话的缓存
语法 | ssl_sesion_cache off|none|[builtin[:size]] [shared:name:size] |
---|---|
默认值 | ssl_session_cache none; |
位置 | http、server |
- off:禁用会话缓存,客户端不得重复使用会话
- none:禁止使用会话缓存,客户端可以重复使用,但是并没有在缓存中存储会话参数
- builtin:内置OpenSSL缓存,仅在一个工作进程中使用。
- shared:所有工作进程之间共享缓存,缓存的相关信息用name和size来指定
ssl_session_timeout:开启SSL会话功能后,设置客户端能够反复使用储存在缓存中的会话参数时间。
语法 | ssl_session_timeout time; |
---|---|
默认值 | ssl_session_timeout 5m;(m:分钟) |
位置 | http、server |
ssl_ciphers:指出允许的密码,密码指定为OpenSSL支持的格式
语法 | ssl_ciphers ciphers; |
---|---|
默认值 | ssl_ciphers HIGH:!aNULL:!MD5; |
位置 | http、server |
可以使用openssl ciphers
查看openssl支持的格式。
ssl_prefer_server_ciphers:该指令指定是否服务器密码优先客户端密码
语法 | ssl_perfer_server_ciphers on|off; |
---|---|
默认值 | ssl_perfer_server_ciphers off; |
位置 | http、server |
¶生成证书
- 方式一:使用阿里云/腾讯云等第三方服务进行购买或使用freessl申请!(有机构验证)
- 方式二:使用openssl生成证书。(无机构验证)
1、先要确认当前系统是否有安装openssl
1 |
|
2、安装下面的命令进行生成
1 |
|
¶开启SSL实例
1 |
|
强制重定向到https:
1 |
|
¶6、反向代理系统调优
反向代理Buffer和Cache!
Buffer翻译过来是"缓冲",Cache翻译过来是"缓存"。
总结下:
相同点:两种方式都是用来提供IO吞吐效率,都是用来提升Nginx代理的性能。
不同点:
- 缓冲主要用来解决不同设备之间数据传递速度不一致导致的性能低的问题,缓冲中的数据一旦此次操作完成后,就可以删除。
- 缓存主要是备份,将被代理服务器的数据缓存一份到代理服务器,这样的话,客户端再次获取相同数据的时候,就只需要从代理服务器上获取,效率较高,缓存中的数据可以重复使用,只有满足特定条件才会删除.
Proxy Buffer相关指令:
proxy_buffering:该指令用来开启或者关闭代理服务器的缓冲区
语法 | proxy_buffering on|off; |
---|---|
默认值 | proxy_buffering on; |
位置 | http、server、location |
proxy_buffers:该指令用来指定单个连接从代理服务器读取响应的缓存区的个数和大小
语法 | proxy_buffers number size; |
---|---|
默认值 | proxy_buffers 8 4k | 8K;(与系统平台有关) |
位置 | http、server、location |
- number:缓冲区的个数
- size:每个缓冲区的大小,缓冲区的总大小就是number*size
proxy_buffer_size:该指令用来设置从被代理服务器获取的第一部分响应数据的大小。保持与proxy_buffers中的size一致即可,当然也可以更小。
语法 | proxy_buffer_size size; |
---|---|
默认值 | proxy_buffer_size 4k | 8k;(与系统平台有关) |
位置 | http、server、location |
proxy_busy_buffers_size:该指令用来限制同时处于BUSY状态的缓冲总大小。
语法 | proxy_busy_buffers_size size; |
---|---|
默认值 | proxy_busy_buffers_size 8k|16K; |
位置 | http、server、location |
proxy_temp_path:当缓冲区存满后,仍未被Nginx服务器完全接受,响应数据就会被临时存放在磁盘文件上,该指令设置文件路径
语法 | proxy_temp_path path; |
---|---|
默认值 | proxy_temp_path proxy_temp; |
位置 | http、server、location |
**注意:**path最多设置三层。
proxy_temp_file_write_size:该指令用来设置磁盘上缓冲文件的大小。
语法 | proxy_temp_file_write_size size; |
---|---|
默认值 | proxy_temp_file_write_size 8K|16K; |
位置 | http、server、location |
通用网站的配置:
1 |
|
根据项目的具体内容进行相应的调节。
¶九、Nginx负载均衡
¶1、负载均衡概述
早期的网站流量和业务功能都比较简单,单台服务器足以满足基本的需求,但是随着互联网的发展,业务流量越来越大并且业务逻辑也跟着越来越复杂,单台服务器的性能及单点故障问题就凸显出来了,因此需要多台服务器进行性能的水平扩展及避免单点故障出现。那么如何将不同用户的请求流量分发到不同的服务器上呢?
¶2、负载均衡的原理及处理流程
系统的扩展可以分为纵向扩展和横向扩展。
纵向扩展是从单机的角度出发,通过增加系统的硬件处理能力来提升服务器的处理能力
横向扩展是通过添加机器来满足大型网站服务的处理能力。
这里面涉及到两个重要的角色分别是"应用集群"和"负载均衡器":
-
应用集群:将同一应用部署到多台机器上,组成处理集群,接收负载均衡设备分发的请求,进行处理并返回响应的数据。
-
负载均衡器:将用户访问的请求根据对应的负载均衡算法,分发到集群中的一台服务器进行处理。
¶3、负载均衡的作用
- 解决服务器的高并发压力,提高应用程序的处理性能。
- 提供故障转移,实现高可用。
- 通过添加或减少服务器数量,增强网站的可扩展性。
- 在负载均衡器上进行过滤,可以提高系统的安全性。
¶3、负载均衡常用的处理方式
¶方式一:用户手动选择
这种方式比较原始,只要实现的方式就是在网站主页上面提供不同线路、不同服务器链接方式,让用户来选择自己访问的具体服务器,来实现负载均衡。
¶方式二:DNS轮询方式
大多域名注册商都支持对同一个主机名添加多条A记录,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,随机分配到不同的IP上,这样就能完成简单的负载均衡。DNS轮询的成本非常低,在一些不重要的服务器,被经常使用。
可以在云服务器控制台添加多条A记录解析!
简单验证:
1 |
|
清空本地的dns缓存再次测试ping:
1 |
|
我们发现使用DNS来实现轮询,不需要投入过多的成本,虽然DNS轮询成本低廉,但是DNS负载均衡存在明显的缺点:
- 可靠性低
假设一个域名DNS轮询多台服务器,如果其中的一台服务器发生故障,那么所有的访问该服务器的请求将不会有所回应,即使你将该服务器的IP从DNS中去掉,但是由于各大宽带接入商将众多的DNS存放在缓存中,以节省访问时间,导致DNS不会实时更新。所以DNS轮流上一定程度上解决了负载均衡问题,但是却存在可靠性不高的缺点。
- 负载均衡不均衡
DNS负载均衡采用的是简单的轮询负载算法,不能区分服务器的差异,不能反映服务器的当前运行状态,不能做到为性能好的服务器多分配请求,另外本地计算机也会缓存已经解析的域名到IP地址的映射,这也会导致使用该DNS服务器的用户在一定时间内访问的是同一台Web服务器,从而引发Web服务器减的负载不均衡。
负载不均衡则会导致某几台服务器负荷很低,而另外几台服务器负荷确很高,处理请求的速度慢,配置高的服务器分配到的请求少,而配置低的服务器分配到的请求多。
¶方式三:四/七层负载均衡
介绍四/七层负载均衡之前,我们先了解一个概念,OSI(open system interconnection), 叫开放式系统互联模型,这个是由国际标准化组织ISO指定的一个不基于具体机型、操作系统或公司的网络体系结构。该模型将网络通信的工作分为七层。
简单介绍,属于计网知识:
应用层:为应用程序提供网络服务。
表示层:对数据进行格式化、编码、加密、压缩等操作。
会话层:建立、维护、管理会话连接。
传输层:建立、维护、管理端到端的连接,常见的有TCP/UDP。
网络层:IP寻址和路由选择
数据链路层:控制网络层与物理层之间的通信。
物理层:比特流传输。
七层和四层指的是从下往上看的第七和第四层!
所谓四层负载均衡指的是OSI七层模型中的传输层,主要是基于IP+PORT的负载均衡:
1 |
|
所谓的七层负载均衡指的是在应用层,主要是基于虚拟的URL或主机IP的负载均衡:
1 |
|
四层和七层负载均衡的区别:
- 四层负载均衡数据包是在底层就进行了分发,而七层负载均衡数据包则在最顶端进行分发,所以四层负载均衡的效率比七层负载均衡的要高。
- 四层负载均衡不识别域名,而七层负载均衡识别域名。
了解:
除了四层和七层负载以为其实还有二层、三层负载均衡,二层是在数据链路层基于mac地址来实现负载均衡,三层是在网络层一般采用虚拟IP地址的方式实现负载均衡。
实际环境采用的模式:四层负载(LVS)+七层负载(Nginx)
¶4、Nginx七层负载均衡
Nginx要实现七层负载均衡需要用到proxy_pass代理模块配置。Nginx默认安装支持这个模块,我们不需要再做任何处理。Nginx的负载均衡是在Nginx的反向代理基础上把用户的请求根据指定的算法分发到一组 upstream虚拟服务池。
¶负载均衡指令
upstream指令:
该指令是用来定义一组服务器,它们可以是监听不同端口的服务器,并且也可以是同时监听TCP和Unix socket的服务器。服务器可以指定不同的权重,默认为1。
语法 | upstream name {…} |
---|---|
默认值 | — |
位置 | http |
server指令:
该指令用来指定后端服务器的名称和一些参数,可以使用域名、IP、端口或者unix socket
语法 | server name [paramerters] |
---|---|
默认值 | — |
位置 | upstream |
¶七层负载均衡的实现流程
服务端设置:
1 |
|
负载均衡器设置:
1 |
|
¶负载均衡状态
状态 | 概述 |
---|---|
down | 当前的server暂时不参与负载均衡 |
backup | 预留的备份服务器 |
max_fails | 允许请求失败的次数 |
fail_timeout | 经过max_fails失败后, 服务暂停时间 |
max_conns | 限制最大的接收连接数 |
down:将该服务器标记为永久不可用,那么该代理服务器将不参与负载均衡。
该状态一般会对需要停机维护的服务器进行设置。
注意:谷歌浏览器在这种情况下不会进行轮询,其他浏览器没问题!可以通过cmd中的curl 地址
进行测试!
1 |
|
backup:将该服务器标记为备份服务器,当主服务器不可用时,将用来传递请求。
此时需要将9003端口的访问禁止掉来模拟下唯一能对外提供访问的服务宕机以后,backup的备份服务器就要开始对外提供服务,此时为了测试验证,我们需要使用防火墙firewalld来进行拦截。
1 |
|
max_conns=number:用来设置代理服务器同时活动链接的最大数量,默认为0,表示不限制,使用该配置可以根据后端服务器处理请求的并发量来进行设置,防止后端服务器被压垮。
max_fails=number:设置允许请求代理服务器失败的次数,默认为1。
fail_timeout=time:设置经过max_fails失败后,服务暂停的时间,默认是10秒。
1 |
|
¶负载均衡策略
Nginx的upstream支持如下六种方式的分配算法,分别是:
算法名称 | 说明 |
---|---|
轮询 | 默认方式 |
weight | 权重方式 |
ip_hash | 依据ip分配方式 |
least_conn | 依据最少连接方式 |
url_hash | 依据URL分配方式 |
fair | 依据响应时间方式 |
¶轮询
是upstream模块负载均衡默认的策略。每个请求会按时间顺序逐个分配到不同的后端服务器。轮询不需要额外的配置。
1 |
|
¶weight加权轮询
weight=number:用来设置服务器的权重,默认为1,权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的,所有此策略比较适合服务器的硬件配置差别比较大的情况。
1 |
|
¶ip_hash
当对后端的多台动态应用服务器做负载均衡时,ip_hash指令能够将某个客户端IP的请求通过哈希算法定位到同一台后端服务器上。这样,当来自某一个IP的用户在后端Web服务器A上登录后,在访问该站点的其他URL,能保证其访问的还是后端web服务器A。
语法 | ip_hash; |
---|---|
默认值 | — |
位置 | upstream |
1 |
|
需要额外多说一点的是使用ip_hash指令无法保证后端服务器的负载均衡,可能导致有些后端服务器接收到的请求多,有些后端服务器接收的请求少,而且设置后端服务器权重等方法将不起作用。
¶least_conn
最少连接,把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。
1 |
|
此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况。
¶url_hash
按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。
1 |
|
访问如下地址:
1 |
|
¶fair
fair采用的不是内置负载均衡使用的轮换的均衡算法,而是可以根据页面大小、加载时间长短智能的进行负载均衡。那么如何使用第三方模块的fair负载均衡策略。
fair属于第三方模块实现的负载均衡。需要添加nginx-upstream-fair
,如何添加对应的模块:
1、下载nginx-upstream-fair模块
1 |
|
2、将下载的文件上传到服务器并进行解压缩
1 |
|
3、重命名资源
1 |
|
4、使用./configure命令将资源添加到Nginx模块中
1 |
|
5、编译
1 |
|
编译可能会出现如下错误,ngx_http_upstream_srv_conf_t结构中缺少default_port
解决方案:
在Nginx的源码中 src/http/ngx_http_upstream.h,找到ngx_http_upstream_srv_conf_s
,在模块中添加添加default_port属性
1 |
|
然后再进行make.
6、更新Nginx
1 |
|
7、编译测试使用Nginx
1 |
|
¶负载均衡案例
案例一:对所有请求实现一般轮询规则的负载均衡
1 |
|
案例二:对所有请求实现加权轮询规则的负载均衡
1 |
|
案例三:对特定资源实现负载均衡
1 |
|
案例四:对不同域名实现负载均衡
1 |
|
案例五:实现带有URL重写的负载均衡
1 |
|
¶5、Nginx四层负载均衡
Nginx在1.9之后,增加了一个stream模块,用来实现四层协议的转发、代理、负载均衡等。stream模块的用法跟http的用法类似,允许我们配置一组TCP或者UDP等协议的监听,然后通过proxy_pass来转发我们的请求,通过upstream添加多个后端服务,实现负载均衡。
四层协议负载均衡的实现,一般都会用到LVS、HAProxy、F5等,要么很贵要么配置很麻烦,而Nginx的配置相对来说更简单,更能快速完成工作。
¶添加stream模块的支持
Nginx默认是没有编译这个模块的,需要使用到stream模块,那么需要在编译的时候加上--with-stream
。
完成添加--with-stream
的实现步骤:
1 |
|
¶负载均衡的指令
stream指令:该指令提供在其中指定流服务器指令的配置文件上下文。和http指令同级。
语法 | stream { … } |
---|---|
默认值 | — |
位置 | main |
upstream指令:该指令和http的upstream指令是类似的。
¶四层负载均衡的案例
需求分析:
实现步骤:
准备Redis服务器:在一条服务器上准备三个Redis,端口分别是6379,6378
1 |
|
准备Tomcat服务器:
1 |
|
nginx.conf配置:
1 |
|
访问测试:
问题: http和stream中都访问同一端口,会进入哪个进行处理?
四层负载均衡在七层负载均衡前面,因此会进入stream访问!
¶十、Nginx缓存集成
¶1、缓存的概念
缓存就是数据交换的缓冲区(称作:Cache),当用户要获取数据的时候,会先从缓存中去查询获取数据,如果缓存中有就会直接返回给用户,如果缓存中没有,则会发请求从服务器重新查询数据,将数据返回给用户的同时将数据放入缓存,下次用户就会直接从缓存中获取数据。
缓存流程图示:
缓存的优点:
- 减少数据传输,节省网络流量,加快响应速度,提升用户体验;
- 减轻服务器压力;
- 提供服务端的高可用性;
缓存的缺点:
- 数据的不一致
- 增加成本
¶2、Nginx的web缓存服务
Nginx是从0.7.48版开始提供缓存功能。Nginx是基于Proxy Store来实现的,其原理是把URL及相关组合当做Key, 在使用MD5算法对Key进行哈希,得到硬盘上对应的哈希目录路径,从而将缓存内容保存在该目录中。它可以支持任意URL连接,同时也支持404/301/302这样的非200状态码。Nginx即可以支持对指定URL或者状态码设置过期时间,也可以使用purge命令来手动清除指定URL的缓存。
Nginx中的步骤图示:
¶3、Nginx缓存相关指令
Nginx的web缓存服务主要是使用ngx_http_proxy_module
模块相关指令集来完成,接下来我们把常用的指令来进行介绍下。
¶proxy_cache_path
该指定用于设置缓存文件的存放路径
语法 | proxy_cache_path path [levels=number] keys_zone=zone_name:zone_size [inactive=time][max_size=size]; |
---|---|
默认值 | — |
位置 | http |
path:缓存路径地址
1 |
|
levels: 指定该缓存空间对应的目录,最多可以设置3层,每层取值为1|2
1 |
|
keys_zone:用来为这个缓存区设置名称和指定大小
1 |
|
inactive:指定缓存的数据多次时间未被访问就将被删除
1 |
|
max_size:设置最大缓存空间,如果缓存空间存满,默认会覆盖缓存时间最长的资源,如:
1 |
|
配置实例:
1 |
|
¶proxy_cache
该指令用来开启或关闭代理缓存,如果是开启则自定使用哪个缓存区来进行缓存。
语法 | proxy_cache zone_name|off; |
---|---|
默认值 | proxy_cache off; |
位置 | http、server、location |
zone_name:指定使用缓存区的名称
¶proxy_cache_key
该指令用来设置web缓存的key值,Nginx会根据key值MD5哈希存缓存。
语法 | proxy_cache_key key; |
---|---|
默认值 | proxy_cache_key $scheme$proxy_host$request_uri; |
位置 | http、server、location |
¶proxy_cache_valid
该指令用来对不同返回状态码的URL设置不同的缓存时间
语法 | proxy_cache_valid [code …] time; |
---|---|
默认值 | — |
位置 | http、server、location |
1 |
|
¶proxy_cache_min_uses
该指令用来设置资源被访问多少次后被缓存
语法 | proxy_cache_min_uses number; |
---|---|
默认值 | proxy_cache_min_uses 1; |
位置 | http、server、location |
¶proxy_cache_methods
该指令用户设置缓存哪些HTTP方法
语法 | proxy_cache_methods GET|HEAD|POST; |
---|---|
默认值 | proxy_cache_methods GET HEAD; |
位置 | http、server、location |
默认缓存HTTP的GET和HEAD方法,不缓存POST方法。
¶4、Nginx缓存设置案例
需求分析:
应用服务器的环境准备:
- 在192.168.200.146服务器上的tomcat的webapps下面添加一个js目录,并在js目录中添加一个jquery.js文件
- 启动tomcat
- 访问测试
1 |
|
Nginx的环境准备:
1、完成Nginx反向代理配置:
1 |
|
2、添加缓存配置:
1 |
|
add_header nginx-cache “$upstream_cache_status”;
第一次为MISS表示没有命中nginx缓存,再次访问变为HIT表示命中缓存!
¶5、Nginx缓存的清除
¶方式一:删除对应的缓存目录
1 |
|
¶方式二:使用第三方扩展模块(推荐)
使用ngx_cache_purge模块,默认没有,需要安装!
1 |
|
在nginx配置文件中进行如下配置:
1 |
|
直接访问/purge/文件…即可自动删除!
proxy_cache_purge:若是key动态的,则也应该与缓存设置中的key一致,即 $scheme$proxy_host$request_uri
会发现并不能正常删除,可以在nginx.conf中添加log_format指定日志文件格式:
1 |
|
日志打印如下:两次访问路径不一致,因此不会删除!真实场景应该写为一致!
¶6、Nginx设置资源不缓存
前面咱们已经完成了Nginx作为web缓存服务器的使用。但是我们得思考一个问题就是不是所有的数据都适合进行缓存。比如说对于一些经常发生变化的数据。如果进行缓存的话,就很容易出现用户访问到的数据不是服务器真实的数据。所以对于这些资源我们在缓存的过程中就需要进行过滤,不进行缓存。
Nginx也提供了这块的功能设置,需要使用到如下两个指令
1、proxy_no_cache
该指令是用来定义不将数据进行缓存的条件。
语法 | proxy_no_cache string …; |
---|---|
默认值 | — |
位置 | http、server、location |
配置实例
1 |
|
2、proxy_cache_bypass
该指令是用来设置不从缓存中获取数据的条件。
语法 | proxy_cache_bypass string …; |
---|---|
默认值 | — |
位置 | http、server、location |
配置实例
1 |
|
解释:
上述两个指令都有一个指定的条件,这个条件可以是多个,并且多个条件中至少有一个不为空且不等于"0",则条件满足成立。上面给的配置实例是从官方网站获取的,里面使用到了三个变量,分别是$cookie_nocache、$arg_nocache、$arg_comment
$cookie_nocache
:指的是当前请求的cookie中键的名称为nocache对应的值$arg_nocache和$arg_comment
:指的是当前请求的参数中属性名为nocache和comment对应的属性值
案例演示如下:
1 |
|
访问:https://ip:8081?nocache=888&comment=777
日志文件就会出现999 888 777获取到的值!
¶案例实现
设置不缓存资源的配置方案
1 |
|
若使用proxy_cache_bypass,将proxy_no_cache注释,访问会发现 nginx-cache值已经变为了BYPASS表示配置生效!
官方推荐两个配置都开启,保证数据最新!
¶十一、Nginx服务器端集群搭建
¶1、Nginx实现动静分离
什么是动静分离?
-
动:后台应用程序的业务处理
-
静:网站的静态资源(html,javaScript,css,images等文件)
-
分离:将两者进行分开部署访问,提供用户进行访问。举例说明就是以后所有和静态资源相关的内容都交给Nginx来部署访问,非静态内容则交个类似于Tomcat的服务器来部署访问。
为什么要动静分离?
前面我们介绍过Nginx在处理静态资源的时候,效率是非常高的,而且Nginx的并发访问量也是名列前茅,而Tomcat则相对比较弱一些,所以把静态资源交给Nginx后,可以减轻Tomcat服务器的访问压力并提高静态资源的访问速度。
动静分离以后,降低了动态资源和静态资源的耦合度。如动态资源宕机了也不影响静态资源的展示。
如何实现动静分离?
实现动静分离的方式很多,比如静态资源可以部署到CDN、Nginx等服务器上,动态资源可以部署到Tomcat,weblogic或者websphere上。我们使用Nginx+Tomcat来实现动静分离。
需求分析:
动静分离实现步骤:
1、将demo.war包部署到tomcat中,把之前部署的内容删除掉
2、在Nginx所在服务器创建如下目录,并将对应的静态资源放入指定的位置
index.html:
1 |
|
3、配置Nginx的静态资源与动态资源的访问
1 |
|
4、启动测试,访问 http://192.168.200.133/
小结:
假如某个时间点,由于某个原因导致Tomcat后的服务器宕机了,我们再次访问Nginx,用户还是能看到页面,只是缺失了访问端口的展示,这就是前后端耦合度降低的效果,并且整个请求只和后的服务器交互了一次,js和images都直接从Nginx返回,提供了效率,降低了后的服务器的压力。
¶2、Nginx实现Tomcat集群搭建
一台服务器容易宕机,那就多搭建几台Tomcat服务器,这样的话就提升了后的服务器的可用性。这也就是我们常说的集群,搭建Tomcat的集群需要用到了Nginx的反向代理和负载均衡的知识,具体如何来实现?我们先来分析下原理:
环境准备:
1、准备3台tomcat,使用端口进行区分[实际环境应该是三台服务器],修改server.xml,将端口修改分别修改为8080,8180,8280。将shutdown端口也修改了不要一样!
2、启动tomcat并访问测试
1 |
|
3、在Nginx对应的配置文件中添加如下内容
1 |
|
¶3、Nginx高可用解决方案
需要两台以上的Nginx服务器对外提供服务,这样的话就可以解决其中一台宕机了,另外一台还能对外提供服务,但是如果是两台Nginx服务器的话,会有两个IP地址,用户该访问哪台服务器,用户怎么知道哪台是好的,哪台是宕机了的?
这就要用到 Keepalived 了!
¶Keepalived
使用Keepalived来解决,Keepalived 软件由 C 编写的,最初是专为 LVS 负载均衡软件设计的,Keepalived 软件主要是通过 VRRP 协议实现高可用功能。
¶VRRP介绍
VRRP(Virtual Route Redundancy Protocol)协议:翻译过来为虚拟路由冗余协议。VRRP协议将两台或多台路由器设备虚拟成一个设备,对外提供虚拟路由器IP,而在路由器组内部,如果实际拥有这个对外IP的路由器如果工作正常的话就是MASTER, MASTER实现针对虚拟路由器IP的各种网络功能。其他设备不拥有该虚拟IP,状态为BACKUP,处了接收MASTER的VRRP状态通告信息以外,不执行对外的网络功能。当主机失效时,BACKUP将接管原先MASTER的网络功能。
从上面的介绍信息获取到的内容就是VRRP是一种协议,那这个协议是用来干什么的?
- 选择协议:VRRP可以把一个虚拟路由器的责任动态分配到局域网上的 VRRP 路由器中的一台。其中的虚拟路由即Virtual路由是由VRRP路由群组创建的一个不真实存在的路由,这个虚拟路由也是有对应的IP地址。而且VRRP路由1和VRRP路由2之间会有竞争选择,通过选择会产生一个Master路由和一个Backup路由。
- 路由容错协议:Master路由和Backup路由之间会有一个心跳检测,Master会定时告知Backup自己的状态,如果在指定的时间内,Backup没有接收到这个通知内容,Backup就会替代Master成为新的Master。Master路由有一个特权就是虚拟路由和后端服务器都是通过Master进行数据传递交互的,而备份节点则会直接丢弃这些请求和数据,不做处理,只是去监听Master的状态
用了Keepalived后,解决方案如下:
¶环境搭建
环境准备:
VIP | IP | 主机名 | 主/从 |
---|---|---|---|
192.168.200.133 | keepalived1 | Master | |
192.168.200.222 | |||
192.168.200.122 | keepalived2 | Backup |
keepalived的安装:
1 |
|
/etc/keepalived/keepalived.conf
:keepalived的系统配置文件/usr/local/sbin/keepalived
:是系统配置脚本,用来启动和关闭keepalived
¶Keepalived配置文件介绍
这里面会分三部,第一部分是global全局配置、第二部分是vrrp相关配置、第三部分是LVS相关配置。
我们主要是使用keepalived实现高可用部署,没有用到LVS,所以我们重点关注的是前两部分!
配置介绍:
1 |
|
VRRP实例配置:
1 |
|
完整配置服务器1:
1 |
|
完整配置服务器2:
1 |
|
¶访问测试
1、启动keepalived之前,先使用命令 ip a
, 查看192.168.200.133和192.168.200.122这两台服务器的IP情况。
2、分别启动两台服务器的keepalived
1 |
|
再次通过 ip a
查看ip:
3、当把192.168.200.133服务器上的keepalived关闭后,再次查看ip
总结:
- 我们会发现,虚拟IP(VIP)会在MASTER节点上,当MASTER节点上的keepalived出问题以后,因为BACKUP无法收到MASTER发出的VRRP状态通过信息,就会直接升为MASTER。VIP也会"漂移"到新的MASTER。
- 我们把192.168.200.133服务器的keepalived再次启动下,由于它的优先级高于服务器192.168.200.122的,所有它会再次成为MASTER,VIP也会"漂移"过去,然后我们再次通过浏览器访问:
http://192.168.200.222/
- 我们会发现要想让vip进行切换,就必须要把服务器上的keepalived进行关闭
¶keepalived之vrrp_script
- keepalived只能做到对网络故障和keepalived本身的监控,即当出现网络故障或者keepalived本身出现问题时,进行切换。
- 但是这些还不够,我们还需要监控keepalived所在服务器上的其他业务,比如Nginx,如果Nginx出现异常了,仅仅keepalived保持正常,是无法完成系统的正常工作的,因此需要根据业务进程的运行状态决定是否需要进行主备切换,这个时候,我们可以通过编写脚本对业务进程进行检测监控。
实现步骤:
在keepalived配置文件中添加对应的配置:
1 |
|
编写脚本 ck_nginx.sh:
1 |
|
- ps命令:用于显示当前进程 (process) 的状态
- -C(command):指定命令的所有进程
- –no-header:排除标题
为脚本文件设置权限:
1 |
|
将脚本添加到配置文件:
1 |
|
如果效果没有出来,可以使用 tail -f /var/log/messages
查看日志信息,找对应的错误信息。
问题思考?
通常如果master服务死掉后backup会变成master,但是当master服务又好了的时候 master此时会抢占VIP,这样就会发生两次切换对业务繁忙的网站来说是不好的。可以设置vrrp_script中的weight来动态调整当前nginx的优先级!
¶十二、Nginx制作下载站点
首先我们先要清楚什么是下载站点?
我们先来看一个网站http://nginx.org/download/
这个我们刚开始学习Nginx的时候给大家看过这样的网站,该网站主要就是用来提供用户来下载相关资源的网站,就叫做下载网站。
如何制作一个下载站点?
nginx使用的是模块ngx_http_autoindex_module来实现的,该模块处理以斜杠(“/”)结尾的请求,并生成目录列表。
nginx编译的时候会自动加载该模块,但是该模块默认是关闭的,我们需要使用下来指令来完成对应的配置。
1、autoindex:启用或禁用目录列表输出
语法 | autoindex on|off; |
---|---|
默认值 | autoindex off; |
位置 | http、server、location |
2、autoindex_exact_size:对应HTLM格式,指定是否在目录列表展示文件的详细大小
- 默认为on,显示出文件的确切大小,单位是bytes。
- 改为off后,显示出文件的大概大小,单位是kB或者MB或者GB
语法 | autoindex_exact_size on|off; |
---|---|
默认值 | autoindex_exact_size on; |
位置 | http、server、location |
3、autoindex_format:设置目录列表的格式
语法 | autoindex_format html|xml|json|jsonp; |
---|---|
默认值 | autoindex_format html; |
位置 | http、server、location |
注意:
-
该指令在1.7.9及以后版本中出现
-
XML/JSON格式一般不用这两种方式!
4、autoindex_localtime:对应HTML格式,是否在目录列表上显示时间。
- 默认为off,显示的文件时间为GMT时间。
- 改为on后,显示的文件时间为文件的服务器时间
语法 | autoindex_localtime on | off; |
---|---|
默认值 | autoindex_localtime off; |
位置 | http、server、location |
配置方式如下:
1 |
|
¶十三、Nginx的用户认证模块
对应系统资源的访问,我们往往需要限制谁能访问,谁不能访问。这块就是我们通常所说的认证部分,认证需要做的就是根据用户输入的用户名和密码来判定用户是否为合法用户,如果是则放行访问,如果不是则拒绝访问。
Nginx对应用户认证这块是通过
ngx_http_auth_basic_module
模块来实现的,它允许通过使用"HTTP基本身份验证"协议验证用户名和密码来限制对资源的访问。默认情况下nginx是已经安装了该模块,如果不需要则使用--without-http_auth_basic_module
。
1、auth_basic:使用“ HTTP基本认证”协议启用用户名和密码的验证
语法 | auth_basic string|off; |
---|---|
默认值 | auth_basic off; |
位置 | http,server,location,limit_except |
开启后,服务端会返回401,指定的字符串会返回到客户端,给用户以提示信息,但是不同的浏览器对内容的展示不一致。
2、auth_basic_user_file:指定用户名和密码所在文件
语法 | auth_basic_user_file file; |
---|---|
默认值 | — |
位置 | http,server,location,limit_except |
指定文件路径,该文件中的用户名和密码的设置,密码需要进行加密。可以采用工具自动生成
nginx.conf 添加如下内容:
1 |
|
我们需要使用htpasswd
工具生成用户名和密码:
1 |
|
1 |
|
效果如下:
小结:
上述方式虽然能实现用户名和密码的验证,但是大家也看到了,所有的用户名和密码信息都记录在文件里面,如果用户量过大的话,这种方式就显得有点麻烦了,这时候我们就得通过后台业务代码来进行用户权限的校验了。
¶十四、Nginx的扩展模块
Nginx是可扩展的,可用于处理各种使用场景。本节中,我们将探讨使用Lua扩展Nginx的功能。
¶1、Lua
¶概念
Lua是一种轻量、小巧的脚本语言,用标准C语言编写并以源代码形式开发。设计的目的是为了嵌入到其他应用程序中,从而为应用程序提供灵活的扩展和定制功能。
¶特性
跟其他语言进行比较,Lua有其自身的特点:
- 轻量级:Lua用标准C语言编写并以源代码形式开发,编译后仅仅一百余千字节,可以很方便的嵌入到其他程序中。
- 可扩展:Lua提供非常丰富易于使用的扩展接口和机制,由宿主语言(通常是C或C++)提供功能,Lua可以使用它们,就像内置的功能一样。
- 支持面向过程编程和函数式编程
¶应用场景
Lua在不同的系统中得到大量应用,场景的应用场景如下:
游戏开发、独立应用脚本、web应用脚本、扩展和数据库插件、系统安全上。
¶Lua的安装
在linux上安装Lua非常简单,只需要下载源码包并在终端解压、编译即可使用。
Lua的官网地址为:https://www.lua.org
1 |
|
¶Lua的语法
Lua和C/C++语法非常相似,整体上比较清晰,简洁。条件语句、循环语句、函数调用都与C/C++基本一致。如果对C/C++不太熟悉的同学来说,也没关系,因为天下语言是一家,基本上理解起来都不会太困难。我们一点点来讲。
¶第一个Lua程序
-
Lua交互式编程模式:可以通过命令lua -i 或lua来启用
-
Lua脚本式编程模式:是将代码保存到一个以lua为扩展名的文件中并执行的方式
脚本式方式一:
我们需要一个文件名为 hello.lua,在文件中添加要执行的代码,然后通过命令 lua hello.lua
来执行,会在控制台输出对应的结果。
hello.lua
1 |
|
脚本式方式二:
将hello.lua做如下修改
1 |
|
第一行用来指定Lua解释器所在位置为 /usr/local/bin/lua
,加上#号标记解释器会忽略它。一般情况下#!
就是用来指定用哪个程序来运行本文件。但是hello.lua并不是一个可执行文件,需要通过chmod来设置可执行权限,最简单的方式为:
1 |
|
可以使用dofile函数指明lua脚本路径在交互式命令行中运行:
1 |
|
注意:在Lua语言中,连续语句之间的分隔符并不是必须的,也就是说后面不需要加分号,当然加上也不会报错
在Lua语言中,表达式之间的换行也起不到任何作用。如以下四个写法,其实都是等效的
1 |
|
不建议使用第四种方式,可读性太差。
¶Lua的注释
关于Lua的注释要分两种,第一种是单行注释,第二种是多行注释。
单行注释的语法为:
1 |
|
多行注释的语法为:
1 |
|
如果想取消多行注释,只需要在第一个–之前在加一个-即可
1 |
|
¶标识符
换句话说标识符就是我们的变量名,Lua定义变量名以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上0个或多个字母,下划线,数字(0到9)。这块建议大家最好不要使用下划线加大写字母的标识符,因为Lua的保留字也是这样定义的,容易发生冲突。注意Lua是区分大小写字母的。
¶关键字
下列是Lua的关键字,大家在定义常量、变量或其他用户自定义标识符都要避免使用以下这些关键字:
and | break | do | else |
---|---|---|---|
elseif | end | false | for |
function | if | in | local |
nil | not | or | repeat |
return | then | true | until |
while | goto |
一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。这个也是上面我们不建议这么定义标识符的原因。
¶运算符
Lua中支持的运算符有算术运算符、关系运算符、逻辑运算符、其他运算符。
算术运算符:
1 |
|
关系运算符:
1 |
|
逻辑运算符:
1 |
|
其他运算符:
1 |
|
例如:
1 |
|
¶全局变量&局部变量
- 在Lua语言中,全局变量无须声明即可使用。在默认情况下,变量总是认为是全局的,如果未提前赋值,默认为nil
- 要想声明一个局部变量,需要使用local来声明
¶Lua数据类型
Lua有8个数据类型:
1 |
|
可以使用type函数测试给定变量或者的类型:
1 |
|
- nil:nil是一种只有一个nil值的类型,它的作用可以用来与其他所有值进行区分,也可以当想要移除一个变量时,只需要将该变量名赋值为nil,垃圾回收就会会释放该变量所占用的内存。
- boolean:具有两个值,true和false。boolean类型一般被用来做条件判断的真与假。在Lua语言中,只会将false和nil视为假,其他的都视为真,特别是在条件检测中0和空字符串都会认为是真,这个和我们熟悉的大多数语言不太一样。
- number:在Lua5.3版本开始,Lua语言为数值格式提供了两种选择:integer(整型)和float(双精度浮点型)[和其他语言不太一样,float不代表单精度类型。
数值常量的表示方式:
1 |
|
不管是整型还是双精度浮点型,使用type()函数来取其类型,都会返回的是number:
1 |
|
所以它们之间是可以相互转换的,同时,具有相同算术值的整型值和浮点型值在Lua语言中是相等的。
- string:Lua语言中的字符串即可以表示单个字符,也可以表示一整本书籍。在Lua语言中,操作100K或者1M个字母组成的字符串的程序很常见。可以使用单引号或双引号来声明字符串。
如果声明的字符串比较长或者有多行,则可以使用如下方式进行声明
1 |
|
- table:是Lua语言中最主要和强大的数据结构。使用表, Lua 语言可以以一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结构。 Lua语言中的表本质上是一种辅助数组。这种数组比Java中的数组更加灵活,可以使用数值做索引,也可以使用字符串或其他任意类型的值作索引(除nil外)。
创建表的最简单方式:
1 |
|
创建数组:
1 |
|
要想获取数组中的值,我们可以通过如下内容来获取:
1 |
|
从上面的结果可以看出来,数组的下标默认是从1开始的。所以上述创建数组,也可以通过如下方式来创建。
1 |
|
上面我们说过了,表的索引既可以是数字,也可以是字符串等其他的内容,所以我们也可以将索引更改为字符串来创建
1 |
|
当然,如果想要获取这些数组中的值,可以使用下面的方式
1 |
|
当前table的灵活不进于此,还有更灵活的声明方式
1 |
|
如何获取上面的值?
1 |
|
- function:在 Lua语言中,函数( Function )是对语句和表达式进行抽象的主要方式。
定义函数的语法为:
1 |
|
函数被调用的时候,传入的参数个数与定义函数时使用的参数个数不一致的时候,Lua 语言会通过 抛弃多余参数和将不足的参数设为 nil 的方式来调整参数的个数:
1 |
|
可变长参数函数:
1 |
|
函数返回值可以有多个,这点和Java不太一样:
1 |
|
-
thread:翻译过来是线程的意思,在Lua中,thread用来表示执行的独立线路,用来执行协同程序。
-
userdata:是一种用户自定义数据,用于表示一种由应用程序或C/C++语言库所创建的类型。
¶Lua控制结构
Lua 语言提供了一组精简且常用的控制结构,包括用于条件执行的证 以及用于循环的 while、 repeat 和 for。 所有的控制结构语法上都有一个显式的终结符: end 用于终结 if、 for 及 while 结构, until 用于终结 repeat 结构。
¶if then elseif else
1 |
|
¶while循环
语法:
1 |
|
1 |
|
¶repeat循环
语法:
1 |
|
1 |
|
¶for循环
数值型for循环:
语法:
1 |
|
param的值从exp1变化到exp2之前的每次循环会执行 循环体,并在每次循环结束后将步长(step)exp3增加到param上。exp3可选,如果不设置默认为1。
1 |
|
泛型for循环:
泛型for循环通过一个迭代器函数来遍历所有值,类似于java中的foreach语句。
语法:
1 |
|
i是数组索引值,v是对应索引的数组元素值,ipairs是Lua提供的一个迭代器函数,用来迭代数组,x是要遍历的数组。
例如:
1 |
|
但是如果将arr的值进行修改为
1 |
|
其中的x为JACK就无法遍历出来,缺失了数据,如果解决呢?
我们可以将迭代器函数变成pairs:
1 |
|
上述实例就输出的结果为:
1 |
|
¶2、ngx_lua模块概念
淘宝开发的ngx_lua模块通过将lua解释器集成进Nginx,可以采用lua脚本实现业务逻辑,由于lua的紧凑、快速以及内建协程,所以在保证高并发服务能力的同时极大地降低了业务逻辑实现成本。
¶3、ngx_lua模块环境准备
¶方式一:lua-nginx-module
一般不使用这种方式,比较繁杂,推荐使用下方的方式二!
¶方式二:OpenRestry(推荐)
¶概述
前面我们提到过,OpenResty是由淘宝工程师开发的,所以其官方网站 http://openresty.org/ 我们读起来是非常的方便。OpenResty是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,所以我们使用起来会更加方便。
openresty已经内置了nginx,因此先将之前的nginx关掉!
¶安装
1 |
|
¶4、ngx_lua的使用
使用Lua编写Nginx脚本的基本构建块是指令。指令用于指定何时运行用户Lua代码以及如何使用结果。下图显示了执行指令的顺序。
先来解释下*的作用:
1 |
|
-
init_by_lua*:该指令在每次Nginx重新加载配置时执行,可以用来完成一些耗时模块的加载,或者初始化一些全局配置。
-
init_worker_by_lua*:该指令用于启动一些定时任务,如心跳检查、定时拉取服务器配置等。
-
set_by_lua*:该指令只要用来做变量赋值,这个指令一次只能返回一个值,并将结果赋值给Nginx中指定的变量。
-
rewrite_by_lua*:该指令用于执行内部URL重写或者外部重定向,典型的如伪静态化URL重写,本阶段在rewrite处理阶段的最后默认执行。
-
access_by_lua*:该指令用于访问控制。例如,如果只允许内网IP访问。
-
content_by_lua*:该指令是应用最多的指令,大部分任务是在这个阶段完成的,其他的过程往往为这个阶段准备数据,正式处理基本都在本阶段。
-
header_filter_by_lua*:该指令用于设置应答消息的头部信息。
-
body_filter_by_lua*:该指令是对响应数据进行过滤,如截断、替换。
-
log_by_lua*:该指令用于在log请求处理阶段,用Lua代码处理日志,但并不替换原有log处理。
-
balancer_by_lua*:该指令主要的作用是用来实现上游服务器的负载均衡器算法*
-
ssl_certificate_by_*:该指令作用在Nginx和下游服务开始一个SSL握手操作时将允许本配置项的Lua代码。
需求:
1 |
|
实现代码:
1 |
|
¶5、ngx_lua操作Redis
Redis在系统中经常作为数据缓存、内存数据库使用,在大型系统中扮演着非常重要的作用。
在Nginx核心系统中,Redis是常备组件。
Nginx支持3种方法访问Redis, 分别是HttpRedis模块、HttpRedis2Module、lua-resty-redis库。这三种方式中HttpRedis模块提供的指令少,功能单一,适合做简单缓存,HttpRedis2Module模块比HttpRedis模块操作更灵活,功能更强大。而Lua-resty-redis库是OpenResty提供的一个操作Redis的接口库,可根据自己的业务情况做一些逻辑处理,适合做复杂的业务逻辑。我们主要以Lua-resty-redis来学习。
¶lua-resty-redis环境准备
步骤一:准备一个Redis环境
1 |
|
步骤二:准备对应的API
1 |
|
步骤三:效果实现
1 |
|
步骤四:运行测试效果
¶6、ngx_lua操作Mysql
MySQL是一个使用广泛的关系型数据库。在ngx_lua中,MySQL有两种访问模式, 分别是
- 使用ngx_lua模块和lua-resty-mysql模块:这两个模块是安装OpenResty时默认安装的。
- 使用drizzle_nginx_module(HttpDrizzleModule)模块:需要单独安装,这个库现不在OpenResty中。
¶lua-resty-mysql
lua-resty-mysql是OpenResty开发的模块,使用灵活、功能强大,适合复杂的业务场景,同时支持存储过程的访问。
¶使用lua-resty-mysql实现数据库的查询
步骤一:准备MYSQL
1 |
|
创建一个数据库表及表中的数据。
1 |
|
步骤二:API学习
1 |
|
步骤三:效果实现
1 |
|
¶使用lua-cjson处理查询结果
通过上述的案例学习,read_result()得到的结果res都是table类型,要想在页面上展示,就必须知道table的具体数据结构才能进行遍历获取。处理起来比较麻烦,接下来我们介绍一种简单方式cjson,使用它就可以将table类型的数据转换成json字符串,把json字符串展示在页面上即可。具体如何使用?
步骤一:引入cjson
1 |
|
步骤二:调用cjson的encode方法进行类型转换
1 |
|
步骤三:使用
1 |
|
¶lua-resty-mysql实现数据库的增删改
优化send_query和read_result !
本方法是send_query和read_result组合的快捷方法。
语法:
1 |
|
有了该API,上面的代码我们就可以进行对应的优化,如下:
1 |
|
¶综合小案例
使用ngx_lua模块完成Redis缓存预热。
分析:
1、先得有一张表(users)
2、浏览器输入如下地址
1 |
|
3、从表中查询出符合条件的记录,此时获取的结果为table类型
4、使用cjson将table数据转换成json字符串
5、将查询的结果数据存入Redis中
1 |
|