Nginx详细教程
作者:mmseoamin日期:2023-12-21

Nginx简介

背景介绍

Nginx(“engine x”)一个具有高性能的【HTTP】和【反向代理】的【WEB服务器】,同时也是一个【POP3/SMTP/IMAP代理服务器】,是由伊戈尔·赛索耶夫(俄罗斯人)使用C语言编写的,Nginx的第一个版本是2004年10月4号发布的0.1.0版本。另外值得一提的是伊戈尔·赛索耶夫将Nginx的源码进行了开源,这也为Nginx的发展提供了良好的保障。

image.png

名词解释

  1. WEB服务器:

WEB服务器也叫网页服务器,英文名叫Web Server,主要功能是为用户提供网上信息浏览服务。

  1. HTTP:

HTTP是超文本传输协议的缩写,是用于从WEB服务器传输超文本到本地浏览器的传输协议,也是互联网上应用最为广泛的一种网络协议。HTTP是一个客户端和服务器端请求和应答的标准,客户端是终端用户,服务端是网站,通过使用Web浏览器、网络爬虫或者其他工具,客户端发起一个到服务器上指定端口的HTTP请求。

  1. POP3/SMTP/IMAP:

POP3(Post Offic Protocol 3)邮局协议的第三个版本,

SMTP(Simple Mail Transfer Protocol)简单邮件传输协议,

IMAP(Internet Mail Access Protocol)交互式邮件存取协议,

通过上述名词的解释,我们可以了解到Nginx也可以作为电子邮件代理服务器。

  1. 反向代理

正向代理

image.png

反向代理

image.png

常见服务器对比

在介绍这一节内容之前,我们先来认识一家公司叫Netcraft。

Netcraft公司于1994年底在英国成立,多年来一直致力于互联网市场以及在线安全方面的咨询服务,其中在国际上最具影响力的当属其针对网站服务器、SSL市场所做的客观严谨的分析研究,公司官网每月公布的调研数据(Web Server Survey)已成为当今人们了解全球网站数量以及服务器市场分额情况的主要参考依据,时常被诸如华尔街杂志,英国BBC,Slashdot等媒体报道或引用。

我们先来看一组数据,我们先打开Nginx的官方网站  http://nginx.org/,找到Netcraft公司公布的数据,对当前主流服务器产品进行介绍。

image.png

上面这张图展示了2019年全球主流Web服务器的市场情况,其中有Apache、Microsoft-IIS、google Servers、Nginx、Tomcat等,而我们在了解新事物的时候,往往习惯通过类比来帮助自己理解事物的概貌。所以下面我们把几种常见的服务器来给大家简单介绍下:

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的理由。

Nginx的优点

(1)速度更快、并发更高

单次请求或者高并发请求的环境下,Nginx都会比其他Web服务器响应的速度更快。一方面在正常情况下,单次请求会得到更快的响应,另一方面,在高峰期(如有数以万计的并发请求),Nginx比其他Web服务器更快的响应请求。Nginx之所以有这么高的并发处理能力和这么好的性能原因在于Nginx采用了多进程和I/O多路复用(epoll)的底层实现。

(2)配置简单,扩展性强

Nginx的设计极具扩展性,它本身就是由很多模块组成,这些模块的使用可以通过配置文件的配置来添加。这些模块有官方提供的也有第三方提供的模块,如果需要完全可以开发服务自己业务特性的定制模块。

(3)高可靠性

Nginx采用的是多进程模式运行,其中有一个master主进程和N多个worker进程,worker进程的数量我们可以手动设置,每个worker进程之间都是相互独立提供服务,并且master主进程可以在某一个worker进程出错时,快速去"拉起"新的worker进程提供服务。

(4)热部署

现在互联网项目都要求以7*24小时进行服务的提供,针对于这一要求,Nginx也提供了热部署功能,即可以在Nginx不停止的情况下,对Nginx进行文件升级、更新配置和更换日志文件等功能。

(5)成本低、BSD许可证

BSD是一个开源的许可证,世界上的开源许可证有很多,现在比较流行的有六种分别是GPL、BSD、MIT、Mozilla、Apache、LGPL。这六种的区别是什么,我们可以通过下面一张图来解释下:

image.png

Nginx本身是开源的,我们不仅可以免费的将Nginx应用在商业领域,而且还可以在项目中直接修改Nginx的源码来定制自己的特殊要求。这些点也都是Nginx为什么能吸引无数开发者继续为Nginx来贡献自己的智慧和青春。OpenRestry [Nginx+Lua]   Tengine[淘宝]

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常用的功能模块
        静态资源部署
        Rewrite地址重写
        	正则表达式
        反向代理
        负载均衡
        	轮询、加权轮询、ip_hash、url_hash、fair
        Web缓存
        环境部署
        	高可用的环境
        用户认证模块...
        

        Nginx的核心组成

        nginx二进制可执行文件
        nginx.conf配置文件
        error.log错误的日志记录
        access.log访问日志记录
        

        Nginx环境准备

        Nginx版本介绍

        Nginx的官方网站为: http://nginx.org

        打开源码可以看到如下的页面内容

        image.png

        Nginx的官方下载网站为http://nginx.org/en/download.html,当然你也可以之间在首页选中右边的download进入版本下载网页。在下载页面我们会看到如下内容:

        image.png

        获取Nginx源码

        http://nginx.org/download/

        打开上述网站,就可以查看到Nginx的所有版本,选中自己需要的版本进行下载。下载我们可以直接在windows上下载然后上传到服务器,也可以直接从服务器上下载,这个时候就需要准备一台服务器。

        image.png

        准备服务器系统

        环境准备

        VMware WorkStation
        Centos7
        MobaXterm
        	xsheel,SecureCRT
        网络
        

        (1)确认centos的内核

        准备一个内核为2.6及以上版本的操作系统,因为linux2.6及以上内核才支持epoll,而Nginx需要解决高并发压力问题是需要用到epoll,所以我们需要有这样的版本要求。

        我们可以使用uname -a命令来查询linux的内核版本。

        image.png

        (2)确保centos能联网

        ping www.baidu.com
        

        image.png

        (3)确认关闭防火墙

        这一项的要求仅针对于那些对linux系统的防火墙设置规则不太清楚的,建议大家把防火墙都关闭掉,因为我们此次课程主要的内容是对Nginx的学习,把防火墙关闭掉,可以省掉后续Nginx学习过程中遇到的诸多问题。

        关闭的方式有如下两种:

        systemctl stop firewalld      关闭运行的防火墙,系统重新启动后,防火墙将重新打开
        systemctl disable firewalld   永久关闭防火墙,,系统重新启动后,防火墙依然关闭
        systemctl status firewalld	 查看防火墙状态
        

        (4)确认停用selinux

        selinux(security-enhanced linux),美国安全局对于强制访问控制的实现,在linux2.6内核以后的版本中,selinux已经成功内核中的一部分。可以说selinux是linux史上最杰出的新安全子系统之一。虽然有了selinux,我们的系统会更安全,但是对于我们的学习Nginx的历程中,会多很多设置,所以这块建议大家将selinux进行关闭。

        image.png

        sestatus查看状态

        image.png

        如果查看不是disabled状态,我们可以通过修改配置文件来进行设置,修改SELINUX=disabled,然后重启下系统即可生效。

        vim /etc/selinux/config
        

        image.png

        Nginx安装方式介绍

        Nginx的安装方式有两种分别是:

        通过Nginx源码
        	通过Nginx源码简单安装 (1)
        	通过Nginx源码复杂安装 (3)
        通过yum安装 (2)
        

        如果通过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进行全部安装。

        方案一:Nginx的源码简单安装

        (1)进入官网查找需要下载版本的链接地址,然后使用wget命令进行下载

        wget http://nginx.org/download/nginx-1.16.1.tar.gz
        

        (2)建议大家将下载的资源进行包管理

        mkdir -p nginx/core
        mv nginx-1.16.1.tar.gz nginx/core
        

        (3)解压缩

        tar -xzf nginx-1.16.1.tar.gz
        

        (4)进入资源文件中,发现configure

        ./configure
        

        (5)编译

        make
        

        (6)安装

        make install
        
        方案二:yum安装

        使用源码进行简单安装,我们会发现安装的过程比较繁琐,需要提前准备GCC编译器、PCRE兼容正则表达式库、zlib压缩库、OpenSSL安全通信的软件库包,然后才能进行Nginx的安装。

        (1)安装yum-utils

        sudo yum  install -y yum-utils
        

        (2)添加yum源文件

        vim /etc/yum.repos.d/nginx.repo
        
        [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
        

        (3)查看是否安装成功

        yum list | grep nginx
        

        image.png

        (4)使用yum进行安装

        yun install -y nginx
        

        (5)查看nginx的安装位置

        whereis nginx
        

        image.png

        (6)启动测试

        源码简单安装和yum安装的差异:

        这里先介绍一个命令: ./nginx -V,通过该命令可以查看到所安装Nginx的版本及相关配置信息。

        简单安装

        image.png

        yum安装

        image.png

        解压Nginx目录

        执行tar -zxvf nginx-1.16.1.tar.gz对下载的资源进行解压缩,进入压缩后的目录,可以看到如下结构

        image.png

        内容解释:

        auto:存放的是编译相关的脚本

        CHANGES:版本变更记录

        CHANGES.ru:俄罗斯文的版本变更记录

        conf:nginx默认的配置文件

        configure:nginx软件的自动脚本程序,是一个比较重要的文件,作用如下:

        (1)检测环境及根据环境检测结果生成C代码
        (2)生成编译代码需要的Makefile文件
        

        contrib:存放的是几个特殊的脚本文件,其中README中对脚本有着详细的说明

        html:存放的是Nginx自带的两个html页面,访问Nginx的首页和错误页面

        LICENSE:许可证的相关描述文件

        man:nginx的man手册

        README:Nginx的阅读指南

        src:Nginx的源代码

        方案三:Nginx的源码复杂安装

        这种方式和简单的安装配置不同的地方在第一步,通过./configure来对编译参数进行设置,需要我们手动来指定。那么都有哪些参数可以进行设置,接下来我们进行一个详细的说明。

        PATH:是和路径相关的配置信息

        with:是启动模块,默认是关闭的

        without:是关闭模块,默认是开启的

        我们先来认识一些简单的路径配置已经通过这些配置来完成一个简单的编译:

        –prefix=PATH

        指向Nginx的安装目录,默认值为/usr/local/nginx
        

        –sbin-path=PATH

        指向(执行)程序文件(nginx)的路径,默认值为
        /sbin/nginx
        

        –modules-path=PATH

        指向Nginx动态模块安装目录,默认值为
        /modules
        

        –conf-path=PATH

        指向配置文件(nginx.conf)的路径,默认值为
        /conf/nginx.conf
        

        –error-log-path=PATH

        指向错误日志文件的路径,默认值为
        /logs/error.log
        

        –http-log-path=PATH

        指向访问日志文件的路径,默认值为
        /logs/access.log
        

        –pid-path=PATH

        指向Nginx启动后进行ID的文件路径,默认值为
        /logs/nginx.pid
        

        –lock-path=PATH

        指向Nginx锁文件的存放路径,默认值为
        /logs/nginx.lock
        

        要想使用可以通过如下命令

        ./configure --prefix=/usr/local/nginx \
        --sbin-path=/usr/local/nginx/sbin/nginx \
        --modules-path=/usr/local/nginx/modules \
        --conf-path=/usr/local/nginx/conf/nginx.conf \
        --error-log-path=/usr/local/nginx/logs/error.log \
        --http-log-path=/usr/local/nginx/logs/access.log \
        --pid-path=/usr/local/nginx/logs/nginx.pid \
        --lock-path=/usr/local/nginx/logs/nginx.lock
        

        在使用上述命令之前,需要将之前服务器已经安装的nginx进行卸载,卸载的步骤分为三步骤:

        步骤一:需要将nginx的进程关闭

        ./nginx -s stop
        

        步骤二:将安装的nginx进行删除

        rm -rf /usr/local/nginx
        

        步骤三:将安装包之前编译的环境清除掉

        make clean
        

        Nginx目录结构分析

        在使用Nginx之前,我们先对安装好的Nginx目录文件进行一个分析,在这块给大家介绍一个工具tree,通过tree我们可以很方面的去查看centos系统上的文件目录结构,当然,如果想使用tree工具,就得先通过yum install -y tree来进行安装,安装成功后,可以通过执行tree /usr/local/nginx(tree后面跟的是Nginx的安装目录),获取的结果如下:

        image.png

        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的启动和停止等相关的命令。
        

        Nginx服务器启停命令

        Nginx安装完成后,接下来我们要学习的是如何启动、重启和停止Nginx的服务。

        对于Nginx的启停在linux系统中也有很多种方式,我们本次课程介绍两种方式:

        1. Nginx服务的信号控制
        2. Nginx的命令行控制
        方式一:Nginx服务的信号控制
        Nginx中的master和worker进程?
        Nginx的工作方式?
        如何获取进程的PID?
        信号有哪些?
        如何通过信号控制Nginx的启停等相关操作?
        

        前面在提到Nginx的高性能,其实也和它的架构模式有关。Nginx默认采用的是多进程的方式来工作的,当将Nginx启动后,我们通过ps -ef | grep nginx命令可以查看到如下内容:

        image.png

        从上图中可以看到,Nginx后台进程中包含一个master进程和多个worker进程,master进程主要用来管理worker进程,包含接收外界的信息,并将接收到的信号发送给各个worker进程,监控worker进程的状态,当worker进程出现异常退出后,会自动重新启动新的worker进程。而worker进程则是专门用来处理用户请求的,各个worker进程之间是平等的并且相互独立,处理请求的机会也是一样的。nginx的进程模型,我们可以通过下图来说明下:

        image.png

        我们现在作为管理员,只需要通过给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"优雅"地关闭整个服务
        HUP重读配置文件并使用服务对新配置项生效
        USR1重新打开日志文件,可以用来进行日志切割
        USR2平滑升级到最新版的nginx
        WINCH所有子进程不在接收处理新连接,相当于给work进程发送QUIT指令

        调用命令为kill -signal PID

        signal:即为信号;PID即为获取到的master线程ID

        1. 发送TERM/INT信号给master进程,会将Nginx服务立即关闭。
        kill -TERM PID / kill -TERM `cat /usr/local/nginx/logs/nginx.pid`
        kill -INT PID / kill -INT `cat /usr/local/nginx/logs/nginx.pid`
        
        1. 发送QUIT信号给master进程,master进程会控制所有的work进程不再接收新的请求,等所有请求处理完后,在把进程都关闭掉。
        kill -QUIT PID / kill -TERM `cat /usr/local/nginx/logs/nginx.pid`
        
        1. 发送HUP信号给master进程,master进程会把控制旧的work进程不再接收新的请求,等处理完请求后将旧的work进程关闭掉,然后根据nginx的配置文件重新启动新的work进程
        kill -HUP PID / kill -TERM `cat /usr/local/nginx/logs/nginx.pid`
        
        1. 发送USR1信号给master进程,告诉Nginx重新开启日志文件
        kill -USR1 PID / kill -TERM `cat /usr/local/nginx/logs/nginx.pid`
        
        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进程,让其处理完请求后再进行关闭
        kill -USR2 PID / kill -USR2 `cat /usr/local/nginx/logs/nginx.pid`
        
        kill -QUIT PID / kill -QUIT `cat /usr/local/nginx/logs/nginx.pid.oldbin`
        

        image.png

        1. 发送WINCH信号给master进程,让master进程控制不让所有的work进程在接收新的请求了,请求处理完后关闭work进程。注意master进程不会被关闭掉
        kill -WINCH PID /kill -WINCH`cat /usr/local/nginx/logs/nginx.pid`
        
        方式二:Nginx的命令行控制

        此方式是通过Nginx安装目录下的sbin下的可执行文件nginx来进行Nginx状态的控制,我们可以通过nginx -h来查看都有哪些参数可以用:

        image.png

        -?和-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的配置文件路径,(默认为: conf/nginx.conf)

        -g:用来补充Nginx配置文件,向Nginx服务指定启动时应用全局的配置

        Nginx服务器版本升级和新增模块

        如果想对Nginx的版本进行更新,或者要应用一些新的模块,最简单的做法就是停止当前的Nginx服务,然后开启新的Nginx服务。但是这样会导致在一段时间内,用户是无法访问服务器。为了解决这个问题,我们就需要用到Nginx服务器提供的平滑升级功能。这个也是Nginx的一大特点,使用这种方式,就可以使Nginx在7*24小时不间断的提供服务了。接下来我们分析下需求:

        需求:Nginx的版本最开始使用的是Nginx-1.14.2,由于服务升级,需要将Nginx的版本升级到Nginx-1.16.1,要求Nginx不能中断提供服务。
        

        为了应对上述的需求,这里我们给大家提供两种解决方案:

        方案一:使用Nginx服务信号完成Nginx的升级

        方案二:使用Nginx安装目录的make命令完成升级

        环境准备

        (1)先准备两个版本的Nginx分别是 1.14.2和1.16.1

        (2)使用Nginx源码安装的方式将1.14.2版本安装成功并正确访问

        进入安装目录
        ./configure
        make && make install
        

        (3)将Nginx1.16.1进行参数配置和编译,不需要进行安装。

        进入安装目录
        ./configure
        make
        
        方案一:使用Nginx服务信号进行升级

        第一步:将1.14.2版本的sbin目录下的nginx进行备份

        cd /usr/local/nginx/sbin
        mv nginx nginxold
        

        第二步:将Nginx1.16.1安装目录编译后的objs目录下的nginx文件,拷贝到原来/usr/local/nginx/sbin目录下

        cd ~/nginx/core/nginx-1.16.1/objs
        cp nginx /usr/local/nginx/sbin
        

        第三步:发送信号USR2给Nginx的1.14.2版本对应的master进程

        第四步:发送信号QUIT给Nginx的1.14.2版本对应的master进程

        kill -QUIT `more /usr/local/logs/nginx.pid.oldbin`
        
        方案二:使用Nginx安装目录的make命令完成升级

        第一步:将1.14.2版本的sbin目录下的nginx进行备份

        cd /usr/local/nginx/sbin
        mv nginx nginxold
        

        第二步:将Nginx1.16.1安装目录编译后的objs目录下的nginx文件,拷贝到原来/usr/local/nginx/sbin目录下

        cd ~/nginx/core/nginx-1.16.1/objs
        cp nginx /usr/local/nginx/sbin
        

        第三步:进入到安装目录,执行make upgrade

        image.png

        第四步:查看是否更新成功

        ./nginx -v
        

        在整个过程中,其实Nginx是一直对外提供服务的。并且当Nginx的服务器启动成功后,我们是可以通过浏览器进行直接访问的,同时我们可以通过更改html目录下的页面来修改我们在页面上所看到的内容,那么问题来了,为什么我们要修改html目录下的文件,能不能多添加一些页面是Nginx的功能更加丰富,还有前面聊到Nginx的前端功能又是如何来实现的,这就需要我们对Nginx的核心配置文件进行一个详细的学习。

        Nginx核心配置文件结构

        从前面的内容学习中,我们知道Nginx的核心配置文件默认是放在/usr/local/nginx/conf/nginx.conf,这一节,我们就来学习下nginx.conf的内容和基本配置方法。

        读取Nginx自带的Nginx配置文件,我们将其中的注释部分【学习一个技术点就是在Nginx的配置文件中可以使用#来注释】删除掉后,就剩下下面内容:

        worker_processes  1;
        events {
            worker_connections  1024;
        }
        http {
            include       mime.types;
            default_type  application/octet-stream;
            sendfile        on;
            keepalive_timeout  65;
            server {
                listen       80;
                server_name  localhost;
                location / {
                    root   html;
                    index  index.html index.htm;
                }
                error_page   500 502 503 504  /50x.html;
                location = /50x.html {
                    root   html;
                }
            }
        }
        
        指令名	指令值;  #全局块,主要设置Nginx服务器整体运行的配置指令
         #events块,主要设置,Nginx服务器与用户的网络连接,这一部分对Nginx服务器的性能影响较大
        events {	 
            指令名	指令值;
        }
        #http块,是Nginx服务器配置中的重要部分,代理、缓存、日志记录、第三方模块配置...             
        http {		
            指令名	指令值;
            server { #server块,是Nginx配置和虚拟主机相关的内容
                指令名	指令值;
                location / { 
                #location块,基于Nginx服务器接收请求字符串与location后面的值进行匹配,对特定请求进行处理
                    指令名	指令值;
                }
            }
        	...
        }
        

        简单小结下:

        nginx.conf配置文件中默认有三大块:全局块、events块、http块

        http块中可以配置多个server块,每个server块又可以配置多个location块。

        全局块

        user指令

        (1)user:用于配置运行Nginx服务器的worker进程的用户和用户组。

        语法user user [group]
        默认值nobody
        位置全局块

        该属性也可以在编译的时候指定,语法如下./configure --user=user --group=group,如果两个地方都进行了设置,最终生效的是配置文件中的配置。

        该指令的使用步骤:

        (1)设置一个用户信息"www"

        user www;
        

        image.png

        (2) 创建一个用户

        useradd www
        

        (3)修改user属性

        user www
        

        (4)创建/root/html/index.html页面,添加如下内容

        
        
        
        Welcome to nginx!
        
        
        
        

        Welcome to nginx!

        If you see this page, the nginx web server is successfully installed and working. Further configuration is required.

        For online documentation and support please refer to nginx.org.
        Commercial support is available at nginx.com.

        Thank you for using nginx.

        I am WWW

        (5)修改nginx.conf

        location / {
        	root   /root/html;
        	index  index.html index.htm;
        }
        

        (5)测试启动访问

        页面会报403拒绝访问的错误
        

        (6)分析原因

        因为当前用户没有访问/root/html目录的权限
        

        (7)将文件创建到 /home/www/html/index.html,修改配置

        location / {
        	root   /home/www/html;
        	index  index.html index.htm;
        }
        

        (8)再次测试启动访问

        能正常访问。
        

        综上所述,使用user指令可以指定启动运行工作进程的用户及用户组,这样对于系统的权限访问控制的更加精细,也更加安全。

        work process指令

        master_process:用来指定是否开启工作进程。

        语法master_process on|off;
        默认值master_process on;
        位置全局块

        worker_processes:用于配置Nginx生成工作进程的数量,这个是Nginx服务器实现并发处理服务的关键所在。理论上来说workder process的值越大,可以支持的并发处理量也越多,但事实上这个值的设定是需要受到来自服务器自身的限制,建议将该值和服务器CPU的内核数保存一致。

        语法worker_processes     num/auto;
        默认值1
        位置全局块

        如果将worker_processes设置成2,则会看到如下内容:

        image.png

        其他指令

        daemon:设定Nginx是否以守护进程的方式启动。

        守护式进程是linux后台执行的一种服务进程,特点是独立于控制终端,不会随着终端关闭而停止。

        语法daemon on|off;
        默认值daemon on;
        位置全局块

        pid:用来配置Nginx当前master进程的进程号ID存储的文件路径。

        语法pid file;
        默认值默认为:/usr/local/nginx/logs/nginx.pid
        位置全局块

        该属性可以通过./configure --pid-path=PATH来指定

        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的性能。

        (5)include:用来引入其他配置文件,使Nginx的配置更加灵活

        语法include file;
        默认值
        位置any

        events块

        (1)accept_mutex:用来设置Nginx网络连接序列化

        语法accept_mutex on|off;
        默认值accept_mutex on;
        位置events

        这个配置主要可以用来解决常说的"惊群"问题。大致意思是在某一个时刻,客户端发来一个请求连接,Nginx后台是以多进程的工作模式,也就是说有多个worker进程会被同时唤醒,但是最终只会有一个进程可以获取到连接,如果每次唤醒的进程数目太多,就会影响Nginx的整体性能。如果将上述值设置为on(开启状态),将会对多个Nginx进程接收连接进行序列号,一个个来唤醒接收,就防止了多个进程对连接的争抢。

        image.png

        (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,添加如下配置

        events{
        	accept_mutex on;
        	multi_accept on;
        	worker_commections 1024;
        	use epoll;
        }
        

        启动测试

        ./nginx -t
        ./nginx -s reload
        

        http块

        定义MIME-Type

        我们都知道浏览器中可以显示的内容有HTML、XML、GIF等种类繁多的文件、媒体等资源,浏览器为了区分这些资源,就需要使用MIME Type。所以说MIME Type是网络资源的媒体类型。Nginx作为web服务器,也需要能够识别前端请求的资源类型。

        在Nginx的配置文件中,默认有两行配置

        include mime.types;
        default_type application/octet-stream;
        

        (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快速实现,这样就不用编写程序响应请求了,可以减少服务器资源占用并且响应性能非常快。

        如何实现:

        location /get_text {
        	#这里也可以设置成text/plain
            default_type text/html;
            return 200 "This is nginx's text";
        }
        location /get_json{
            default_type application/json;
            return 200 '{"name":"TOM","age":18}';
        }
        
        自定义服务日志

        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
        其他配置指令

        (1)sendfile:用来设置Nginx服务器是否使用sendfile()传输文件,该属性可以大大提高Nginx处理静态资源的性能

        语法sendfile on|off;
        默认值sendfile off;
        位置http、server、location

        (2)keepalive_timeout:用来设置长连接的超时时间。

        》为什么要使用keepalive?

        我们都知道HTTP是一种无状态协议,客户端向服务端发送一个TCP请求,服务端响应完毕后断开连接。
        如何客户端向服务端发送多个请求,每个请求都需要重新创建一次连接,效率相对来说比较多,使用keepalive模式,可以告诉服务器端在处理完一个请求后保持这个TCP连接的打开状态,若接收到来自这个客户端的其他请求,服务端就会利用这个未被关闭的连接,而不需要重新创建一个新连接,提升效率,但是这个连接也不能一直保持,这样的话,连接如果过多,也会是服务端的性能下降,这个时候就需要我们进行设置其的超时时间。
        
        语法keepalive_timeout time;
        默认值keepalive_timeout 75s;
        位置http、server、location

        (3)keepalive_requests:用来设置一个keep-alive连接使用的次数。

        语法keepalive_requests number;
        默认值keepalive_requests 100;
        位置http、server、location

        server块和location块

        server块和location块都是我们要重点讲解和学习的内容,因为我们后面会对Nginx的功能进行详细讲解,所以这块内容就放到静态资源部署的地方给大家详细说明。

        本节我们主要来认识下Nginx默认给的nginx.conf中的相关内容,以及server块与location块在使用的时候需要注意的一些内容。

        	server {
                listen       80;
                server_name  localhost;
                location / {
                    root   html;
                    index  index.html index.htm;
                }
               
                error_page   500 502 503 504 404  /50x.html;
                location = /50x.html {
                    root   html;
                }
            }
        

        Nginx进阶篇

        Nginx服务器基础配置实例

        前面我们已经对Nginx服务器默认配置文件的结构和涉及的基本指令做了详细的阐述。通过这些指令的合理配置,我们就可以让一台Nginx服务器正常工作,并且提供基本的web服务器功能。

        接下来我们将通过一个比较完整和最简单的基础配置实例,来巩固下前面所学习的指令及其配置。

        需求如下:

        (1)有如下访问:
        	http://192.168.200.133:8081/server1/location1
        		访问的是:index_sr1_location1.html
        	http://192.168.200.133:8081/server1/location2
        		访问的是:index_sr1_location2.html
        	http://192.168.200.133:8082/server2/location1
        		访问的是:index_sr2_location1.html
        	http://192.168.200.133:8082/server2/location2
        		访问的是:index_sr2_location2.html
        (2)如果访问的资源不存在,
        	返回自定义的404页面
        (3)将/server1和/server2的配置使用不同的配置文件分割
        	将文件放到/home/www/conf.d目录下,然后使用include进行合并
        (4)为/server1和/server2各自创建一个访问日志文件
        

        准备相关文件,目录如下:

        image.png

        配置的内容如下:

        ##全局块 begin##
        #配置允许运行Nginx工作进程的用户和用户组
        user www;
        #配置运行Nginx进程生成的worker进程数
        worker_processes 2;
        #配置Nginx服务器运行对错误日志存放的路径
        error_log logs/error.log;
        #配置Nginx服务器允许时记录Nginx的master进程的PID文件路径和名称
        pid logs/nginx.pid;
        #配置Nginx服务是否以守护进程方法启动
        #daemon on;
        ##全局块 end##
        ##events块 begin##
        events{
        	#设置Nginx网络连接序列化
        	accept_mutex on;
        	#设置Nginx的worker进程是否可以同时接收多个请求
        	multi_accept on;
        	#设置Nginx的worker进程最大的连接数
        	worker_connections 1024;
        	#设置Nginx使用的事件驱动模型
        	use epoll;
        }
        ##events块 end##
        ##http块 start##
        http{
        	#定义MIME-Type
        	include mime.types;
        	default_type application/octet-stream;
        	#配置允许使用sendfile方式运输
        	sendfile on;
        	#配置连接超时时间
        	keepalive_timeout 65;
        	#配置请求处理日志格式
        	log_format server1 '===>server1 access log';
        	log_format server2 '===>server2 access log';
        	##server块 开始##
        	include /home/www/conf.d/*.conf;
        	##server块 结束##
        }
        ##http块 end##
        

        server1.conf

        server{
        		#配置监听端口和主机名称
        		listen 8081;
        		server_name localhost;
        		#配置请求处理日志存放路径
        		access_log /home/www/myweb/server1/logs/access.log server1;
        		#配置错误页面
        		error_page 404 /404.html;
        		#配置处理/server1/location1请求的location
        		location /server1/location1{
        			root /home/www/myweb;
        			index index_sr1_location1.html;
        		}
        		#配置处理/server1/location2请求的location
        		location /server1/location2{
        			root /home/www/myweb;
        			index index_sr1_location2.html;
        		}
        		#配置错误页面转向
        		location = /404.html {
        			root /home/www/myweb;
        			index 404.html;
        		}
        }
        

        server2.conf

        server{
        		#配置监听端口和主机名称
        		listen 8082;
        		server_name localhost;
        		#配置请求处理日志存放路径
        		access_log /home/www/myweb/server2/logs/access.log server2;
        		#配置错误页面,对404.html做了定向配置
        		error_page 404 /404.html;
        		#配置处理/server1/location1请求的location
        		location /server2/location1{
        			root /home/www/myweb;
        			index index_sr2_location1.html;
        		}
        		#配置处理/server2/location2请求的location
        		location /server2/location2{
        			root /home/www/myweb;
        			index index_sr2_location2.html;
        		}
        		#配置错误页面转向
        		location = /404.html {
        			root /home/www/myweb;
        			index 404.html;
        		}
        	}
        

        访问测试:

        image.png

        image.png

        image.pngimage.png

        image.png

        Nginx服务操作的问题

        经过前面的操作,我们会发现,如果想要启动、关闭或重新加载nginx配置文件,都需要先进入到nginx的安装目录的sbin目录,然后使用nginx的二级制可执行文件来操作,相对来说操作比较繁琐,这块该如何优化?另外如果我们想把Nginx设置成随着服务器启动就自动完成启动操作,又该如何来实现?这就需要用到接下来我们要讲解的两个知识点:

        Nginx配置成系统服务
        Nginx命令配置到系统环境
        

        Nginx配置成系统服务

        把Nginx应用服务设置成为系统服务,方便对Nginx服务的启动和停止等相关操作,具体实现步骤:

        (1) 在/usr/lib/systemd/system目录下添加nginx.service,内容如下:

        vim /usr/lib/systemd/system/nginx.service
        
        [Unit]
        Description=nginx web service
        Documentation=http://nginx.org/en/docs/
        After=network.target
        [Service]
        Type=forking
        PIDFile=/usr/local/nginx/logs/nginx.pid
        ExecStartPre=/usr/local/nginx/sbin/nginx -t -c /usr/local/nginx/conf/nginx.conf
        ExecStart=/usr/local/nginx/sbin/nginx
        ExecReload=/usr/local/nginx/sbin/nginx -s reload
        ExecStop=/usr/local/nginx/sbin/nginx -s stop
        PrivateTmp=true
        [Install]
        WantedBy=default.target
        

        (2)添加完成后如果权限有问题需要进行权限设置

        chmod 755 /usr/lib/systemd/system/nginx.service
        

        (3)使用系统命令来操作Nginx服务

        启动: systemctl start nginx
        停止: systemctl stop nginx
        重启: systemctl restart nginx
        重新加载配置文件: systemctl reload nginx
        查看nginx状态: systemctl status nginx
        开机启动: systemctl enable nginx
        

        Nginx命令配置到系统环境

        前面我们介绍过Nginx安装目录下的二级制可执行文件nginx的很多命令,要想使用这些命令前提是需要进入sbin目录下才能使用,很不方便,如何去优化,我们可以将该二进制可执行文件加入到系统的环境变量,这样的话在任何目录都可以使用nginx对应的相关命令。具体实现步骤如下:

        演示可删除

        /usr/local/nginx/sbin/nginx -V
        cd /usr/local/nginx/sbin  nginx -V
        如何优化???
        

        (1)修改/etc/profile文件

        vim /etc/profile
        在最后一行添加
        export PATH=$PATH:/usr/local/nginx/sbin
        

        (2)使之立即生效

        source /etc/profile
        

        (3)执行nginx命令

        nginx -V
        

        Nginx静态资源部署

        Nginx静态资源概述

        上网去搜索访问资源对于我们来说并不陌生,通过浏览器发送一个HTTP请求实现从客户端发送请求到服务器端获取所需要内容后并把内容回显展示在页面的一个过程。这个时候,我们所请 求的内容就分为两种类型,一类是静态资源、一类是动态资源。

        静态资源即指在服务器端真实存在并且能直接拿来展示的一些文件,比如常见的html页面、css文件、js文件、图 片、视频等资源;

        动态资源即指在服务器端真实存在但是要想获取需要经过一定的业务逻辑处理,根据不同的条件展示在页面不同这 一部分内容,比如说报表数据展示、根据当前登录用户展示相关具体数据等资源;

        Nginx处理静态资源的内容,我们需要考虑下面这几个问题:

        (1)静态资源的配置指令
        (2)静态资源的配置优化
        (3)静态资源的压缩配置指令
        (4)静态资源的缓存处理
        (5)静态资源的访问控制,包括跨域问题和防盗链问题
        

        Nginx静态资源的配置指令

        listen指令

        listen:用来配置监听端口。

        | 语法 | listen address[:port] [default_server]…;

        listen port [default_server]…; |

        | — | — |

        | 默认值 | listen _:80 | _:8000 |

        | 位置 | server |

        listen的设置比较灵活,我们通过几个例子来把常用的设置方式熟悉下:

        listen 127.0.0.1:8000; // listen localhost:8000 监听指定的IP和端口
        listen 127.0.0.1;	监听指定IP的所有端口
        listen 8000;	监听指定端口上的连接
        listen *:8000;	监听指定端口上的连接
        

        default_server属性是标识符,用来将此虚拟主机设置成默认主机。所谓的默认主机指的是如果没有匹配到对应的address:port,则会默认执行的。如果不指定默认使用的是第一个server。

        server{
        	listen 8080;
        	server_name 127.0.0.1;
        	location /{
        		root html;
        		index index.html;
        	}
        }
        server{
        	listen 8080 default_server;
        	server_name localhost;
        	default_type text/plain;
        	return 444 'This is a error request';
        }
        
        server_name指令

        server_name:用来设置虚拟主机服务名称。

        127.0.0.1 、 localhost 、域名[www.baidu.com | www.jd.com]

        | 语法 | server_name  name …;

        name可以提供多个中间用空格分隔 |

        | — | — |

        | 默认值 | server_name  “”; |

        | 位置 | server |

        关于server_name的配置方式有三种,分别是:

        精确匹配
        通配符匹配
        正则表达式匹配
        

        配置方式一:精确匹配

        如:

        server {
        	listen 80;
        	server_name www.itcast.cn www.itheima.cn;
        	...
        }
        

        补充小知识点:

        hosts是一个没有扩展名的系统文件,可以用记事本等工具打开,其作用就是将一些常用的网址域名与其对应的IP地址建立一个关联“数据库”,当用户在浏览器中输入一个需要登录的网址时,系统会首先自动从hosts文件中寻找对应的IP地址,一旦找到,系统会立即打开对应网页,如果没有找到,则系统会再将网址提交DNS域名解析服务器进行IP地址的解析。
        

        windows:C:\Windows\System32\drivers\etc

        centos:/etc/hosts

        因为域名是要收取一定的费用,所以我们可以使用修改hosts文件来制作一些虚拟域名来使用。需要修改 /etc/hosts文件来添加

        vim /etc/hosts
        127.0.0.1 www.itcast.cn
        127.0.0.1 www.itheima.cn
        

        配置方式二:使用通配符配置

        server_name中支持通配符"*",但需要注意的是通配符不能出现在域名的中间,只能出现在首段或尾段,如:

        server {
        	listen 80;
        	server_name  *.itcast.cn	www.itheima.*;
        	# www.itcast.cn abc.itcast.cn www.itheima.cn www.itheima.com
        	...
        }
        

        下面的配置就会报错

        server {
        	listen 80;
        	server_name  www.*.cn www.itheima.c*
        	...
        }
        

        配置三:使用正则表达式配置

        server_name中可以使用正则表达式,并且使用~作为正则表达式字符串的开始标记。

        常见的正则表达式

        代码说明
        ^匹配搜索字符串开始位置
        $匹配搜索字符串结束位置
        .匹配除换行符\n之外的任何单个字符
        \转义字符,将下一个字符标记为特殊字符
        [xyz]字符集,与任意一个指定字符匹配
        [a-z]字符范围,匹配指定范围内的任何字符
        \w与以下任意字符匹配 A-Z a-z 0-9 和下划线,等效于[A-Za-z0-9_]
        \d数字字符匹配,等效于[0-9]
        {n}正好匹配n次
        {n,}至少匹配n次
        {n,m}匹配至少n次至多m次
        *零次或多次,等效于{0,}
        +一次或多次,等效于{1,}
        ?零次或一次,等效于{0,1}

        配置如下:

        server{
                listen 80;
                server_name ~^www\.(\w+)\.com$;
                default_type text/plain;
                return 200 $1  $2 ..;
        }
        注意 ~后面不能加空格,括号可以取值
        
        匹配执行顺序

        由于server_name指令支持通配符和正则表达式,因此在包含多个虚拟主机的配置文件中,可能会出现一个名称被多个虚拟主机的server_name匹配成功,当遇到这种情况,当前的请求交给谁来处理呢?

        server{
        	listen 80;
        	server_name ~^www\.\w+\.com$;
        	default_type text/plain;
        	return 200 'regex_success';
        }
        server{
        	listen 80;
        	server_name www.itheima.*;
        	default_type text/plain;
        	return 200 'wildcard_after_success';
        }
        server{
        	listen 80;
        	server_name *.itheima.com;
        	default_type text/plain;
        	return 200 'wildcard_before_success';
        }
        server{
        	listen 80;
        	server_name www.itheima.com;
        	default_type text/plain;
        	return 200 'exact_success';
        }
        server{
        	listen 80 default_server;
        	server_name _;
        	default_type text/plain;
        	return 444 'default_server not found server';
        }
        

        结论:

        exact_success
        wildcard_before_success
        wildcard_after_success
        regex_success
        default_server not found server!!
        
        No1:准确匹配server_name
        No2:通配符在开始时匹配server_name成功
        No3:通配符在结束时匹配server_name成功
        No4:正则表达式匹配server_name成功
        No5:被默认的default_server处理,如果没有指定默认找第一个server
        
        location指令
        server{
        	listen 80;
        	server_name localhost;
        	location / {
        	
        	}
        	location /abc{
        	
        	}
        	...
        }
        

        location:用来设置请求的URI

        语法location [  =  |   ~  |  ~*   |   ^~   |@ ] uri{…}
        默认值
        位置server,location

        uri变量是待匹配的请求字符串,可以不包含正则表达式,也可以包含正则表达式,那么nginx服务器在搜索匹配location的时候,是先使用不包含正则表达式进行匹配,找到一个匹配度最高的一个,然后在通过包含正则表达式的进行匹配,如果能匹配到直接访问,匹配不到,就使用刚才匹配度最高的那个location来处理请求。

        属性介绍:

        不带符号,要求必须以指定模式开始

        server {
        	listen 80;
        	server_name 127.0.0.1;
        	location /abc{
        		default_type text/plain;
        		return 200 "access success";
        	}
        }
        以下访问都是正确的
        http://192.168.200.133/abc
        http://192.168.200.133/abc?p1=TOM
        http://192.168.200.133/abc/
        http://192.168.200.133/abcdef
        

        = :  用于不包含正则表达式的uri前,必须与指定的模式精确匹配

        server {
        	listen 80;
        	server_name 127.0.0.1;
        	location =/abc{
        		default_type text/plain;
        		return 200 "access success";
        	}
        }
        可以匹配到
        http://192.168.200.133/abc
        http://192.168.200.133/abc?p1=TOM
        匹配不到
        http://192.168.200.133/abc/
        http://192.168.200.133/abcdef
        

        ~ : 用于表示当前uri中包含了正则表达式,并且区分大小写

        ~*:  用于表示当前uri中包含了正则表达式,并且不区分大小写

        换句话说,如果uri包含了正则表达式,需要用上述两个符合来标识

        server {
        	listen 80;
        	server_name 127.0.0.1;
        	location ~^/abc\w${
        		default_type text/plain;
        		return 200 "access success";
        	}
        }
        server {
        	listen 80;
        	server_name 127.0.0.1;
        	location ~*^/abc\w${
        		default_type text/plain;
        		return 200 "access success";
        	}
        }
        

        ^~: 用于不包含正则表达式的uri前,功能和不加符号的一致,唯一不同的是,如果模式匹配,那么就停止搜索其他模式了。

        server {
        	listen 80;
        	server_name 127.0.0.1;
        	location ^~/abc{
        		default_type text/plain;
        		return 200 "access success";
        	}
        }
        
        设置请求资源的目录root / alias

        root:设置请求的根目录

        语法root path;
        默认值root html;
        位置http、server、location

        path为Nginx服务器接收到请求以后查找资源的根目录路径。

        alias:用来更改location的URI

        语法alias path;
        默认值
        位置location

        path为修改后的根路径。

        以上两个指令都可以来指定访问资源的路径,那么这两者之间的区别是什么?

        举例说明:

        (1)在/usr/local/nginx/html目录下创建一个 images目录,并在目录下放入一张图片mv.png图片

        location /images {
        	root /usr/local/nginx/html;
        }
        

        访问图片的路径为:

        http://192.168.200.133/https://blog.csdn.net/github_36665118/article/details/images/mv.png
        

        (2)如果把root改为alias

        location /images {
        	alias /usr/local/nginx/html;
        }
        

        再次访问上述地址,页面会出现404的错误,查看错误日志会发现是因为地址不对,所以验证了:

        root的处理结果是: root路径+location路径
        /usr/local/nginx/html/https://blog.csdn.net/github_36665118/article/details/images/mv.png
        alias的处理结果是:使用alias路径替换location路径
        /usr/local/nginx/html/images
        

        需要在alias后面路径改为

        location /images {
        	alias /usr/local/nginx/html/images;
        }
        

        (3)如果location路径是以/结尾,则alias也必须是以/结尾,root没有要求

        将上述配置修改为

        location /images/ {
        	alias /usr/local/nginx/html/images;
        }
        

        访问就会出问题,查看错误日志还是路径不对,所以需要把alias后面加上 /

        小结:

        root的处理结果是: root路径+location路径
        alias的处理结果是:使用alias路径替换location路径
        alias是一个目录别名的定义,root则是最上层目录的含义。
        如果location路径是以/结尾,则alias也必须是以/结尾,root没有要求
        
        index指令

        index:设置网站的默认首页

        语法index file …;
        默认值index index.html;
        位置http、server、location

        index后面可以跟多个设置,如果访问的时候没有指定具体访问的资源,则会依次进行查找,找到第一个为止。

        举例说明:

        location / {
        	root /usr/local/nginx/html;
        	index index.html index.htm;
        }
        访问该location的时候,可以通过 http://ip:port/,地址后面如果不添加任何内容,则默认依次访问index.html和index.htm,找到第一个来进行返回
        
        error_page指令

        error_page:设置网站的错误页面

        语法error_page code … [=[response]] uri;
        默认值
        位置http、server、location…

        当出现对应的响应code后,如何来处理。

        举例说明:

        (1)可以指定具体跳转的地址

        server {
        	error_page 404 http://www.itcast.cn;
        }
        

        (2)可以指定重定向地址

        server{
        	error_page 404 /50x.html;
        	error_page 500 502 503 504 /50x.html;
        	location =/50x.html{
        		root html;
        	}
        }
        

        (3)使用location的@符合完成错误信息展示

        server{
        	error_page 404 @jump_to_error;
        	location @jump_to_error {
        		default_type text/plain;
        		return 404 'Not Found Page...';
        	}
        }
        

        可选项=[response]的作用是用来将相应代码更改为另外一个

        server{
        	error_page 404 =200 /50x.html;
        	location =/50x.html{
        		root html;
        	}
        }
        这样的话,当返回404找不到对应的资源的时候,在浏览器上可以看到,最终返回的状态码是200,这块需要注意下,编写error_page后面的内容,404后面需要加空格,200前面不能加空格
        

        静态资源优化配置语法

        Nginx对静态资源如何进行优化配置。这里从三个属性配置进行优化:

        sendfile on;
        tcp_nopush on;
        tcp_nodeplay on;
        

        (1)sendfile,用来开启高效的文件传输模式。

        语法sendfile on |off;
        默认值sendfile off;
        位置http、server、location…

        请求静态资源的过程:客户端通过网络接口向服务端发送请求,操作系统将这些客户端的请求传递给服务器端应用程序,服务器端应用程序会处理这些请求,请求处理完成以后,操作系统还需要将处理得到的结果通过网络适配器传递回去。

        如:

        server {
        	listen 80;
        	server_name localhost;
        	location / {
        		root html;
        		index index.html;
        	}
        }
        在html目录下有一个welcome.html页面,访问地址
        http://192.168.200.133/welcome.html
        

        image.png

        image.png

        (2)tcp_nopush:该指令必须在sendfile打开的状态下才会生效,主要是用来提升网络包的传输’效率’

        语法tcp_nopush on|off;
        默认值tcp_nopush off;
        位置http、server、location

        (3)tcp_nodelay:该指令必须在keep-alive连接开启的情况下才生效,来提高网络包传输的’实时性’

        语法tcp_nodelay on|off;
        默认值tcp_nodelay on;
        位置http、server、location

        image.png

        经过刚才的分析,“tcp_nopush"和”tcp_nodelay“看起来是"互斥的”,那么为什么要将这两个值都打开,这个大家需要知道的是在linux2.5.9以后的版本中两者是可以兼容的,三个指令都开启的好处是,sendfile可以开启高效的文件传输模式,tcp_nopush开启可以确保在发送到客户端之前数据包已经充分“填满”, 这大大减少了网络开销,并加快了文件发送的速度。 然后,当它到达最后一个可能因为没有“填满”而暂停的数据包时,Nginx会忽略tcp_nopush参数, 然后,tcp_nodelay强制套接字发送数据。由此可知,TCP_NOPUSH可以与TCP_NODELAY一起设置,它比单独配置TCP_NODELAY具有更强的性能。所以我们可以使用如下配置来优化Nginx静态资源的处理

        sendfile on;
        tcp_nopush on;
        tcp_nodelay on;
        

        Nginx静态资源压缩实战

        经过上述内容的优化,我们再次思考一个问题,假如在满足上述优化的前提下,我们传送一个1M的数据和一个10M的数据那个效率高?,答案显而易见,传输内容小,速度就会快。那么问题又来了,同样的内容,如果把大小降下来,我们脑袋里面要蹦出一个词就是"压缩",接下来,我们来学习Nginx的静态资源压缩模块。

        在Nginx的配置文件中可以通过配置gzip来对静态资源进行压缩,相关的指令可以配置在http块、server块和location块中,Nginx可以通过

        ngx_http_gzip_module模块
        ngx_http_gzip_static_module模块
        ngx_http_gunzip_module模块
        

        对这些指令进行解析和处理。

        接下来我们从以下内容进行学习

        (1)Gzip各模块支持的配置指令
        (2)Gzip压缩功能的配置
        (3)Gzip和sendfile的冲突解决
        (4)浏览器不支持Gzip的解决方案
        
        Gzip模块配置指令

        接下来所学习的指令都来自ngx_http_gzip_module模块,该模块会在nginx安装的时候内置到nginx的安装环境中,也就是说我们可以直接使用这些指令。

        1. gzip指令:该指令用于开启或者关闭gzip功能

          | 语法 | gzip on|off; |

          | — | — |

          | 默认值 | gzip off; |

          | 位置 | http、server、location… |

        注意只有该指令为打开状态,下面的指令才有效果

        http{
           gzip on;
        }
        
        1. gzip_types指令:该指令可以根据响应页的MIME类型选择性地开启Gzip压缩功能

          | 语法 | gzip_types mime-type …; |

          | — | — |

          | 默认值 | gzip_types text/html; |

          | 位置 | http、server、location |

        所选择的值可以从mime.types文件中进行查找,也可以使用"*"代表所有。

        http{
        	gzip_types application/javascript;
        }
        
        1. gzip_comp_level指令:该指令用于设置Gzip压缩程度,级别从1-9,1表示要是程度最低,要是效率最高,9刚好相反,压缩程度最高,但是效率最低最费时间。

          | 语法 | gzip_comp_level level; |

          | — | — |

          | 默认值 | gzip_comp_level 1; |

          | 位置 | http、server、location |

        http{
        	gzip_comp_level 6;
        }
        
        1. gzip_vary指令:该指令用于设置使用Gzip进行压缩发送是否携带“Vary:Accept-Encoding”头域的响应头部。主要是告诉接收方,所发送的数据经过了Gzip压缩处理

          | 语法 | gzip_vary on|off; |

          | — | — |

          | 默认值 | gzip_vary off; |

          | 位置 | http、server、location |

        image.png

        1. gzip_buffers指令:该指令用于处理请求压缩的缓冲区数量和大小。

          | 语法 | gzip_buffers number size; |

          | — | — |

          | 默认值 | gzip_buffers 32 4k|16 8k; |

          | 位置 | http、server、location |

        其中number:指定Nginx服务器向系统申请缓存空间个数,size指的是每个缓存空间的大小。主要实现的是申请number个每个大小为size的内存空间。这个值的设定一般会和服务器的操作系统有关,所以建议此项不设置,使用默认值即可。

        gzip_buffers 4 16K;	  #缓存空间大小
        
        1. gzip_disable指令:针对不同种类客户端发起的请求,可以选择性地开启和关闭Gzip功能。

          | 语法 | gzip_disable regex …; |

          | — | — |

          | 默认值 | — |

          | 位置 | http、server、location |

        regex:根据客户端的浏览器标志(user-agent)来设置,支持使用正则表达式。指定的浏览器标志不使用Gzip.该指令一般是用来排除一些明显不支持Gzip的浏览器。

        gzip_disable "MSIE [1-6]\.";
        
        1. gzip_http_version指令:针对不同的HTTP协议版本,可以选择性地开启和关闭Gzip功能。

          | 语法 | gzip_http_version 1.0|1.1; |

          | — | — |

          | 默认值 | gzip_http_version 1.1; |

          | 位置 | http、server、location |

        该指令是指定使用Gzip的HTTP最低版本,该指令一般采用默认值即可。

        1. gzip_min_length指令:该指令针对传输数据的大小,可以选择性地开启和关闭Gzip功能

          | 语法 | gzip_min_length length; |

          | — | — |

          | 默认值 | gzip_min_length 20; |

          | 位置 | http、server、location |

        nignx计量大小的单位:bytes[字节] / kb[千字节] / M[兆]
        例如: 1024 / 10k|K / 10m|M
        

        Gzip压缩功能对大数据的压缩效果明显,但是如果要压缩的数据比较小的化,可能出现越压缩数据量越大的情况,因此我们需要根据响应内容的大小来决定是否使用Gzip功能,响应页面的大小可以通过头信息中的Content-Length来获取。但是如何使用了Chunk编码动态压缩,该指令将被忽略。建议设置为1K或以上。

        1. 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压缩功能的实例配置
        gzip on;  			  #开启gzip功能
        gzip_types *;		  #压缩源文件类型,根据具体的访问资源类型设定
        gzip_comp_level 6;	  #gzip压缩级别
        gzip_min_length 1024; #进行压缩响应页面的最小长度,content-length
        gzip_buffers 4 16K;	  #缓存空间大小
        gzip_http_version 1.1; #指定压缩响应所需要的最低HTTP请求版本
        gzip_vary  on;		  #往头信息中添加压缩标识
        gzip_disable "MSIE [1-6]\."; #对IE6以下的版本都不进行压缩
        gzip_proxied  off; #nginx作为反向代理压缩服务端返回数据的条件
        

        这些配置在很多地方可能都会用到,所以我们可以将这些内容抽取到一个配置文件中,然后通过include指令把配置文件再次加载到nginx.conf配置文件中,方法使用。

        nginx_gzip.conf

        gzip on;
        gzip_types *;
        gzip_comp_level 6;
        gzip_min_length 1024;
        gzip_buffers 4 16K;
        gzip_http_version 1.1;
        gzip_vary  on;
        gzip_disable "MSIE [1-6]\.";
        gzip_proxied  off;
        

        nginx.conf

        include nginx_gzip.conf
        
        Gzip和sendfile共存问题

        前面在讲解sendfile的时候,提到过,开启sendfile以后,在读取磁盘上的静态资源文件的时候,可以减少拷贝的次数,可以不经过用户进程将静态文件通过网络设备发送出去,但是Gzip要想对资源压缩,是需要经过用户进程进行操作的。所以如何解决两个设置的共存问题。

        可以使用ngx_http_gzip_static_module模块的gzip_static指令来解决。

        gzip_static指令

        gzip_static: 检查与访问资源同名的.gz文件时,response中以gzip相关的header返回.gz文件的内容。

        语法gzip_static on | off | always;
        默认值gzip_static off;
        位置http、server、location

        添加上述命令后,会报一个错误,unknown directive "gzip_static"主要的原因是Nginx默认是没有添加ngx_http_gzip_static_module模块。如何来添加?

        添加模块到Nginx的实现步骤

        (1)查询当前Nginx的配置参数

        nginx -V
        

        (2)将nginx安装目录下sbin目录中的nginx二进制文件进行更名

        cd /usr/local/nginx/sbin
        mv nginx nginxold
        

        (3) 进入Nginx的安装目录

        cd /root/nginx/core/nginx-1.16.1
        

        (4)执行make clean清空之前编译的内容

        make clean
        

        (5)使用configure来配置参数

        ./configure --with-http_gzip_static_module
        

        (6)使用make命令进行编译

        make
        

        (7) 将objs目录下的nginx二进制执行文件移动到nginx安装目录下的sbin目录中

        mv objs/nginx /usr/local/nginx/sbin
        

        (8)执行更新命令

        make upgrade
        
        gzip_static测试使用

        (1)直接访问http://192.168.200.133/jquery.js

        image.png

        (2)使用gzip命令进行压缩

        cd /usr/local/nginx/html
        gzip jquery.js
        

        (3)再次访问http://192.168.200.133/jquery.js

        image.png

        静态资源的缓存处理

        什么是缓存
        缓存(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值

        image.png

        (1)用户首次通过浏览器发送请求到服务端获取数据,客户端是没有对应的缓存,所以需要发送request请求来获取数据;

        (2)服务端接收到请求后,获取服务端的数据及服务端缓存的允许后,返回200的成功状态码并且在响应头上附上对应资源以及缓存信息;

        (3)当用户再次访问相同资源的时候,客户端会在浏览器的缓存目录中查找是否存在响应的缓存文件

        (4)如果没有找到对应的缓存文件,则走(2)步

        (5)如果有缓存文件,接下来对缓存文件是否过期进行判断,过期的判断标准是(Expires),

        (6)如果没有过期,则直接从本地缓存中返回数据进行展示

        (7)如果Expires过期,接下来需要判断缓存文件是否发生过变化

        (8)判断的标准有两个,一个是ETag(Entity Tag),一个是Last-Modified

        (9)判断结果是未发生变化,则服务端返回304,直接从缓存文件中获取数据

        (10)如果判断是发生了变化,重新从服务端获取数据,并根据缓存协商(服务端所设置的是否需要进行缓存数据的设置)来进行数据缓存。

        浏览器缓存相关指令

        Nginx需要进行缓存相关设置,就需要用到如下的指令

        expires指令

        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;

        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:默认不缓存。

        add_header指令

        add_header指令是用来添加指定的响应头和响应值。

        语法add_header name value [always];
        默认值
        位置http、server、location…

        Cache-Control作为响应头信息,可以设置如下值:

        缓存响应指令:

        Cache-control: must-revalidate
        Cache-control: no-cache
        Cache-control: no-store
        Cache-control: no-transform
        Cache-control: public
        Cache-control: private
        Cache-control: proxy-revalidate
        Cache-Control: max-age=
        Cache-control: s-maxage=
        
        指令说明
        must-revalidate可缓存但必须再向源服务器进行确认
        no-cache缓存前必须确认其有效性
        no-store不缓存请求或响应的任何内容
        no-transform代理不可更改媒体类型
        public可向任意方提供响应的缓存
        private仅向特定用户返回响应
        proxy-revalidate要求中间缓存服务器对缓存的响应有效性再进行确认
        max-age=<秒>响应最大Age值
        s-maxage=<秒>公共缓存服务器响应的最大Age值

        max-age=[秒]:

        Nginx的跨域问题解决

        这块内容,我们主要从以下方面进行解决:

        什么情况下会出现跨域问题?
        实例演示跨域问题
        具体的解决方案是什么?
        
        同源策略

        浏览器的同源策略:是一种约定,是浏览器最核心也是最基本的安全功能,如果浏览器少了同源策略,则浏览器的正常功能可能都会受到影响。

        同源:  协议、域名(IP)、端口相同即为同源

        http://192.168.200.131/user/1
        https://192.168.200.131/user/1
        不
        http://192.168.200.131/user/1
        http://192.168.200.132/user/1
        不
        http://192.168.200.131/user/1
        http://192.168.200.131:8080/user/1
        不
        http://www.nginx.com/user/1
        http://www.nginx.org/user/1
        不
        http://192.168.200.131/user/1
        http://192.168.200.131:8080/user/1
        不
        http://www.nginx.org:80/user/1
        http://www.nginx.org/user/1
        满足
        
        跨域问题

        简单描述下:

        有两台服务器分别为A,B,如果从服务器A的页面发送异步请求到服务器B获取数据,如果服务器A和服务器B不满足同源策略,则就会出现跨域问题。
        
        跨域问题的案例演示

        出现跨域问题会有什么效果?,接下来通过一个需求来给大家演示下:

        image.png(1)nginx的html目录下新建一个a.html

        
          
                
                跨域问题演示
                
                
          
          
                
          
        
        

        (2)在nginx.conf配置如下内容

        server{
                listen  8080;
                server_name localhost;
                location /getUser{
                        default_type application/json;
                        return 200 '{"id":1,"name":"TOM","age":18}';
                }
        }
        server{
        	listen 	80;
        	server_name localhost;
        	location /{
        		root html;
        		index index.html;
        	}
        }
        

        (3)通过浏览器访问测试

        image.png

        解决方案

        使用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…,可以全部设置,也可以根据需要设置,多个用逗号分隔

        具体配置方式

        location /getUser{
            add_header Access-Control-Allow-Origin *;
            add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE;
            default_type application/json;
            return 200 '{"id":1,"name":"TOM","age":18}';
        }
        

        静态资源防盗链

        什么是资源盗链

        资源盗链指的是此内容不在自己服务器上,而是通过技术手段,绕过别人的限制将别人的内容放到自己页面上最终展示给用户。以此来盗取大网站的空间和流量。简而言之就是用别人的东西成就自己的网站。

        效果演示

        京东:https://img14.360buyimg.com/n7/jfs/t1/101062/37/2153/254169/5dcbd410E6d10ba22/4ddbd212be225fcd.jpg

        百度:https://pics7.baidu.com/feed/cf1b9d16fdfaaf516f7e2011a7cda1e8f11f7a1a.jpeg?token=551979a23a0995e5e5279b8fa1a48b34&s=BD385394D2E963072FD48543030030BB

        我们自己准备一个页面,在页面上引入这两个图片查看效果

        image.png

        从上面的效果,可以看出来,下面的图片地址添加了防止盗链的功能,京东这边我们可以直接使用其图片。

        Nginx防盗链的实现原理:

        了解防盗链的原理之前,我们得先学习一个HTTP的头信息Referer,当浏览器向web服务器发送请求的时候,一般都会带上Referer,来告诉浏览器该网页是从哪个页面链接过来的。

        image.png

        后台服务器可以根据获取到的这个Referer信息来判断是否为自己信任的网站地址,如果是则放行继续访问,如果不是则可以返回403(服务端拒绝访问)的状态信息。

        在本地模拟上述的服务器效果:

        image.png

        Nginx防盗链的具体实现:

        valid_referers:nginx会通就过查看referer自动和valid_referers后面的内容进行匹配,如果匹配到了就将 i n v a l i d r e f e r e r 变量置 0 ,如果没有匹配到,则将 invalid_referer变量置0,如果没有匹配到,则将 invalidr​eferer变量置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: 可以支持正则表达式和*的字符串。如果是正则表达式,需要以~开头表示,例如

        location ~*\.(png|jpg|gif){
                   valid_referers none blocked www.baidu.com 192.168.200.222 *.example.com example.*  www.example.org  ~\.google\.;
                   if ($invalid_referer){
                        return 403;
                   }
                   root /usr/local/nginx/html;
        }
        

        遇到的问题:图片有很多,该如何批量进行防盗链?

        针对目录进行防盗链

        配置如下:

        location /images {
                   valid_referers none blocked www.baidu.com 192.168.200.222 *.example.com example.*  www.example.org  ~\.google\.;
                   if ($invalid_referer){
                        return 403;
                   }
                   root /usr/local/nginx/html;
        }
        

        这样我们可以对一个目录下的所有资源进行翻到了操作。

        遇到的问题:Referer的限制比较粗,比如随意加一个Referer,上面的方式是无法进行限制的。那么这个问题改如何解决?

        此处我们需要用到Nginx的第三方模块ngx_http_accesskey_module,第三方模块如何实现盗链,如果在Nginx中使用第三方模块的功能,这些我们在后面的Nginx的模块篇再进行详细的讲解。

        Rewrite功能配置

        Rewrite是Nginx服务器提供的一个重要基本功能,是Web服务器产品中几乎必备的功能。主要的作用是用来实现URL的重写。

        注意:Nginx服务器的Rewrite功能的实现依赖于PCRE的支持,因此在编译安装Nginx服务器之前,需要安装PCRE库。Nginx使用的是ngx_http_rewrite_module模块来解析和处理Rewrite功能的相关配置。

        “地址重写"与"地址转发”

        重写和转发的区别:

        地址重写浏览器地址会发生变化而地址转发则不变
        一次地址重写会产生两次请求而一次地址转发只会产生一次请求
        地址重写到的页面必须是一个完整的路径而地址转发则不需要
        地址重写因为是两次请求所以request范围内属性不能传递给新页面而地址转发因为是一次请求所以可以传递值
        地址转发速度快于地址重写
        
        Rewrite规则
        set指令

        该指令用来设置一个新的变量。

        语法set $variable value;
        默认值
        位置server、location、if

        variable:变量的名称,该变量名称要用"$"作为变量的第一个字符,且不能与Nginx服务器预设的全局变量同名。

        value:变量的值,可以是字符串、其他变量或者变量的组合等。

        Rewrite常用全局变量
        变量说明
        $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。
        if ($param){
        	
        }
        
        2. 使用"="和"!="比较变量和字符串是否相等,满足条件为true,不满足为false
        
        if ($request_method = POST){
        	return 405;
        }
        

        注意:此处和Java不太一样的地方是字符串不需要添加引号。

        1. 使用正则表达式对变量进行匹配,匹配成功返回true,否则返回false。变量与正则表达式之间使用"“,”“,”!“,”!"来连接。

          "~"代表匹配正则表达式过程中区分大小写,

          "~"代表匹配正则表达式过程中不区分大小写

          "!“和”!"刚好和上面取相反值,如果匹配上返回false,匹配不上返回true

        if ($http_user_agent ~ MSIE){
        	#$http_user_agent的值中是否包含MSIE字符串,如果包含返回true
        }
        

        注意:正则表达式字符串一般不需要加引号,但是如果字符串中包含"}“或者是”;"等字符时,就需要把引号加上。

        1. 判断请求的文件是否存在使用"-f"和"!-f",

          当使用"-f"时,如果请求的文件存在返回true,不存在返回false。

          当使用"!f"时,如果请求文件不存在,但该文件所在目录存在返回true,文件和目录都不存在返回false,如果文件存在返回false

        if (-f $request_filename){
        	#判断请求的文件是否存在
        }
        if (!-f $request_filename){
        	#判断请求的文件是否不存在
        }
        
        1. 判断请求的目录是否存在使用"-d"和"!-d",

          当使用"-d"时,如果请求的目录存在,if返回true,如果目录不存在则返回false

          当使用"!-d"时,如果请求的目录不存在但该目录的上级目录存在则返回true,该目录和它上级目录都不存在则返回false,如果请求目录存在也返回false.

        2. 判断请求的目录或者文件是否存在使用"-e"和"!-e"

          当使用"-e",如果请求的目录或者文件存在时,if返回true,否则返回false.

          当使用"!-e",如果请求的文件和文件所在路径上的目录都不存在返回true,否则返回false

        3. 判断请求的文件是否可执行使用"-x"和"!-x"

          当使用"-x",如果请求的文件可执行,if返回true,否则返回false

          当使用"!-x",如果请求文件不可执行,返回true,否则返回false

        break指令

        该指令用于中断当前相同作用域中的其他Nginx配置。与该指令处于同一作用域的Nginx配置中,位于它前面的指令配置生效,位于后面的指令配置无效。

        语法break;
        默认值
        位置server、location、if

        例子:

        location /{
        	if ($param){
        		set $id $1;
        		break;
        		limit_rate 10k;
        	}
        }
        
        return指令

        该指令用于完成对请求的处理,直接向客户端返回响应状态代码。在return后的所有Nginx配置都是无效的。

        | 语法 | return code [text];

        return code URL;

        return URL; |

        | — | — |

        | 默认值 | — |

        | 位置 | server、location、if |

        code:为返回给客户端的HTTP状态代理。可以返回的状态代码为0~999的任意HTTP状态代理

        text:为返回给客户端的响应体内容,支持变量的使用

        URL:为返回给客户端的URL地址

        rewrite指令

        该指令通过正则表达式的使用来改变URI。可以同时存在一个或者多个指令,按照顺序依次对URL进行匹配和处理。

        URL和URI的区别:

        URI:统一资源标识符
        URL:统一资源定位符
        
        语法rewrite regex replacement [flag];
        默认值
        位置server、location、if

        regex:用来匹配URI的正则表达式

        replacement:匹配成功后,用于替换URI中被截取内容的字符串。如果该字符串是以"http://"或者"https://"开头的,则不会继续向下对URI进行其他处理,而是直接返回重写后的URI给客户端。

        flag:用来设置rewrite对URI的处理行为,可选值有如下:

        • last:
        • break
        • redirect
        • permanent
          rewrite_log指令

          该指令配置是否开启URL重写日志的输出功能。

          语法rewrite_log on|off;
          默认值rewrite_log off;
          位置http、server、location、if

          开启后,URL重写的相关日志将以notice级别输出到error_log指令配置的日志文件汇总。

          Rewrite的案例

          域名跳转

          》问题分析

          先来看一个效果,如果我们想访问京东网站,大家都知道我们可以输入www.jd.com,但是同样的我们也可以输入www.360buy.com同样也都能访问到京东网站。这个其实是因为京东刚开始的时候域名就是www.360buy.com,后面由于各种原因把自己的域名换成了www.jd.com, 虽然说域名变量,但是对于以前只记住了www.360buy.com的用户来说,我们如何把这部分用户也迁移到我们新域名的访问上来,针对于这个问题,我们就可以使用Nginx中Rewrite的域名跳转来解决。

          》环境准备

          • 准备两个域名  www.360buy.com | www.jd.com
            vim /etc/hosts
            
            192.168.200.133 www.360buy.com
            192.168.200.133 www.jd.com
            
            • 在/usr/local/nginx/html/hm目录下创建一个访问页面
              
              	
              	
              		

              欢迎来到我们的网站

              • 通过Nginx实现当访问www.访问到系统的首页
                server {
                	listen 80;
                	server_name www.hm.com;
                	location /{
                		root /usr/local/nginx/html/hm;
                		index index.html;
                	}
                }
                

                》通过Rewrite完成将www.360buy.com的请求跳转到www.jd.com

                server {
                	listen 80;
                	server_name www.360buy.com;
                	rewrite ^/ http://www.jd.com permanent;
                }
                

                问题描述:如何在域名跳转的过程中携带请求的URI?

                修改配置信息

                server {
                	listen 80;
                	server_name www.itheima.com;
                	rewrite ^(.*) http://www.hm.com$1 permanent;
                }
                

                问题描述:我们除了上述说的www.jd.com 、www.360buy.com其实还有我们也可以通过www.jingdong.com来访问,那么如何通过Rewrite来实现多个域名的跳转?

                添加域名

                vim /etc/hosts
                192.168.200.133 www.jingdong.com
                

                修改配置信息

                server{
                	listen 80;
                	server_name www.360buy.com www.jingdong.com;
                	rewrite ^(.*) http://www.jd.com$1 permanent;
                }
                
                域名镜像

                上述案例中,将www.360buy.com 和 www.jingdong.com都能跳转到www.jd.com,那么www.jd.com我们就可以把它起名叫主域名,其他两个就是我们所说的镜像域名,当然如果我们不想把整个网站做镜像,只想为其中某一个子目录下的资源做镜像,我们可以在location块中配置rewrite功能,比如:

                server {
                	listen 80;
                	server_name rewrite.myweb.com;
                	location ^~ /source1{
                		rewrite ^/resource1(.*) http://rewrite.myweb.com/web$1 last;
                	}
                	location ^~ /source2{
                		rewrite ^/resource2(.*) http://rewrite.myweb.com/web$1 last;
                	}
                }
                
                独立域名

                一个完整的项目包含多个模块,比如购物网站有商品商品搜索模块、商品详情模块已经购物车模块等,那么我们如何为每一个模块设置独立的域名。

                需求:

                http://search.hm.com  访问商品搜索模块
                http://item.hm.com	  访问商品详情模块
                http://cart.hm.com	  访问商品购物车模块
                
                server{
                	listen 80;
                	server_name search.hm.com;
                	rewrite ^(.*) http://www.hm.com/bbs$1 last;
                }
                server{
                	listen 81;
                	server_name item.hm.com;
                	rewrite ^(.*) http://www.hm.com/item$1 last;
                }
                server{
                	listen 82;
                	server_name cart.hm.com;
                	rewrite ^(.*) http://www.hm.com/cart$1 last;
                }
                
                目录自动添加"/"

                问题描述

                通过一个例子来演示下问题:

                server {
                	listen	80;
                	server_name localhost;
                	location / {
                		root html;
                		index index.html;
                	}
                }
                

                要想访问上述资源,很简单,只需要通过http://192.168.200.133直接就能访问,地址后面不需要加/,但是如果将上述的配置修改为如下内容:

                server {
                	listen	80;
                	server_name localhost;
                	location /hm {
                		root html;
                		index index.html;
                	}
                }
                

                这个时候,要想访问上述资源,按照上述的访问方式,我们可以通过http://192.168.200.133/hm/来访问,但是如果地址后面不加斜杠,页面就会出问题。如果不加斜杠,Nginx服务器内部会自动做一个301的重定向,重定向的地址会有一个指令叫server_name_in_redirect on|off;来决定重定向的地址:

                如果该指令为on
                	重定向的地址为:  http://server_name/目录名/;
                如果该指令为off
                	重定向的地址为:  http://原URL中的域名/目录名/;
                

                所以就拿刚才的地址来说,http://192.168.200.133/hm如果不加斜杠,那么按照上述规则,如果指令server_name_in_redirect为on,则301重定向地址变为 http://localhost/hm/,如果为off,则301重定向地址变为http://192.168.200.133/ht/。后面这个是正常的,前面地址就有问题。

                注意server_name_in_redirect指令在Nginx的0.8.48版本之前默认都是on,之后改成了off,所以现在我们这个版本不需要考虑这个问题,但是如果是0.8.48以前的版本并且server_name_in_redirect设置为on,我们如何通过rewrite来解决这个问题?

                解决方案

                我们可以使用rewrite功能为末尾没有斜杠的URL自动添加一个斜杠

                server {
                	listen	80;
                	server_name localhost;
                	server_name_in_redirect on;
                	location /hm {
                		if (-d $request_filename){
                			rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
                		}
                	}
                }
                
                合并目录

                搜索引擎优化(SEO)是一种利用搜索引擎的搜索规则来提供目的网站的有关搜索引擎内排名的方式。我们在创建自己的站点时,可以通过很多中方式来有效的提供搜索引擎优化的程度。其中有一项就包含URL的目录层级一般不要超过三层,否则的话不利于搜索引擎的搜索也给客户端的输入带来了负担,但是将所有的文件放在一个目录下又会导致文件资源管理混乱并且访问文件的速度也会随着文件增多而慢下来,这两个问题是相互矛盾的,那么使用rewrite如何解决上述问题?

                举例,网站中有一个资源文件的访问路径时 /server/11/22/33/44/20.html,也就是说20.html存在于第5级目录下,如果想要访问该资源文件,客户端的URL地址就要写成 http://www.web.name/server/11/22/33/44/20.html,

                server {
                	listen 80;
                	server_name www.web.name;
                	location /server{
                		root html;
                	}
                }
                

                但是这个是非常不利于SEO搜索引擎优化的,同时客户端也不好记.使用rewrite我们可以进行如下配置:

                server {
                	listen 80;
                	server_name www.web.name;
                	location /server{
                		rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4/$5.html last;
                	}
                }
                

                这样的花,客户端只需要输入http://www.web.name/server-11-22-33-44-20.html就可以访问到20.html页面了。这里也充分利用了rewrite指令支持正则表达式的特性。

                防盗链

                防盗链之前我们已经介绍过了相关的知识,在rewrite中的防盗链和之前将的原理其实都是一样的,只不过通过rewrite可以将防盗链的功能进行完善下,当出现防盗链的情况,我们可以使用rewrite将请求转发到自定义的一张图片和页面,给用户比较好的提示信息。下面我们就通过根据文件类型实现防盗链的一个配置实例:

                server{
                	listen 80;
                	server_name www.web.com;
                	locatin ~* ^.+\.(gif|jpg|png|swf|flv|rar|zip)${
                		valid_referers none blocked server_names *.web.com;
                		if ($invalid_referer){
                			rewrite ^/ http://www.web.com/images/forbidden.png;
                		}
                	}
                }
                

                根据目录实现防盗链配置:

                server{
                	listen 80;
                	server_name www.web.com;
                	location /file/{
                		root /server/file/;
                		valid_referers none blocked server_names *.web.com;
                		if ($invalid_referer){
                			rewrite ^/ http://www.web.com/images/forbidden.png;
                		}
                	}
                }
                

                Rewrite功能配置

                Rewrite是Nginx服务器提供的一个重要基本功能,是Web服务器产品中几乎必备的功能。主要的作用是用来实现URL的重写。www.jd.com

                注意:Nginx服务器的Rewrite功能的实现依赖于PCRE的支持,因此在编译安装Nginx服务器之前,需要安装PCRE库。Nginx使用的是ngx_http_rewrite_module模块来解析和处理Rewrite功能的相关配置。

                Rewrite的相关命令

                set指令
                if指令
                break指令
                return指令
                rewrite指令
                rewrite_log指令
                

                Rewrite的应用场景

                域名跳转
                域名镜像
                独立域名
                目录自动添加"/"
                合并目录
                防盗链的实现
                

                Rewrite的相关指令

                set指令

                该指令用来设置一个新的变量。

                语法set $variable value;
                默认值
                位置server、location、if

                variable:变量的名称,该变量名称要用"$"作为变量的第一个字符,且不要与Nginx服务器预设的全局变量同名。

                value:变量的值,可以是字符串、其他变量或者变量的组合等。

                Rewrite常用全局变量
                变量说明
                $args变量中存放了请求URL中的请求参数。比如http://192.168.200.133/server?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"

                上述参数还可以在日志文件中使用,这个就要用到前面我们介绍的log_format指令

                log_format main '$remote_addr - $request - $status-$request_uri  $http_user_agent';
                access_log logs/access.log main;
                
                if指令

                该指令用来支持条件判断,并根据条件判断结果选择不同的Nginx配置。

                语法if  (condition){…}
                默认值
                位置server、location

                condition为判定条件,可以支持以下写法:

                1. 变量名。如果变量名对应的值为空字符串或"0",if都判断为false,其他条件为true。
                if ($param){
                	
                }
                
                1. 使用"=“和”!="比较变量和字符串是否相等,满足条件为true,不满足为false
                if ($request_method = POST){
                	return 405;
                }
                

                注意:此处和Java不太一样的地方是字符串不需要添加引号,并且等号和不等号前后到需要加空格。

                1. 使用正则表达式对变量进行匹配,匹配成功返回true,否则返回false。变量与正则表达式之间使用"“,”“,”!“,”!"来连接。

                  "~"代表匹配正则表达式过程中区分大小写,

                  "~"代表匹配正则表达式过程中不区分大小写

                  "!“和”!"刚好和上面取相反值,如果匹配上返回false,匹配不上返回true

                if ($http_user_agent ~ MSIE){
                	#$http_user_agent的值中是否包含MSIE字符串,如果包含返回true
                }
                

                注意:正则表达式字符串一般不需要加引号,但是如果字符串中包含"}“或者是”;"等字符时,就需要把引号加上。

                1. 判断请求的文件是否存在使用"-f"和"!-f",
                if (-f $request_filename){
                	#判断请求的文件是否存在
                }
                if (!-f $request_filename){
                	#判断请求的文件是否不存在
                }
                
                1. 判断请求的目录是否存在使用"-d"和"!-d"
                2. 判断请求的目录或者文件是否存在使用"-e"和"!-e"
                3. 判断请求的文件是否可执行使用"-x"和"!-x"
                break指令

                该指令用于中断当前相同作用域中的其他Nginx配置。与该指令处于同一作用域的Nginx配置中,位于它前面的指令配置生效,位于后面的指令配置无效。并且break还有另外一个功能就是终止当前的匹配并把当前的URI在本location进行重定向访问处理。

                语法break;
                默认值
                位置server、location、if

                例子:

                location /testbreak{
                	default_type text/plain;
                	set $username TOM;
                	if ($args){
                		Set $username JERRY;
                        break;
                		set $username ROSE;
                	}
                	add_header username $username;
                	return 200 $username;
                }
                
                return指令

                该指令用于完成对请求的处理,直接向客户端返回。在return后的所有Nginx配置都是无效的。

                | 语法 | return code [text];

                return code URL;

                return URL; |

                | — | — |

                | 默认值 | — |

                | 位置 | server、location、if |

                code:为返回给客户端的HTTP状态代理。可以返回的状态代码为0~999的任意HTTP状态代理

                text:为返回给客户端的响应体内容,支持变量的使用

                URL:为返回给客户端的URL地址

                location /testreturn {
                	return 200 success;
                }
                location /testreturn {
                	return https://www.baidu.com; // 302重定向到百度
                }
                location /testreturn {
                	return 302 https://www.baidu.com;
                }
                location /testreturn {
                	return 302 www.baidu.com;//不允许这么写
                }
                
                rewrite指令

                该指令通过正则表达式的使用来改变URI。可以同时存在一个或者多个指令,按照顺序依次对URL进行匹配和处理。

                语法rewrite regex replacement [flag];
                默认值
                位置server、location、if

                regex:用来匹配URI的正则表达式

                replacement:匹配成功后,用于替换URI中被截取内容的字符串。如果该字符串是以"http://"或者"https://"开头的,则不会继续向下对URI进行其他处理,而是直接返回重写后的URI给客户端。

                location rewrite {
                	rewrite ^/rewrite/url\w*$ https://www.baidu.com;
                	rewrite ^/rewrite/(test)\w*$ /$1;
                	rewrite ^/rewrite/(demo)\w*$ /$1;
                }
                location /test{
                	default_type text/plain;
                	return 200 test_success;
                }
                location /demo{
                	default_type text/plain;
                	return 200 demo_success;
                }
                

                flag:用来设置rewrite对URI的处理行为,可选值有如下:

                • last:终止继续在本location块中处理接收到的URI,并将此处重写的URI作为一个新的URI,使用各location块进行处理。该标志将重写后的URI重写在server块中执行,为重写后的URI提供了转入到其他location块的机会。
                  location rewrite {
                  	rewrite ^/rewrite/(test)\w*$ /$1 last;
                  	rewrite ^/rewrite/(demo)\w*$ /$1 last;
                  }
                  location /test{
                  	default_type text/plain;
                  	return 200 test_success;
                  }
                  location /demo{
                  	default_type text/plain;
                  	return 200 demo_success;
                  }
                  

                  访问 http://192.168.200.133:8081/rewrite/testabc,能正确访问

                  image.png

                  • break:将此处重写的URI作为一个新的URI,在本块中继续进行处理。该标志将重写后的地址在当前的location块中执行,不会将新的URI转向其他的location块。
                    location rewrite {
                        #/test   /usr/local/nginx/html/test/index.html
                    	rewrite ^/rewrite/(test)\w*$ /$1 break;
                    	rewrite ^/rewrite/(demo)\w*$ /$1 break;
                    }
                    location /test{
                    	default_type text/plain;
                    	return 200 test_success;
                    }
                    location /demo{
                    	default_type text/plain;
                    	return 200 demo_success;
                    }
                    

                    访问 http://192.168.200.133:8081/rewrite/demoabc,页面报404错误

                    image.png

                    • redirect:将重写后的URI返回给客户端,状态码为302,指明是临时重定向URI,主要用在replacement变量不是以"http://"或者"https://"开头的情况。
                      location rewrite {
                      	rewrite ^/rewrite/(test)\w*$ /$1 redirect;
                      	rewrite ^/rewrite/(demo)\w*$ /$1 redirect;
                      }
                      location /test{
                      	default_type text/plain;
                      	return 200 test_success;
                      }
                      location /demo{
                      	default_type text/plain;
                      	return 200 demo_success;
                      }
                      

                      访问http://192.168.200.133:8081/rewrite/testabc请求会被临时重定向,浏览器地址也会发生改变

                      • permanent:将重写后的URI返回给客户端,状态码为301,指明是永久重定向URI,主要用在replacement变量不是以"http://"或者"https://"开头的情况。
                        location rewrite {
                        	rewrite ^/rewrite/(test)\w*$ /$1 permanent;
                        	rewrite ^/rewrite/(demo)\w*$ /$1 permanent;
                        }
                        location /test{
                        	default_type text/plain;
                        	return 200 test_success;
                        }
                        location /demo{
                        	default_type text/plain;
                        	return 200 demo_success;
                        }
                        

                        访问http://192.168.200.133:8081/rewrite/testabc请求会被永久重定向,浏览器地址也会发生改变

                        rewrite_log指令

                        该指令配置是否开启URL重写日志的输出功能。

                        语法rewrite_log on|off;
                        默认值rewrite_log off;
                        位置http、server、location、if

                        开启后,URL重写的相关日志将以notice级别输出到error_log指令配置的日志文件汇总。

                        rewrite_log on;
                        error_log  logs/error.log notice;
                        

                        Rewrite的案例

                        域名跳转

                        》问题分析

                        先来看一个效果,如果我们想访问京东网站,大家都知道我们可以输入www.jd.com,但是同样的我们也可以输入www.360buy.com同样也都能访问到京东网站。这个其实是因为京东刚开始的时候域名就是www.360buy.com,后面由于各种原因把自己的域名换成了www.jd.com, 虽然说域名变量,但是对于以前只记住了www.360buy.com的用户来说,我们如何把这部分用户也迁移到我们新域名的访问上来,针对于这个问题,我们就可以使用Nginx中Rewrite的域名跳转来解决。

                        》环境准备

                        • 准备三个域名:
                          vim /etc/hosts
                          
                          127.0.0.1   www.itcast.cn
                          127.0.0.1   www.itheima.cn
                          127.0.0.1   www.itheima.com
                          
                          • 通过Nginx实现访问www.itcast.cn
                            server {
                            	listen 80;
                            	server_name www.itcast.cn;
                            	location /{
                            		default_type text/html;
                            		return 200 '

                            welcome to itcast

                            '; } }

                            》通过Rewrite完成将www.ithema.com和www.itheima.cn的请求跳转到www.itcast.com

                            server {
                            	listen 80;
                            	server_name www.itheima.com www.itheima.cn;
                            	rewrite ^/ http://www.itcast.cn;
                            }
                            

                            问题描述:如何在域名跳转的过程中携带请求的URI?

                            修改配置信息

                            server {
                            	listen 80;
                            	server_name www.itheima.com www.itheima.cn;
                            	rewrite ^(.*) http://www.itcast.cn$1;
                            }
                            
                            域名镜像

                            镜像网站指定是将一个完全相同的网站分别放置到几台服务器上,并分别使用独立的URL进行访问。其中一台服务器上的网站叫主站,其他的为镜像网站。镜像网站和主站没有太大的区别,可以把镜像网站理解为主站的一个备份节点。可以通过镜像网站提供网站在不同地区的响应速度。镜像网站可以平衡网站的流量负载、可以解决网络宽带限制、封锁等。

                            image.png

                            而我们所说的域名镜像和网站镜像比较类似,上述案例中,将www.itheima.com和 www.itheima.cn都能跳转到www.itcast.cn,那么www.itcast.cn我们就可以把它起名叫主域名,其他两个就是我们所说的镜像域名,当然如果我们不想把整个网站做镜像,只想为其中某一个子目录下的资源做镜像,我们可以在location块中配置rewrite功能,比如:

                            server {
                                listen          80;
                                server_name     www.itheima.cn www.itheima.com;
                                location /user {
                                	rewrite ^/user(.*)$ http://www.itcast.cn$1;
                                }
                                location /emp{
                                    default_type text/html;
                                    return 200 '

                            emp_success

                            '; } }
                            独立域名

                            一个完整的项目包含多个模块,比如购物网站有商品搜索模块、商品详情模块和购物车模块等,那么我们如何为每一个模块设置独立的域名。

                            需求:

                            http://search.itcast.com:81  访问商品搜索模块
                            http://item.itcast.com:82	  访问商品详情模块
                            http://cart.itcast.com:83	  访问商品购物车模块
                            
                            server{
                            	listen 81;
                            	server_name search.itcast.com;
                            	rewrite ^(.*) http://www.itcast.cn/search$1;
                            }
                            server{
                            	listen 82;
                            	server_name item.itcast.com;
                            	rewrite ^(.*) http://www.itcast.cn/item$1;
                            }
                            server{
                            	listen 83;
                            	server_name cart.itcast.com;
                            	rewrite ^(.*) http://www.itcast.cn/cart$1;
                            }
                            
                            目录自动添加"/"

                            问题描述

                            通过一个例子来演示下问题:

                            server {
                            	listen	8082;
                            	server_name localhost;
                            	location /heima {
                            		root html;
                            		index index.html;
                            	}
                            }
                            

                            通过http://192.168.200.133:8082/heima和通过http://192.168.200.133:8082/heima/访问的区别?

                            如果不加斜杠,Nginx服务器内部会自动做一个301的重定向,重定向的地址会有一个指令叫server_name_in_redirect on|off;来决定重定向的地址:

                            如果该指令为on
                            	重定向的地址为:  http://server_name:8082/目录名/;
                            	http://localhost:8082/heima/
                            如果该指令为off
                            	重定向的地址为:  http://原URL中的域名:8082/目录名/;
                            	http://192.168.200.133:8082/heima/
                            

                            所以就拿刚才的地址来说,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自动添加一个斜杠

                            server {
                            	listen	80;
                            	server_name localhost;
                            	server_name_in_redirect on;
                            	location /heima {
                            		if (-d $request_filename){
                            			rewrite ^/(.*)([^/])$ http://$host/$1$2/ permanent;
                            		}
                            	}
                            }
                            
                            合并目录

                            搜索引擎优化(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,

                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /server{
                            		root html;
                            	}
                            }
                            

                            但是这个是非常不利于SEO搜索引擎优化的,同时客户端也不好记.使用rewrite我们可以进行如下配置:

                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /server{
                            		rewrite ^/server-([0-9]+)-([0-9]+)-([0-9]+)-([0-9]+)\.html$ /server/$1/$2/$3/$4/$5.html last;
                            	}
                            }
                            

                            这样的花,客户端只需要输入http://www.web.name/server-11-22-33-44-20.html就可以访问到20.html页面了。这里也充分利用了rewrite指令支持正则表达式的特性。

                            防盗链

                            防盗链之前我们已经介绍过了相关的知识,在rewrite中的防盗链和之前将的原理其实都是一样的,只不过通过rewrite可以将防盗链的功能进行完善下,当出现防盗链的情况,我们可以使用rewrite将请求转发到自定义的一张图片和页面,给用户比较好的提示信息。下面我们就通过根据文件类型实现防盗链的一个配置实例:

                            location /images {
                                root html;
                                valid_referers none blocked www.baidu.com;
                                if ($invalid_referer){
                                    #return 403;
                                    rewrite ^/    /images/forbidden.png break;
                                }
                            }
                            

                            Nginx反向代理

                            Nginx反向代理概述

                            关于正向代理和反向代理,我们在前面的章节已经通过一张图给大家详细的介绍过了,简而言之就是正向代理代理的对象是客户端,反向代理代理的是服务端,这是两者之间最大的区别。

                            Nginx即可以实现正向代理,也可以实现反向代理。

                            我们先来通过一个小案例演示下Nginx正向代理的简单应用。

                            先提需求:

                            image.png

                            (1)服务端的设置:

                            http {
                              log_format main 'client send request=>clientIp=$remote_addr serverIp=>$host';
                            	server{
                            		listen 80;
                            		server_name	localhost;
                            		access_log logs/access.log main;
                            		location {
                            			root html;
                            			index index.html index.htm;
                            		}
                            	}
                            }
                            

                            (2)使用客户端访问服务端,打开日志查看结果

                            image.png

                            (3)代理服务器设置:

                            server {
                                    listen  82;
                                    resolver 8.8.8.8;
                                    location /{
                                            proxy_pass http://$host$request_uri;
                                    }
                                }
                            

                            (4)查看代理服务器的IP(192.168.200.146)和Nginx配置监听的端口(82)

                            (5)在客户端配置代理服务器

                            image.png

                            (6)设置完成后,再次通过浏览器访问服务端

                            image.png

                            通过对比,上下两次的日志记录,会发现虽然我们是客户端访问服务端,但是如何使用了代理,那么服务端能看到的只是代理发送过去的请求,这样的化,就使用Nginx实现了正向代理的设置。

                            但是Nginx正向代理,在实际的应用中不是特别多,所以我们简单了解下,接下来我们继续学习Nginx的反向代理,这是Nginx比较重要的一个功能。

                            Nginx反向代理的配置语法

                            Nginx反向代理模块的指令是由ngx_http_proxy_module模块进行解析,该模块在安装Nginx的时候已经自己加装到Nginx中了,接下来我们把反向代理中的常用指令一一介绍下:

                            proxy_pass
                            proxy_set_header
                            proxy_redirect
                            
                            proxy_pass

                            该指令用来设置被代理服务器地址,可以是主机名称、IP地址加端口号形式。

                            语法proxy_pass URL;
                            默认值
                            位置location

                            URL:为要设置的被代理服务器地址,包含传输协议(http,https://)、主机名称或IP地址加端口号、URI等要素。

                            举例:

                            proxy_pass http://www.baidu.com;
                            location /server{}
                            proxy_pass http://192.168.200.146;
                                http://192.168.200.146/server/index.html
                            proxy_pass http://192.168.200.146/;
                                http://192.168.200.146/index.html
                            

                            大家在编写proxy_pass的时候,后面的值要不要加"/"?

                            接下来通过例子来说明刚才我们提到的问题:

                            server {
                            	listen 80;
                            	server_name localhost;
                            	location /{
                            		#proxy_pass http://192.168.200.146;
                            		proxy_pass http://192.168.200.146/;
                            	}
                            }
                            当客户端访问 http://localhost/index.html,效果是一样的
                            server{
                            	listen 80;
                            	server_name localhost;
                            	location /server{
                            		#proxy_pass http://192.168.200.146;
                            		proxy_pass http://192.168.200.146/;
                            	}
                            }
                            当客户端访问 http://localhost/server/index.html
                            这个时候,第一个proxy_pass就变成了http://localhost/server/index.html
                            第二个proxy_pass就变成了http://localhost/index.html效果就不一样了。
                            
                            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]

                            server {
                                    listen  8080;
                                    server_name localhost;
                                    default_type text/plain;
                                    return 200 $http_username;
                            }
                            

                            代理服务器: [192.168.200.133]

                            server {
                                    listen  8080;
                                    server_name localhost;
                                    location /server {
                                            proxy_pass http://192.168.200.146:8080/;
                                            proxy_set_header username TOM;
                                    }
                                }
                            

                            访问测试

                            proxy_redirect

                            该指令是用来重置头信息中的"Location"和"Refresh"的值。

                            | 语法 | proxy_redirect redirect replacement;

                            proxy_redirect default;

                            proxy_redirect off; |

                            | — | — |

                            | 默认值 | proxy_redirect default; |

                            | 位置 | http、server、location |

                            》为什么要用该指令?

                            服务端[192.168.200.146]

                            server {
                                listen  8081;
                                server_name localhost;
                                if (!-f $request_filename){
                                	return 302 http://192.168.200.146;
                                }
                            }
                            

                            代理服务端[192.168.200.133]

                            server {
                            	listen  8081;
                            	server_name localhost;
                            	location / {
                            		proxy_pass http://192.168.200.146:8081/;
                            		proxy_redirect http://192.168.200.146 http://192.168.200.133;
                            	}
                            }
                            

                            》该指令的几组选项

                            proxy_redirect redirect replacement;

                            redirect:目标,Location的值
                            replacement:要替换的值
                            

                            proxy_redirect default;

                            default;
                            将location块的uri变量作为replacement,
                            将proxy_pass变量作为redirect进行替换
                            

                            proxy_redirect off;

                            关闭proxy_redirect的功能
                            

                            Nginx反向代理实战

                            image.png

                            服务器1,2,3存在两种情况

                            第一种情况: 三台服务器的内容不一样。
                            第二种情况: 三台服务器的内容是一样。
                            
                            1. 如果服务器1、服务器2和服务器3的内容不一样,那我们可以根据用户请求来分发到不同的服务器。
                            代理服务器
                            server {
                                    listen          8082;
                                    server_name     localhost;
                                    location /server1 {
                                            proxy_pass http://192.168.200.146:9001/;
                                    }
                                    location /server2 {
                                            proxy_pass http://192.168.200.146:9002/;
                                    }
                                    location /server3 {
                                            proxy_pass http://192.168.200.146:9003/;
                                    }
                            }
                            服务端
                            server1
                            server {
                                    listen          9001;
                                    server_name     localhost;
                                    default_type text/html;
                                    return 200 '

                            192.168.200.146:9001

                            ' } server2 server { listen 9002; server_name localhost; default_type text/html; return 200 '

                            192.168.200.146:9002

                            ' } server3 server { listen 9003; server_name localhost; default_type text/html; return 200 '

                            192.168.200.146:9003

                            ' }
                            1. 如果服务器1、服务器2和服务器3的内容是一样的,该如何处理?

                            Nginx的安全控制

                            关于web服务器的安全是比较大的一个话题,里面所涉及的内容很多,Nginx反向代理是如何来提升web服务器的安全呢?

                            安全隔离
                            

                            什么是安全隔离?

                            通过代理分开了客户端到应用程序服务器端的连接,实现了安全措施。在反向代理之前设置防火墙,仅留一个入口供代理服务器访问。

                            image.png

                            如何使用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模块的增量添加

                            》将原有/usr/local/nginx/sbin/nginx进行备份
                            》拷贝nginx之前的配置信息
                            》在nginx的安装源码进行配置指定对应模块  ./configure --with-http_ssl_module
                            》通过make模板进行编译
                            》将objs下面的nginx移动到/usr/local/nginx/sbin下
                            》在源码目录下执行  make upgrade进行升级,这个可以实现不停机添加新模块的功能
                            
                            Nginx的SSL相关指令

                            因为刚才我们介绍过该模块的指令都是通过ngx_http_ssl_module模块来解析的。

                            》ssl:该指令用来在指定的服务器开启HTTPS,可以使用 listen 443 ssl,后面这种方式更通用些。

                            语法ssl on | off;
                            默认值ssl off;
                            位置http、server
                            server{
                            	listen 443 ssl;
                            }
                            

                            》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;
                            位置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
                            生成证书

                            方式一:使用阿里云/腾讯云等第三方服务进行购买。

                            方式二:使用openssl生成证书

                            先要确认当前系统是否有安装openssl

                            openssl version
                            

                            安装下面的命令进行生成

                            mkdir /root/cert
                            cd /root/cert
                            openssl genrsa -des3 -out server.key 1024
                            openssl req -new -key server.key -out server.csr
                            cp server.key server.key.org
                            openssl rsa -in server.key.org -out server.key
                            openssl x509 -req -days 365 -in server.csr -signkey server.key -out server.crt
                            
                            开启SSL实例
                            server {
                                listen       443 ssl;
                                server_name  localhost;
                                ssl_certificate      server.cert;
                                ssl_certificate_key  server.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;
                                }
                            }
                            

                            (4)验证

                            反向代理系统调优

                            反向代理值Buffer和Cache

                            Buffer翻译过来是"缓冲",Cache翻译过来是"缓存"。

                            image.png

                            总结下:

                            相同点:
                            两种方式都是用来提供IO吞吐效率,都是用来提升Nginx代理的性能。
                            不同点:
                            缓冲主要用来解决不同设备之间数据传递速度不一致导致的性能低的问题,缓冲中的数据一旦此次操作完成后,就可以删除。
                            缓存主要是备份,将被代理服务器的数据缓存一份到代理服务器,这样的话,客户端再次获取相同数据的时候,就只需要从代理服务器上获取,效率较高,缓存中的数据可以重复使用,只有满足特定条件才会删除.
                            

                            (1)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

                            通用网站的配置

                            proxy_buffering on;
                            proxy_buffer_size 4 32k;
                            proxy_busy_buffers_size 64k;
                            proxy_temp_file_write_size 64k;
                            

                            根据项目的具体内容进行相应的调节。

                            Nginx负载均衡

                            负载均衡概述

                            早期的网站流量和业务功能都比较简单,单台服务器足以满足基本的需求,但是随着互联网的发展,业务流量越来越大并且业务逻辑也跟着越来越复杂,单台服务器的性能及单点故障问题就凸显出来了,因此需要多台服务器进行性能的水平扩展及避免单点故障出现。那么如何将不同用户的请求流量分发到不同的服务器上呢?

                            image.png

                            负载均衡的原理及处理流程

                            系统的扩展可以分为纵向扩展和横向扩展。

                            纵向扩展是从单机的角度出发,通过增加系统的硬件处理能力来提升服务器的处理能力

                            横向扩展是通过添加机器来满足大型网站服务的处理能力。

                            image.png

                            这里面涉及到两个重要的角色分别是"应用集群"和"负载均衡器"。

                            应用集群:将同一应用部署到多台机器上,组成处理集群,接收负载均衡设备分发的请求,进行处理并返回响应的数据。

                            负载均衡器:将用户访问的请求根据对应的负载均衡算法,分发到集群中的一台服务器进行处理。

                            负载均衡的作用

                            1、解决服务器的高并发压力,提高应用程序的处理性能。

                            2、提供故障转移,实现高可用。

                            3、通过添加或减少服务器数量,增强网站的可扩展性。

                            4、在负载均衡器上进行过滤,可以提高系统的安全性。

                            负载均衡常用的处理方式

                            方式一:用户手动选择

                            这种方式比较原始,只要实现的方式就是在网站主页上面提供不同线路、不同服务器链接方式,让用户来选择自己访问的具体服务器,来实现负载均衡。

                            image.png

                            方式二:DNS轮询方式

                            DNS

                            域名系统(服务)协议(DNS)是一种分布式网络目录服务,主要用于域名与 IP 地址的相互转换。
                            

                            大多域名注册商都支持对同一个主机名添加多条A记录,这就是DNS轮询,DNS服务器将解析请求按照A记录的顺序,随机分配到不同的IP上,这样就能完成简单的负载均衡。DNS轮询的成本非常低,在一些不重要的服务器,被经常使用。

                            image.png

                            如下是我们为某一个域名添加的IP地址,用2台服务器来做负载均衡。

                            image.png

                            验证:

                            ping www.nginx521.cn
                            

                            清空本地的dns缓存

                            ipconfig/flushdns
                            

                            我们发现使用DNS来实现轮询,不需要投入过多的成本,虽然DNS轮询成本低廉,但是DNS负载均衡存在明显的缺点。

                            1.可靠性低

                            假设一个域名DNS轮询多台服务器,如果其中的一台服务器发生故障,那么所有的访问该服务器的请求将不会有所回应,即使你将该服务器的IP从DNS中去掉,但是由于各大宽带接入商将众多的DNS存放在缓存中,以节省访问时间,导致DNS不会实时更新。所以DNS轮流上一定程度上解决了负载均衡问题,但是却存在可靠性不高的缺点。

                            2.负载均衡不均衡

                            DNS负载均衡采用的是简单的轮询负载算法,不能区分服务器的差异,不能反映服务器的当前运行状态,不能做到为性能好的服务器多分配请求,另外本地计算机也会缓存已经解析的域名到IP地址的映射,这也会导致使用该DNS服务器的用户在一定时间内访问的是同一台Web服务器,从而引发Web服务器减的负载不均衡。

                            负载不均衡则会导致某几台服务器负荷很低,而另外几台服务器负荷确很高,处理请求的速度慢,配置高的服务器分配到的请求少,而配置低的服务器分配到的请求多。

                            方式三:四/七层负载均衡

                            介绍四/七层负载均衡之前,我们先了解一个概念,OSI(open system interconnection),叫开放式系统互联模型,这个是由国际标准化组织ISO指定的一个不基于具体机型、操作系统或公司的网络体系结构。该模型将网络通信的工作分为七层。

                            image.png

                            应用层:为应用程序提供网络服务。

                            表示层:对数据进行格式化、编码、加密、压缩等操作。

                            会话层:建立、维护、管理会话连接。

                            传输层:建立、维护、管理端到端的连接,常见的有TCP/UDP。

                            网络层:IP寻址和路由选择

                            数据链路层:控制网络层与物理层之间的通信。

                            物理层:比特流传输。

                            所谓四层负载均衡指的是OSI七层模型中的传输层,主要是基于IP+PORT的负载均衡

                            实现四层负载均衡的方式:
                            硬件:F5 BIG-IP、Radware等
                            软件:LVS、Nginx、Hayproxy等
                            

                            所谓的七层负载均衡指的是在应用层,主要是基于虚拟的URL或主机IP的负载均衡

                            实现七层负载均衡的方式:
                            软件:Nginx、Hayproxy等
                            

                            四层和七层负载均衡的区别

                            四层负载均衡数据包是在底层就进行了分发,而七层负载均衡数据包则在最顶端进行分发,所以四层负载均衡的效率比七层负载均衡的要高。
                            四层负载均衡不识别域名,而七层负载均衡识别域名。
                            

                            处理四层和七层负载以为其实还有二层、三层负载均衡,二层是在数据链路层基于mac地址来实现负载均衡,三层是在网络层一般采用虚拟IP地址的方式实现负载均衡。

                            实际环境采用的模式

                            四层负载(LVS)+七层负载(Nginx)
                            

                            Nginx七层负载均衡

                            Nginx要实现七层负载均衡需要用到proxy_pass代理模块配置。Nginx默认安装支持这个模块,我们不需要再做任何处理。Nginx的负载均衡是在Nginx的反向代理基础上把用户的请求根据指定的算法分发到一组【upstream虚拟服务池】。

                            Nginx七层负载均衡的指令
                            upstream指令

                            该指令是用来定义一组服务器,它们可以是监听不同端口的服务器,并且也可以是同时监听TCP和Unix socket的服务器。服务器可以指定不同的权重,默认为1。

                            语法upstream name {…}
                            默认值
                            位置http
                            server指令

                            该指令用来指定后端服务器的名称和一些参数,可以使用域名、IP、端口或者unix socket

                            语法server name [paramerters]
                            默认值
                            位置upstream
                            Nginx七层负载均衡的实现流程

                            image.png

                            服务端设置

                            server {
                                listen   9001;
                                server_name localhost;
                                default_type text/html;
                                location /{
                                	return 200 '

                            192.168.200.146:9001

                            '; } } server { listen 9002; server_name localhost; default_type text/html; location /{ return 200 '

                            192.168.200.146:9002

                            '; } } server { listen 9003; server_name localhost; default_type text/html; location /{ return 200 '

                            192.168.200.146:9003

                            '; } }

                            负载均衡器设置

                            upstream backend{
                            	server 192.168.200.146:9091;
                            	server 192.168.200.146:9092;
                            	server 192.168.200.146:9093;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            
                            负载均衡状态

                            代理服务器在负责均衡调度中的状态有以下几个:

                            状态概述
                            down当前的server暂时不参与负载均衡
                            backup预留的备份服务器
                            max_fails允许请求失败的次数
                            fail_timeout经过max_fails失败后, 服务暂停时间
                            max_conns限制最大的接收连接数
                            down

                            down:将该服务器标记为永久不可用,那么该代理服务器将不参与负载均衡。

                            upstream backend{
                            	server 192.168.200.146:9001 down;
                            	server 192.168.200.146:9002
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            

                            该状态一般会对需要停机维护的服务器进行设置。

                            backup

                            backup:将该服务器标记为备份服务器,当主服务器不可用时,将用来传递请求。

                            upstream backend{
                            	server 192.168.200.146:9001 down;
                            	server 192.168.200.146:9002 backup;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            

                            此时需要将9094端口的访问禁止掉来模拟下唯一能对外提供访问的服务宕机以后,backup的备份服务器就要开始对外提供服务,此时为了测试验证,我们需要使用防火墙来进行拦截。

                            介绍一个工具firewall-cmd,该工具是Linux提供的专门用来操作firewall的。

                            查询防火墙中指定的端口是否开放

                            firewall-cmd --query-port=9001/tcp
                            

                            如何开放一个指定的端口

                            firewall-cmd --permanent --add-port=9002/tcp
                            

                            批量添加开发端口

                            firewall-cmd --permanent --add-port=9001-9003/tcp
                            

                            如何移除一个指定的端口

                            firewall-cmd --permanent --remove-port=9003/tcp
                            

                            重新加载

                            firewall-cmd --reload
                            

                            其中

                             --permanent表示设置为持久
                             --add-port表示添加指定端口
                             --remove-port表示移除指定端口
                            
                            max_conns

                            max_conns=number:用来设置代理服务器同时活动链接的最大数量,默认为0,表示不限制,使用该配置可以根据后端服务器处理请求的并发量来进行设置,防止后端服务器被压垮。

                            max_fails和fail_timeout

                            max_fails=number:设置允许请求代理服务器失败的次数,默认为1。

                            fail_timeout=time:设置经过max_fails失败后,服务暂停的时间,默认是10秒。

                            upstream backend{
                            	server 192.168.200.133:9001 down;
                            	server 192.168.200.133:9002 backup;
                            	server 192.168.200.133:9003 max_fails=3 fail_timeout=15;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            
                            负载均衡策略

                            介绍完Nginx负载均衡的相关指令后,我们已经能实现将用户的请求分发到不同的服务器上,那么除了采用默认的分配方式以外,我们还能采用什么样的负载算法?

                            Nginx的upstream支持如下六种方式的分配算法,分别是:

                            算法名称说明
                            轮询默认方式
                            weight权重方式
                            ip_hash依据ip分配方式
                            least_conn依据最少连接方式
                            url_hash依据URL分配方式
                            fair依据响应时间方式
                            轮询

                            是upstream模块负载均衡默认的策略。每个请求会按时间顺序逐个分配到不同的后端服务器。轮询不需要额外的配置。

                            upstream backend{
                            	server 192.168.200.146:9001 weight=1;
                            	server 192.168.200.146:9002;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            
                            weight加权[加权轮询]

                            weight=number:用来设置服务器的权重,默认为1,权重数据越大,被分配到请求的几率越大;该权重值,主要是针对实际工作环境中不同的后端服务器硬件配置进行调整的,所有此策略比较适合服务器的硬件配置差别比较大的情况。

                            upstream backend{
                            	server 192.168.200.146:9001 weight=10;
                            	server 192.168.200.146:9002 weight=5;
                            	server 192.168.200.146:9003 weight=3;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            
                            ip_hash

                            当对后端的多台动态应用服务器做负载均衡时,ip_hash指令能够将某个客户端IP的请求通过哈希算法定位到同一台后端服务器上。这样,当来自某一个IP的用户在后端Web服务器A上登录后,在访问该站点的其他URL,能保证其访问的还是后端web服务器A。

                            语法ip_hash;
                            默认值
                            位置upstream
                            upstream backend{
                            	ip_hash;
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            

                            需要额外多说一点的是使用ip_hash指令无法保证后端服务器的负载均衡,可能导致有些后端服务器接收到的请求多,有些后端服务器接收的请求少,而且设置后端服务器权重等方法将不起作用。

                            image.png

                            least_conn

                            最少连接,把请求转发给连接数较少的后端服务器。轮询算法是把请求平均的转发给各个后端,使它们的负载大致相同;但是,有些请求占用的时间很长,会导致其所在的后端负载较高。这种情况下,least_conn这种方式就可以达到更好的负载均衡效果。

                            upstream backend{
                            	least_conn;
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            

                            此负载均衡策略适合请求处理时间长短不一造成服务器过载的情况。

                            image.png

                            url_hash

                            按访问url的hash结果来分配请求,使每个url定向到同一个后端服务器,要配合缓存命中来使用。同一个资源多次请求,可能会到达不同的服务器上,导致不必要的多次下载,缓存命中率不高,以及一些资源时间的浪费。而使用url_hash,可以使得同一个url(也就是同一个资源请求)会到达同一台服务器,一旦缓存住了资源,再此收到请求,就可以从缓存中读取。

                            upstream backend{
                            	hash &request_uri;
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            

                            访问如下地址:

                            http://192.168.200.133:8083/a
                            http://192.168.200.133:8083/b
                            http://192.168.200.133:8083/c
                            

                            image.png

                            fair

                            fair采用的不是内建负载均衡使用的轮换的均衡算法,而是可以根据页面大小、加载时间长短智能的进行负载均衡。那么如何使用第三方模块的fair负载均衡策略。

                            upstream backend{
                            	fair;
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            

                            但是如何直接使用会报错,因为fair属于第三方模块实现的负载均衡。需要添加nginx-upstream-fair,如何添加对应的模块:

                            1. 下载nginx-upstream-fair模块
                            下载地址为:
                            	https://github.com/gnosek/nginx-upstream-fair
                            
                            1. 将下载的文件上传到服务器并进行解压缩
                            unzip nginx-upstream-fair-master.zip
                            
                            1. 重命名资源
                            mv nginx-upstream-fair-master fair
                            
                            1. 使用./configure命令将资源添加到Nginx模块中
                            ./configure --add-module=/root/fair
                            
                            1. 编译
                            make
                            

                            编译可能会出现如下错误,ngx_http_upstream_srv_conf_t结构中缺少default_port

                            image.png

                            解决方案:

                            在Nginx的源码中 src/http/ngx_http_upstream.h,找到ngx_http_upstream_srv_conf_s,在模块中添加添加default_port属性

                            in_port_t	   default_port
                            

                            image.png

                            然后再进行make.

                            1. 更新Nginx

                              6.1 将sbin目录下的nginx进行备份

                            mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginxold
                            
                            6.2 将安装目录下的objs中的nginx拷贝到sbin目录
                            
                            cd objs
                            cp nginx /usr/local/nginx/sbin
                            
                             6.3 更新Nginx
                            
                            cd ../
                            make upgrade
                            
                            1. 编译测试使用Nginx

                            上面介绍了Nginx常用的负载均衡的策略,有人说是5种,是把轮询和加权轮询归为一种,也有人说是6种。那么在咱们以后的开发中到底使用哪种,这个需要根据实际项目的应用场景来决定的。

                            负载均衡案例
                            案例一:对所有请求实现一般轮询规则的负载均衡
                            upstream backend{
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            
                            案例二:对所有请求实现加权轮询规则的负载均衡
                            upstream backend{
                            	server 192.168.200.146:9001 weight=7;
                            	server 192.168.200.146:9002 weight=5;
                            	server 192.168.200.146:9003 weight=3;
                            }
                            server {
                            	listen 8083;
                            	server_name localhost;
                            	location /{
                            		proxy_pass http://backend;
                            	}
                            }
                            
                            案例三:对特定资源实现负载均衡
                            upstream videobackend{
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            }
                            upstream filebackend{
                            	server 192.168.200.146:9003;
                            	server 192.168.200.146:9004;
                            }
                            server {
                            	listen 8084;
                            	server_name localhost;
                            	location /video/ {
                            		proxy_pass http://videobackend;
                            	}
                            	location /file/ {
                            		proxy_pass http://filebackend;
                            	}
                            }
                            
                            案例四:对不同域名实现负载均衡
                            upstream itcastbackend{
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            }
                            upstream itheimabackend{
                            	server 192.168.200.146:9003;
                            	server 192.168.200.146:9004;
                            }
                            server {
                            	listen	8085;
                            	server_name www.itcast.cn;
                            	location / {
                            		proxy_pass http://itcastbackend;
                            	}
                            }
                            server {
                            	listen	8086;
                            	server_name www.itheima.cn;
                            	location / {
                            		proxy_pass http://itheimabackend;
                            	}
                            }
                            
                            案例五:实现带有URL重写的负载均衡
                            upstream backend{
                            	server 192.168.200.146:9001;
                            	server 192.168.200.146:9002;
                            	server 192.168.200.146:9003;
                            }
                            server {
                            	listen	80;
                            	server_name localhost;
                            	location /file/ {
                            		rewrite ^(/file/.*) /server/$1 last;
                            	}
                            	location / {
                            		proxy_pass http://backend;
                            	}
                            }
                            

                            Nginx四层负载均衡

                            Nginx在1.9之后,增加了一个stream模块,用来实现四层协议的转发、代理、负载均衡等。stream模块的用法跟http的用法类似,允许我们配置一组TCP或者UDP等协议的监听,然后通过proxy_pass来转发我们的请求,通过upstream添加多个后端服务,实现负载均衡。

                            四层协议负载均衡的实现,一般都会用到LVS、HAProxy、F5等,要么很贵要么配置很麻烦,而Nginx的配置相对来说更简单,更能快速完成工作。

                            添加stream模块的支持

                            Nginx默认是没有编译这个模块的,需要使用到stream模块,那么需要在编译的时候加上--with-stream。

                            完成添加--with-stream的实现步骤:

                            》将原有/usr/local/nginx/sbin/nginx进行备份
                            》拷贝nginx之前的配置信息
                            》在nginx的安装源码进行配置指定对应模块  ./configure --with-stream
                            》通过make模板进行编译
                            》将objs下面的nginx移动到/usr/local/nginx/sbin下
                            》在源码目录下执行  make upgrade进行升级,这个可以实现不停机添加新模块的功能
                            
                            Nginx四层负载均衡的指令
                            stream指令

                            该指令提供在其中指定流服务器指令的配置文件上下文。和http指令同级。

                            语法stream { … }
                            默认值
                            位置main
                            upstream指令

                            该指令和http的upstream指令是类似的。

                            四层负载均衡的案例
                            需求分析

                            image.png

                            实现步骤

                            (1)准备Redis服务器,在一条服务器上准备三个Redis,端口分别是6379,6378

                            1.上传redis的安装包,redis-4.0.14.tar.gz

                            2.将安装包进行解压缩

                            tar -zxf redis-4.0.14.tar.gz
                            

                            3.进入redis的安装包

                            cd redis-4.0.14
                            

                            4.使用make和install进行编译和安装

                            make PREFIX=/usr/local/redis/redis01 install
                            

                            5.拷贝redis配置文件redis.conf到/usr/local/redis/redis01/bin目录中

                            cp redis.conf	/usr/local/redis/redis01/bin
                            

                            6.修改redis.conf配置文件

                            port  6379      #redis的端口
                            daemonize yes   #后台启动redis
                            

                            7.将redis01复制一份为redis02

                            cd /usr/local/redis
                            cp -r redis01 redis02
                            

                            8.将redis02文件文件夹中的redis.conf进行修改

                            port  6378      #redis的端口
                            daemonize yes   #后台启动redis
                            

                            9.分别启动,即可获取两个Redis.并查看

                            ps -ef | grep redis
                            

                            使用Nginx将请求分发到不同的Redis服务器上。

                            (2)准备Tomcat服务器.

                            1.上传tomcat的安装包,apache-tomcat-8.5.56.tar.gz

                            2.将安装包进行解压缩

                            tar -zxf apache-tomcat-8.5.56.tar.gz
                            

                            3.进入tomcat的bin目录

                            cd apache-tomcat-8.5.56/bin
                            ./startup
                            

                            nginx.conf配置

                            stream {
                                    upstream redisbackend {
                                            server 192.168.200.146:6379;
                                            server 192.168.200.146:6378;
                                    }
                                    upstream tomcatbackend {
                                    		server 192.168.200.146:8080;
                                    }
                                    server {
                                            listen  81;
                                            proxy_pass redisbackend;
                                    }
                                    server {
                                    		listen	82;
                                    		proxy_pass tomcatbackend;
                                    }
                            }
                            

                            访问测试。

                            Nginx缓存集成

                            缓存的概念

                            缓存就是数据交换的缓冲区(称作:Cache),当用户要获取数据的时候,会先从缓存中去查询获取数据,如果缓存中有就会直接返回给用户,如果缓存中没有,则会发请求从服务器重新查询数据,将数据返回给用户的同时将数据放入缓存,下次用户就会直接从缓存中获取数据。

                            image.png

                            缓存其实在很多场景中都有用到,比如:

                            场景作用
                            操作系统磁盘缓存减少磁盘机械操作
                            数据库缓存减少文件系统的IO操作
                            应用程序缓存减少对数据库的查询
                            Web服务器缓存减少对应用服务器请求次数
                            浏览器缓存减少与后台的交互次数

                            缓存的优点

                            1.减少数据传输,节省网络流量,加快响应速度,提升用户体验;
                            2.减轻服务器压力;
                            3.提供服务端的高可用性;
                            

                            缓存的缺点

                            1.数据的不一致
                            2.增加成本
                            

                            image.png

                            本次课程注解讲解的是Nginx,Nginx作为web服务器,Nginx作为Web缓存服务器,它介于客户端和应用服务器之间,当用户通过浏览器访问一个URL时,web缓存服务器会去应用服务器获取要展示给用户的内容,将内容缓存到自己的服务器上,当下一次请求到来时,如果访问的是同一个URL,web缓存服务器就会直接将之前缓存的内容返回给客户端,而不是向应用服务器再次发送请求。web缓存降低了应用服务器、数据库的负载,减少了网络延迟,提高了用户访问的响应速度,增强了用户的体验。

                            Nginx的web缓存服务

                            Nginx是从0.7.48版开始提供缓存功能。Nginx是基于Proxy Store来实现的,其原理是把URL及相关组合当做Key,在使用MD5算法对Key进行哈希,得到硬盘上对应的哈希目录路径,从而将缓存内容保存在该目录中。它可以支持任意URL连接,同时也支持404/301/302这样的非200状态码。Nginx即可以支持对指定URL或者状态码设置过期时间,也可以使用purge命令来手动清除指定URL的缓存。

                            image.png

                            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:缓存路径地址,如:

                            /usr/local/proxy_cache
                            

                            levels: 指定该缓存空间对应的目录,最多可以设置3层,每层取值为1|2如 :

                            levels=1:2   缓存空间有两层目录,第一次是1个字母,第二次是2个字母
                            举例说明:
                            itheima[key]通过MD5加密以后的值为 43c8233266edce38c2c9af0694e2107d
                            levels=1:2   最终的存储路径为/usr/local/proxy_cache/d/07
                            levels=2:1:2 最终的存储路径为/usr/local/proxy_cache/7d/0/21
                            levels=2:2:2 最终的存储路径为??/usr/local/proxy_cache/7d/10/e2
                            

                            keys_zone:用来为这个缓存区设置名称和指定大小,如:

                            keys_zone=itcast:200m  缓存区的名称是itcast,大小为200M,1M大概能存储8000个keys
                            

                            inactive:指定缓存的数据多次时间未被访问就将被删除,如:

                            inactive=1d   缓存数据在1天内没有被访问就会被删除
                            

                            max_size:设置最大缓存空间,如果缓存空间存满,默认会覆盖缓存时间最长的资源,如:

                            max_size=20g
                            

                            配置实例:

                            http{
                            	proxy_cache_path /usr/local/proxy_cache keys_zone=itcast:200m  levels=1:2:1 inactive=1d max_size=20g;
                            }
                            

                            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 s c h e m e scheme schemeproxy_host$request_uri;
                            位置http、server、location

                            proxy_cache_valid

                            该指令用来对不同返回状态码的URL设置不同的缓存时间

                            语法proxy_cache_valid [code …] time;
                            默认值
                            位置http、server、location

                            如:

                            proxy_cache_valid 200 302 10m;
                            proxy_cache_valid 404 1m;
                            为200和302的响应URL设置10分钟缓存,为404的响应URL设置1分钟缓存
                            proxy_cache_valid any 1m;
                            对所有响应状态码的URL都设置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方法。

                            Nginx缓存设置案例

                            需求分析

                            image.png

                            步骤实现

                            1.环境准备

                            应用服务器的环境准备

                            (1)在192.168.200.146服务器上的tomcat的webapps下面添加一个js目录,并在js目录中添加一个jquery.js文件

                            (2)启动tomcat

                            (3)访问测试

                            http://192.168.200.146:8080/js/jquery.js
                            

                            Nginx的环境准备

                            (1)完成Nginx反向代理配置

                            http{
                            	upstream backend{
                            		server 192.168.200.146:8080;
                            	}
                            	server {
                            		listen       8080;
                                    server_name  localhost;
                                    location / {
                                    	proxy_pass http://backend/js/;
                                    }
                            	}
                            }
                            

                            (2)完成Nginx缓存配置

                            4.添加缓存配置

                            http{
                            	proxy_cache_path /usr/local/proxy_cache levels=2:1 keys_zone=itcast:200m inactive=1d max_size=20g;
                            	upstream backend{
                            		server 192.168.200.146:8080;
                            	}
                            	server {
                            		listen       8080;
                                    server_name  localhost;
                                    location / {
                                    	proxy_cache itcast;
                                        proxy_cache_key itheima;
                                        proxy_cache_min_uses 5;
                                        proxy_cache_valid 200 5d;
                                        proxy_cache_valid 404 30s;
                                        proxy_cache_valid any 1m;
                                        add_header nginx-cache "$upstream_cache_status";
                                    	proxy_pass http://backend/js/;
                                    }
                            	}
                            }
                            

                            Nginx缓存的清除

                            方式一:删除对应的缓存目录

                            rm -rf /usr/local/proxy_cache/......
                            

                            方式二:使用第三方扩展模块

                            ngx_cache_purge

                            (1)下载ngx_cache_purge模块对应的资源包,并上传到服务器上。

                            ngx_cache_purge-2.3.tar.gz
                            

                            (2)对资源文件进行解压缩

                            tar -zxf ngx_cache_purge-2.3.tar.gz
                            

                            (3)修改文件夹名称,方便后期配置

                            mv ngx_cache_purge-2.3 purge
                            

                            (4)查询Nginx的配置参数

                            nginx -V
                            

                            (5)进入Nginx的安装目录,使用./configure进行参数配置

                            ./configure --add-module=/root/nginx/module/purge
                            

                            (6)使用make进行编译

                            make
                            

                            (7)将nginx安装目录的nginx二级制可执行文件备份

                            mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginxold
                            

                            (8)将编译后的objs中的nginx拷贝到nginx的sbin目录下

                            cp objs/nginx /usr/local/nginx/sbin
                            

                            (9)使用make进行升级

                            make upgrade
                            

                            (10)在nginx配置文件中进行如下配置

                            server{
                            	location ~/purge(/.*) {
                            		proxy_cache_purge itcast itheima;
                            	}
                            }
                            

                            Nginx设置资源不缓存

                            前面咱们已经完成了Nginx作为web缓存服务器的使用。但是我们得思考一个问题就是不是所有的数据都适合进行缓存。比如说对于一些经常发生变化的数据。如果进行缓存的话,就很容易出现用户访问到的数据不是服务器真实的数据。所以对于这些资源我们在缓存的过程中就需要进行过滤,不进行缓存。

                            Nginx也提供了这块的功能设置,需要使用到如下两个指令

                            proxy_no_cache

                            该指令是用来定义不将数据进行缓存的条件。

                            语法proxy_no_cache string …;
                            默认值
                            位置http、server、location

                            配置实例

                            proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;
                            

                            proxy_cache_bypass

                            该指令是用来设置不从缓存中获取数据的条件。

                            语法proxy_cache_bypass string …;
                            默认值
                            位置http、server、location

                            配置实例

                            proxy_cache_bypass $cookie_nocache $arg_nocache $arg_comment;
                            

                            上述两个指令都有一个指定的条件,这个条件可以是多个,并且多个条件中至少有一个不为空且不等于"0",则条件满足成立。上面给的配置实例是从官方网站获取的,里面使用到了三个变量,分别是 c o o k i e n o c a c h e 、 cookie_nocache、 cookien​ocache、arg_nocache、$arg_comment

                            c o o k i e n o c a c h e 、 cookie_nocache、 cookien​ocache、arg_nocache、$arg_comment

                            这三个参数分别代表的含义是:

                            $cookie_nocache
                            指的是当前请求的cookie中键的名称为nocache对应的值
                            $arg_nocache和$arg_comment
                            指的是当前请求的参数中属性名为nocache和comment对应的属性值
                            

                            案例演示下:

                            log_format params $cookie_nocache | $arg_nocache | $arg_comment;
                            server{
                            	listen	8081;
                            	server_name localhost;
                            	location /{
                            		access_log logs/access_params.log params;
                            		add_header Set-Cookie 'nocache=999';
                            		root html;
                            		index index.html;
                            	}
                            }
                            

                            案例实现

                            设置不缓存资源的配置方案

                            server{
                            	listen	8080;
                            	server_name localhost;
                            	location / {
                            		if ($request_uri ~ /.*\.js$){
                                       set $nocache 1;
                                    }
                            		proxy_no_cache $nocache $cookie_nocache $arg_nocache $arg_comment;
                                    proxy_cache_bypass $nocache $cookie_nocache $arg_nocache $arg_comment;
                            	}
                            }
                            

                            Nginx实现服务器端集群搭建

                            Nginx与Tomcat部署

                            前面课程已经将Nginx的大部分内容进行了讲解,我们都知道了Nginx在高并发场景和处理静态资源是非常高性能的,但是在实际项目中除了静态资源还有就是后台业务代码模块,一般后台业务都会被部署在Tomcat,weblogic或者是websphere等web服务器上。那么如何使用Nginx接收用户的请求并把请求转发到后台web服务器?

                            image.png

                            步骤分析:

                            1.准备Tomcat环境,并在Tomcat上部署一个web项目
                            2.准备Nginx环境,使用Nginx接收请求,并把请求分发到Tomat上
                            

                            环境准备(Tomcat)

                            浏览器访问:

                            http://192.168.200.146:8080/demo/index.html
                            

                            image.png

                            获取动态资源的链接地址:

                            http://192.168.200.146:8080/demo/getAddress
                            

                            本次课程将采用Tomcat作为后台web服务器

                            (1)在Centos上准备一个Tomcat

                            1.Tomcat官网地址:https://tomcat.apache.org/
                            2.下载tomcat,本次课程使用的是apache-tomcat-8.5.59.tar.gz
                            3.将tomcat进行解压缩
                            mkdir web_tomcat
                            tar -zxf apache-tomcat-8.5.59.tar.gz -C /web_tomcat
                            

                            (2)准备一个web项目,将其打包为war

                            1.将资料中的demo.war上传到tomcat8目录下的webapps包下
                            2.将tomcat进行启动,进入tomcat8的bin目录下
                            ./startup.sh
                            

                            (3)启动tomcat进行访问测试。

                            静态资源: http://192.168.200.146:8080/demo/index.html
                            动态资源: http://192.168.200.146:8080/demo/getAddress
                            

                            环境准备(Nginx)

                            (1)使用Nginx的反向代理,将请求转给Tomcat进行处理。

                            upstream webservice {
                            	server 192.168.200.146:8080;
                            }
                            server{
                                listen		80;
                                server_name localhost;
                                location /demo {
                                	proxy_pass http://webservice;
                                }
                            }
                            

                            (2)启动访问测试

                            image.png

                            学习到这,可能大家会有一个困惑,明明直接通过tomcat就能访问,为什么还需要多加一个nginx,这样不是反而是系统的复杂度变高了么?

                            那接下来我们从两个方便给大家分析下这个问题,

                            第一个使用Nginx实现动静分离

                            第二个使用Nginx搭建Tomcat的集群

                            Nginx实现动静分离

                            什么是动静分离?

                            动:后台应用程序的业务处理

                            静:网站的静态资源(html,javaScript,css,images等文件)

                            分离:将两者进行分开部署访问,提供用户进行访问。举例说明就是以后所有和静态资源相关的内容都交给Nginx来部署访问,非静态内容则交个类似于Tomcat的服务器来部署访问。

                            为什么要动静分离?

                            前面我们介绍过Nginx在处理静态资源的时候,效率是非常高的,而且Nginx的并发访问量也是名列前茅,而Tomcat则相对比较弱一些,所以把静态资源交个Nginx后,可以减轻Tomcat服务器的访问压力并提高静态资源的访问速度。
                            动静分离以后,降低了动态资源和静态资源的耦合度。如动态资源宕机了也不影响静态资源的展示。
                            

                            如何实现动静分离?

                            实现动静分离的方式很多,比如静态资源可以部署到CDN、Nginx等服务器上,动态资源可以部署到Tomcat,weblogic或者websphere上。本次课程只要使用Nginx+Tomcat来实现动静分离。

                            需求分析

                            image.png

                            动静分离实现步骤

                            1.将demo.war项目中的静态资源都删除掉,重新打包生成一个war包,在资料中有提供。

                            2.将war包部署到tomcat中,把之前部署的内容删除掉

                            进入到tomcat的webapps目录下,将之前的内容删除掉
                            将新的war包复制到webapps下
                            将tomcat启动
                            

                            3.在Nginx所在服务器创建如下目录,并将对应的静态资源放入指定的位置

                            image.png

                            其中index.html页面的内容如下:

                            
                            
                            
                                
                                Title
                                
                                
                            
                            
                                
                                

                            Nginx如何将请求转发到后端服务器

                            4.配置Nginx的静态资源与动态资源的访问

                            upstream webservice{
                               server 192.168.200.146:8080;
                            }
                            server {
                                    listen       80;
                                    server_name  localhost;
                                    #动态资源
                                    location /demo {
                                            proxy_pass http://webservice;
                                    }
                                    #静态资源
                                    location ~/.*\.(png|jpg|gif|js){
                                            root html/web;
                                            gzip on;
                                    }
                                    location / {
                                        root   html/web;
                                        index  index.html index.htm;
                                    }
                            }
                            

                            5.启动测试,访问http://192.168.200.133/index.html

                            image.png

                            假如某个时间点,由于某个原因导致Tomcat后的服务器宕机了,我们再次访问Nginx,会得到如下效果,用户还是能看到页面,只是缺失了访问次数的统计,这就是前后端耦合度降低的效果,并且整个请求只和后的服务器交互了一次,js和images都直接从Nginx返回,提供了效率,降低了后的服务器的压力。

                            image.png

                            Nginx实现Tomcat集群搭建

                            在使用Nginx和Tomcat部署项目的时候,我们使用的是一台Nginx服务器和一台Tomcat服务器,效果图如下:

                            image.png

                            那么问题来了,如果Tomcat的真的宕机了,整个系统就会不完整,所以如何解决上述问题,一台服务器容易宕机,那就多搭建几台Tomcat服务器,这样的话就提升了后的服务器的可用性。这也就是我们常说的集群,搭建Tomcat的集群需要用到了Nginx的反向代理和赋值均衡的知识,具体如何来实现?我们先来分析下原理

                            image.png

                            环境准备:

                            (1)准备3台tomcat,使用端口进行区分[实际环境应该是三台服务器],修改server.ml,将端口修改分别修改为8080,8180,8280

                            (2)启动tomcat并访问测试,

                            http://192.168.200.146:8080/demo/getAddress
                            

                            image.png

                            http://192.168.200.146:8180/demo/getAddress
                            

                            image.png

                            http://192.168.200.146:8280/demo/getAddress
                            

                            image.png

                            (3)在Nginx对应的配置文件中添加如下内容:

                            upstream webservice{
                                    server 192.168.200.146:8080;
                                    server 192.168.200.146:8180;
                                    server 192.168.200.146:8280;
                                }
                            

                            好了,完成了上述环境的部署,我们已经解决了Tomcat的高可用性,一台服务器宕机,还有其他两条对外提供服务,同时也可以实现后台服务器的不间断更新。但是新问题出现了,上述环境中,如果是Nginx宕机了呢,那么整套系统都将服务对外提供服务了,这个如何解决?

                            Nginx高可用解决方案

                            针对于上面提到的问题,我们来分析下要想解决上述问题,需要面临哪些问题?

                            image.png

                            需要两台以上的Nginx服务器对外提供服务,这样的话就可以解决其中一台宕机了,另外一台还能对外提供服务,但是如果是两台Nginx服务器的话,会有两个IP地址,用户该访问哪台服务器,用户怎么知道哪台是好的,哪台是宕机了的?
                            

                            Keepalived

                            使用Keepalived来解决,Keepalived 软件由 C 编写的,最初是专为 LVS 负载均衡软件设计的,Keepalived 软件主要是通过 VRRP 协议实现高可用功能。

                            VRRP介绍

                            image.png

                            VRRP(Virtual Route Redundancy Protocol)协议,翻译过来为虚拟路由冗余协议。VRRP协议将两台或多台路由器设备虚拟成一个设备,对外提供虚拟路由器IP,而在路由器组内部,如果实际拥有这个对外IP的路由器如果工作正常的话就是MASTER,MASTER实现针对虚拟路由器IP的各种网络功能。其他设备不拥有该虚拟IP,状态为BACKUP,处了接收MASTER的VRRP状态通告信息以外,不执行对外的网络功能。当主机失效时,BACKUP将接管原先MASTER的网络功能。

                            从上面的介绍信息获取到的内容就是VRRP是一种协议,那这个协议是用来干什么的?

                            1.选择协议

                            VRRP可以把一个虚拟路由器的责任动态分配到局域网上的 VRRP 路由器中的一台。其中的虚拟路由即Virtual路由是由VRRP路由群组创建的一个不真实存在的路由,这个虚拟路由也是有对应的IP地址。而且VRRP路由1和VRRP路由2之间会有竞争选择,通过选择会产生一个Master路由和一个Backup路由。
                            

                            2.路由容错协议

                            Master路由和Backup路由之间会有一个心跳检测,Master会定时告知Backup自己的状态,如果在指定的时间内,Backup没有接收到这个通知内容,Backup就会替代Master成为新的Master。Master路由有一个特权就是虚拟路由和后端服务器都是通过Master进行数据传递交互的,而备份节点则会直接丢弃这些请求和数据,不做处理,只是去监听Master的状态
                            

                            用了Keepalived后,解决方案如下:

                            image.png

                            环境搭建

                            环境准备

                            VIPIP主机名主/从
                            192.168.200.133keepalived1Master
                            192.168.200.222
                            192.168.200.122keepalived2Backup

                            keepalived的安装

                            步骤1:从官方网站下载keepalived,官网地址https://keepalived.org/
                            步骤2:将下载的资源上传到服务器
                            	keepalived-2.0.20.tar.gz
                            步骤3:创建keepalived目录,方便管理资源
                            	mkdir keepalived
                            步骤4:将压缩文件进行解压缩,解压缩到指定的目录
                            	tar -zxf keepalived-2.0.20.tar.gz -C keepalived/
                            步骤5:对keepalived进行配置,编译和安装
                            	cd keepalived/keepalived-2.0.20
                            	./configure --sysconf=/etc --prefix=/usr/local
                            	make && make install
                            

                            安装完成后,有两个文件需要我们认识下,一个是 /etc/keepalived/keepalived.conf(keepalived的系统配置文件,我们主要操作的就是该文件),一个是/usr/local/sbin目录下的keepalived,是系统配置脚本,用来启动和关闭keepalived

                            Keepalived配置文件介绍

                            打开keepalived.conf配置文件

                            这里面会分三部,第一部分是global全局配置、第二部分是vrrp相关配置、第三部分是LVS相关配置。

                            本次课程主要是使用keepalived实现高可用部署,没有用到LVS,所以我们重点关注的是前两部分

                            global全局部分:
                            global_defs {
                               #通知邮件,当keepalived发送切换时需要发email给具体的邮箱地址
                               notification_email {
                                 tom@itcast.cn
                                 jerry@itcast.cn
                               }
                               #设置发件人的邮箱信息
                               notification_email_from zhaomin@itcast.cn
                               #指定smpt服务地址
                               smtp_server 192.168.200.1
                               #指定smpt服务连接超时时间
                               smtp_connect_timeout 30
                               #运行keepalived服务器的一个标识,可以用作发送邮件的主题信息
                               router_id LVS_DEVEL
                               
                               #默认是不跳过检查。检查收到的VRRP通告中的所有地址可能会比较耗时,设置此命令的意思是,如果通告与接收的上一个通告来自相同的master路由器,则不执行检查(跳过检查)
                               vrrp_skip_check_adv_addr
                               #严格遵守VRRP协议。
                               vrrp_strict
                               #在一个接口发送的两个免费ARP之间的延迟。可以精确到毫秒级。默认是0
                               vrrp_garp_interval 0
                               #在一个网卡上每组na消息之间的延迟时间,默认为0
                               vrrp_gna_interval 0
                            }
                            
                            VRRP部分,该部分可以包含以下四个子模块
                            1. vrrp_script
                            2. vrrp_sync_group
                            3. garp_group
                            4. vrrp_instance
                            我们会用到第一个和第四个,
                            #设置keepalived实例的相关信息,VI_1为VRRP实例名称
                            vrrp_instance VI_1 {
                                state MASTER  		#有两个值可选MASTER主 BACKUP备
                                interface ens33		#vrrp实例绑定的接口,用于发送VRRP包[当前服务器使用的网卡名称]
                                virtual_router_id 51#指定VRRP实例ID,范围是0-255
                                priority 100		#指定优先级,优先级高的将成为MASTER
                                advert_int 1		#指定发送VRRP通告的间隔,单位是秒
                                authentication {	#vrrp之间通信的认证信息
                                    auth_type PASS	#指定认证方式。PASS简单密码认证(推荐)
                                    auth_pass 1111	#指定认证使用的密码,最多8位
                                }
                                virtual_ipaddress { #虚拟IP地址设置虚拟IP地址,供用户访问使用,可设置多个,一行一个
                                    192.168.200.222
                                }
                            }
                            

                            配置内容如下:

                            服务器1

                            global_defs {
                               notification_email {
                                    tom@itcast.cn
                                    jerry@itcast.cn
                               }
                               notification_email_from zhaomin@itcast.cn
                               smtp_server 192.168.200.1
                               smtp_connect_timeout 30
                               router_id keepalived1
                               vrrp_skip_check_adv_addr
                               vrrp_strict
                               vrrp_garp_interval 0
                               vrrp_gna_interval 0
                            }
                            vrrp_instance VI_1 {
                                state MASTER
                                interface ens33
                                virtual_router_id 51
                                priority 100
                                advert_int 1
                                authentication {
                                    auth_type PASS
                                    auth_pass 1111
                                }
                                virtual_ipaddress {
                                    192.168.200.222
                                }
                            }
                            

                            服务器2

                            ! Configuration File for keepalived
                            global_defs {
                               notification_email {
                                    tom@itcast.cn
                                    jerry@itcast.cn
                               }
                               notification_email_from zhaomin@itcast.cn
                               smtp_server 192.168.200.1
                               smtp_connect_timeout 30
                               router_id keepalived2
                               vrrp_skip_check_adv_addr
                               vrrp_strict
                               vrrp_garp_interval 0
                               vrrp_gna_interval 0
                            }
                            vrrp_instance VI_1 {
                                state BACKUP
                                interface ens33
                                virtual_router_id 51
                                priority 90
                                advert_int 1
                                authentication {
                                    auth_type PASS
                                    auth_pass 1111
                                }
                                virtual_ipaddress {
                                    192.168.200.222
                                }
                            }
                            

                            访问测试

                            1. 启动keepalived之前,咱们先使用命令 ip a,查看192.168.200.133和192.168.200.122这两台服务器的IP情况。

                            image.png

                            1. 分别启动两台服务器的keepalived
                            cd /usr/local/sbin
                            ./keepalived
                            

                            再次通过 ip a查看ip

                            image.png

                            1. 当把192.168.200.133服务器上的keepalived关闭后,再次查看ip

                            image.png

                            通过上述的测试,我们会发现,虚拟IP(VIP)会在MASTER节点上,当MASTER节点上的keepalived出问题以后,因为BACKUP无法收到MASTER发出的VRRP状态通过信息,就会直接升为MASTER。VIP也会"漂移"到新的MASTER。

                            上面测试和Nginx有什么关系?

                            我们把192.168.200.133服务器的keepalived再次启动下,由于它的优先级高于服务器192.168.200.122的,所有它会再次成为MASTER,VIP也会"漂移"过去,然后我们再次通过浏览器访问:

                            http://192.168.200.222/
                            

                            image.png

                            如果把192.168.200.133服务器的keepalived关闭掉,再次访问相同的地址

                            image.png

                            效果实现了以后, 我们会发现要想让vip进行切换,就必须要把服务器上的keepalived进行关闭,而什么时候关闭keepalived呢?应该是在keepalived所在服务器的nginx出现问题后,把keepalived关闭掉,就可以让VIP执行另外一台服务器,但是现在这所有的操作都是通过手动来完成的,我们如何能让系统自动判断当前服务器的nginx是否正确启动,如果没有,要能让VIP自动进行"漂移",这个问题该如何解决?

                            keepalived之vrrp_script

                            keepalived只能做到对网络故障和keepalived本身的监控,即当出现网络故障或者keepalived本身出现问题时,进行切换。但是这些还不够,我们还需要监控keepalived所在服务器上的其他业务,比如Nginx,如果Nginx出现异常了,仅仅keepalived保持正常,是无法完成系统的正常工作的,因此需要根据业务进程的运行状态决定是否需要进行主备切换,这个时候,我们可以通过编写脚本对业务进程进行检测监控。

                            实现步骤:

                            1. 在keepalived配置文件中添加对应的配置像
                            vrrp_script 脚本名称
                            {
                                script "脚本位置"
                                interval 3 #执行时间间隔
                                weight -20 #动态调整vrrp_instance的优先级
                            }
                            
                            1. 编写脚本

                            ck_nginx.sh

                            #!/bin/bash
                            num=`ps -C nginx --no-header | wc -l`
                            if [ $num -eq 0 ];then
                             /usr/local/nginx/sbin/nginx
                             sleep 2
                             if [ `ps -C nginx --no-header | wc -l` -eq 0 ]; then
                              killall keepalived
                             fi
                            fi
                            

                            Linux ps命令用于显示当前进程 (process) 的状态。

                            -C(command) :指定命令的所有进程

                            –no-header 排除标题

                            1. 为脚本文件设置权限
                            chmod 755 ck_nginx.sh
                            
                            1. 将脚本添加到
                            vrrp_script ck_nginx {
                               script "/etc/keepalived/ck_nginx.sh" #执行脚本的位置
                               interval 2		#执行脚本的周期,秒为单位
                               weight -20		#权重的计算方式
                            }
                            vrrp_instance VI_1 {
                                state MASTER
                                interface ens33
                                virtual_router_id 10
                                priority 100
                                advert_int 1
                                authentication {
                                    auth_type PASS
                                    auth_pass 1111
                                }
                                virtual_ipaddress {
                                    192.168.200.111
                                }
                                track_script {
                                  ck_nginx
                                }
                            }
                            
                            1. 如果效果没有出来,可以使用 tail -f /var/log/messages查看日志信息,找对应的错误信息。
                            2. 测试

                            问题思考:

                            通常如果master服务死掉后backup会变成master,但是当master服务又好了的时候 master此时会抢占VIP,这样就会发生两次切换对业务繁忙的网站来说是不好的。所以我们要在配置文件加入 nopreempt 非抢占,但是这个参数只能用于state 为backup,故我们在用HA的时候最好master 和backup的state都设置成backup 让其通过priority来竞争。

                            Nginx制作下载站点

                            首先我们先要清楚什么是下载站点?

                            我们先来看一个网站http://nginx.org/download/这个我们刚开始学习Nginx的时候给大家看过这样的网站,该网站主要就是用来提供用户来下载相关资源的网站,就叫做下载网站。

                            image.png

                            如何制作一个下载站点:

                            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及以后版本中出现

                            (4)autoindex_localtime:对应HTML格式,是否在目录列表上显示时间。

                            默认为off,显示的文件时间为GMT时间。

                            改为on后,显示的文件时间为文件的服务器时间

                            语法autoindex_localtime on | off;
                            默认值autoindex_localtime off;
                            位置http、server、location

                            配置方式如下:

                            location /download{
                                root /usr/local;
                                autoindex on;
                                autoindex_exact_size on;
                                autoindex_format html;
                                autoindex_localtime on;
                            }
                            

                            XML/JSON格式[一般不用这两种方式]

                            image.png

                            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

                            指定文件路径,该文件中的用户名和密码的设置,密码需要进行加密。可以采用工具自动生成

                            实现步骤:

                            1.nginx.conf添加如下内容

                            location /download{
                                root /usr/local;
                                autoindex on;
                                autoindex_exact_size on;
                                autoindex_format html;
                                autoindex_localtime on;
                                auth_basic 'please input your auth';
                                auth_basic_user_file htpasswd;
                            }
                            

                            2.我们需要使用htpasswd工具生成

                            yum install -y httpd-tools
                            
                            htpasswd -c /usr/local/nginx/conf/htpasswd username //创建一个新文件记录用户名和密码
                            htpasswd -b /usr/local/nginx/conf/htpasswd username password //在指定文件新增一个用户名和密码
                            htpasswd -D /usr/local/nginx/conf/htpasswd username //从指定文件删除一个用户信息
                            htpasswd -v /usr/local/nginx/conf/htpasswd username //验证用户名和密码是否正确
                            

                            image.png

                            上述方式虽然能实现用户名和密码的验证,但是大家也看到了,所有的用户名和密码信息都记录在文件里面,如果用户量过大的话,这种方式就显得有点麻烦了,这时候我们就得通过后台业务代码来进行用户权限的校验了。

                            Nginx的扩展模块

                            Nginx是可扩展的,可用于处理各种使用场景。本节中,我们将探讨使用Lua扩展Nginx的功能。

                            Lua

                            概念

                            Lua是一种轻量、小巧的脚本语言,用标准C语言编写并以源代码形式开发。设计的目的是为了嵌入到其他应用程序中,从而为应用程序提供灵活的扩展和定制功能。

                            特性

                            跟其他语言进行比较,Lua有其自身的特点:

                            (1)轻量级

                            Lua用标准C语言编写并以源代码形式开发,编译后仅仅一百余千字节,可以很方便的嵌入到其他程序中。
                            

                            (2)可扩展

                            Lua提供非常丰富易于使用的扩展接口和机制,由宿主语言(通常是C或C++)提供功能,Lua可以使用它们,就像内置的功能一样。
                            

                            (3)支持面向过程编程和函数式编程

                            应用场景

                            Lua在不同的系统中得到大量应用,场景的应用场景如下:

                            游戏开发、独立应用脚本、web应用脚本、扩展和数据库插件、系统安全上。

                            Lua的安装

                            在linux上安装Lua非常简单,只需要下载源码包并在终端解压、编译即可使用。

                            Lua的官网地址为:https://www.lua.org

                            image.png

                            1. 点击download可以找到对应版本的下载地址,我们本次课程采用的是lua-5.3.5,其对应的资源链接地址为https://www.lua.org/ftp/lua-5.4.1.tar.gz,也可以使用wget命令直接下载:
                            wget https://www.lua.org/ftp/lua-5.4.1.tar.gz
                            
                            1. 编译安装
                            cd lua-5.4.1
                            make linux test
                            make install
                            

                            如果在执行make linux test失败,报如下错误:

                            image.png

                            说明当前系统缺少libreadline-dev依赖包,需要通过命令来进行安装

                            yum install -y readline-devel
                            

                            验证是否安装成功

                            lua -v
                            

                            Lua的语法

                            Lua和C/C语法非常相似,整体上比较清晰,简洁。条件语句、循环语句、函数调用都与C/C基本一致。如果对C/C++不太熟悉的同学来说,也没关系,因为天下语言是一家,基本上理解起来都不会太困难。我们一点点来讲。

                            第一个Lua程序

                            大家需要知道的是,Lua有两种交互方式,分别是:交互式和脚本式,这两者的区别,下面我们分别来讲解下:

                            交互式之HELLOWORLD

                            交互式是指可以在命令行输入程序,然后回车就可以看到运行的效果。
                            

                            Lua交互式编程模式可以通过命令lua -i 或lua来启用:

                            image.png

                            在命令行中key输入如下命令,并按回车,会有输出在控制台:

                            image.png

                            脚本式之HELLOWORLD

                            脚本式是将代码保存到一个以lua为扩展名的文件中并执行的方式。

                            方式一:

                            我们需要一个文件名为 hello.lua,在文件中添加要执行的代码,然后通过命令 lua hello.lua来执行,会在控制台输出对应的结果。

                            hello.lua

                            print("Hello World!!")
                            

                            image.png

                            方式二:

                            将hello.lua做如下修改

                            #!/usr/local/bin/lua
                            print("Hello World!!!")
                            

                            第一行用来指定Lua解释器所在位置为 /usr/local/bin/lua,加上#号标记解释器会忽略它。一般情况下#!就是用来指定用哪个程序来运行本文件。但是hello.lua并不是一个可执行文件,需要通过chmod来设置可执行权限,最简单的方式为:

                            chmod 755 hello.lua
                            

                            然后执行该文件

                            ./hello.lua
                            

                            image.png

                            补充一点,如果想在交互式中运行脚本式的hello.lua中的内容,我们可以使用一个dofile函数,如:

                            dofile("lua_demo/hello.lua")
                            

                            注意:在Lua语言中,连续语句之间的分隔符并不是必须的,也就是说后面不需要加分号,当然加上也不会报错,

                            在Lua语言中,表达式之间的换行也起不到任何作用。如以下四个写法,其实都是等效的

                            写法一
                            a=1
                            b=a+2
                            写法二
                            a=1;
                            b=a+2;
                            写法三
                            a=1; b=a+2;
                            写法四
                            a=1 b=a+2
                            

                            不建议使用第四种方式,可读性太差。

                            Lua的注释

                            关于Lua的注释要分两种,第一种是单行注释,第二种是多行注释。

                            单行注释的语法为:

                            --注释内容
                            

                            多行注释的语法为:

                            --[[
                            	注释内容
                            	注释内容
                            --]]
                            

                            如果想取消多行注释,只需要在第一个–之前在加一个-即可,如:

                            ---[[
                            	注释内容
                            	注释内容
                            --]]
                            
                            标识符

                            换句话说标识符就是我们的变量名,Lua定义变量名以一个字母 A 到 Z 或 a 到 z 或下划线 _ 开头后加上0个或多个字母,下划线,数字(0到9)。这块建议大家最好不要使用下划线加大写字母的标识符,因为Lua的保留字也是这样定义的,容易发生冲突。注意Lua是区分大小写字母的。

                            A0

                            关键字

                            下列是Lua的关键字,大家在定义常量、变量或其他用户自定义标识符都要避免使用以下这些关键字:

                            andbreakdoelse
                            elseifendfalsefor
                            functionifinlocal
                            nilnotorrepeat
                            returnthentrueuntil
                            whilegoto

                            一般约定,以下划线开头连接一串大写字母的名字(比如 _VERSION)被保留用于 Lua 内部全局变量。这个也是上面我们不建议这么定义标识符的原因。

                            运算符

                            Lua中支持的运算符有算术运算符、关系运算符、逻辑运算符、其他运算符。

                            算术运算符:

                            +   加法
                            -	减法
                            *	乘法
                            /	除法
                            %	取余
                            ^	乘幂
                            -	负号
                            

                            例如:

                            10+20	-->30
                            20-10	-->10
                            10*20	-->200
                            20/10	-->2
                            3%2		-->1
                            10^2	-->100
                            -10		-->-10
                            

                            关系运算符

                            ==	等于
                            ~=	不等于
                            >	大于
                            <	小于
                            >=	大于等于
                            <=	小于等于
                            

                            例如:

                            10==10		-->true
                            10~=10		-->false
                            20>10		-->true
                            20<10		-->false
                            20>=10		-->true
                            20<=10		-->false
                            

                            逻辑运算符

                            and	逻辑与	 A and B     &&   
                            or	逻辑或	 A or B     ||
                            not	逻辑非  取反,如果为true,则返回false  !
                            

                            逻辑运算符可以作为if的判断条件,返回的结果如下:

                            A = true
                            B = true
                            A and B	-->true
                            A or  B -->true
                            not A 	-->false
                            A = true
                            B = false
                            A and B	-->false
                            A or  B -->true
                            not A 	-->false
                            A = false
                            B = true
                            A and B	-->false
                            A or  B -->true
                            not A 	-->true
                            

                            其他运算符

                            ..	连接两个字符串
                            #	一元预算法,返回字符串或表的长度
                            

                            例如:

                            > "HELLO ".."WORLD"		-->HELLO WORLD
                            > #"HELLO"			-->5
                            
                            全局变量&局部变量

                            在Lua语言中,全局变量无须声明即可使用。在默认情况下,变量总是认为是全局的,如果未提前赋值,默认为nil:

                            image.png

                            要想声明一个局部变量,需要使用local来声明

                            image.png

                            Lua数据类型

                            Lua有8个数据类型

                            nil(空,无效值)
                            boolean(布尔,true/false)
                            number(数值)
                            string(字符串)
                            function(函数)
                            table(表)
                            thread(线程)
                            userdata(用户数据)
                            

                            可以使用type函数测试给定变量或者的类型:

                            print(type(nil))				-->nil
                            print(type(true))               --> boolean
                            print(type(1.1*1.1))             --> number
                            print(type("Hello world"))      --> string
                            print(type(io.stdin))			-->userdata
                            print(type(print))              --> function
                            print(type(type))               -->function
                            print(type{})					-->table
                            print(type(type(X)))            --> string
                            
                            nil

                            nil是一种只有一个nil值的类型,它的作用可以用来与其他所有值进行区分,也可以当想要移除一个变量时,只需要将该变量名赋值为nil,垃圾回收就会会释放该变量所占用的内存。

                            boolean

                            boolean类型具有两个值,true和false。boolean类型一般被用来做条件判断的真与假。在Lua语言中,只会将false和nil视为假,其他的都视为真,特别是在条件检测中0和空字符串都会认为是真,这个和我们熟悉的大多数语言不太一样。

                            number

                            在Lua5.3版本开始,Lua语言为数值格式提供了两种选择:integer(整型)和float(双精度浮点型)[和其他语言不太一样,float不代表单精度类型]。

                            数值常量的表示方式:

                            >4			-->4
                            >0.4		-->0.4
                            >4.75e-3	-->0.00475
                            >4.75e3		-->4750
                            

                            不管是整型还是双精度浮点型,使用type()函数来取其类型,都会返回的是number

                            >type(3)	-->number
                            >type(3.3)	-->number
                            

                            所以它们之间是可以相互转换的,同时,具有相同算术值的整型值和浮点型值在Lua语言中是相等的

                            string

                            Lua语言中的字符串即可以表示单个字符,也可以表示一整本书籍。在Lua语言中,操作100K或者1M个字母组成的字符串的程序很常见。

                            可以使用单引号或双引号来声明字符串

                            >a = "hello"
                            >b = 'world'
                            >print(a)	-->hello
                            >print(b) 	-->world
                            

                            如果声明的字符串比较长或者有多行,则可以使用如下方式进行声明

                            html = [[
                            
                            
                            Lua-string
                            
                            
                            Lua
                            
                            
                            ]]
                            
                            table
                            table是Lua语言中最主要和强大的数据结构。使用表, Lua 语言可以以一种简单、统一且高效的方式表示数组、集合、记录和其他很多数据结构。 Lua语言中的表本质上是一种辅助数组。这种数组比Java中的数组更加灵活,可以使用数值做索引,也可以使用字符串或其他任意类型的值作索引(除nil外)。
                            

                            创建表的最简单方式:

                            > a = {}
                            

                            创建数组:

                            我们都知道数组就是相同数据类型的元素按照一定顺序排列的集合,那么使用table如何创建一个数组呢?
                            
                            >arr = {"TOM","JERRY","ROSE"}
                            
                            要想获取数组中的值,我们可以通过如下内容来获取:
                            
                            print(arr[0])		nil
                            print(arr[1])		TOM
                            print(arr[2])		JERRY
                            print(arr[3])		ROSE
                            
                            从上面的结果可以看出来,数组的下标默认是从1开始的。所以上述创建数组,也可以通过如下方式来创建
                            
                            >arr = {}
                            >arr[1] = "TOM"
                            >arr[2] = "JERRY"
                            >arr[3] = "ROSE"
                            

                            上面我们说过了,表的索引即可以是数字,也可以是字符串等其他的内容,所以我们也可以将索引更改为字符串来创建

                            >arr = {}
                            >arr["X"] = 10
                            >arr["Y"] = 20
                            >arr["Z"] = 30
                            

                            当然,如果想要获取这些数组中的值,可以使用下面的方式

                            方式一
                            >print(arr["X"])
                            >print(arr["Y"])
                            >print(arr["Z"])
                            方式二
                            >print(arr.X)
                            >print(arr.Y)
                            >print(arr.Z)
                            

                            当前table的灵活不进于此,还有更灵活的声明方式

                            >arr = {"TOM",X=10,"JERRY",Y=20,"ROSE",Z=30}
                            

                            如何获取上面的值?

                            TOM :  arr[1]
                            10  :  arr["X"] | arr.X
                            JERRY: arr[2]
                            20  :  arr["Y"] | arr.Y
                            ROESE?
                            
                            function

                            在 Lua语言中,函数( Function )是对语句和表达式进行抽象的主要方式。

                            定义函数的语法为:

                            function functionName(params)
                            end
                            

                            函数被调用的时候,传入的参数个数与定义函数时使用的参数个数不一致的时候,Lua 语言会通过 抛弃多余参数和将不足的参数设为 nil 的方式来调整参数的个数。

                            function  f(a,b)
                            print(a,b)
                            end
                            f()		--> nil  nil
                            f(2)	--> 2 nil
                            f(2,6)	--> 2 6
                            f(2.6.8)	--> 2 6 (8被丢弃)
                            

                            可变长参数函数

                            function add(...)
                            a,b,c=...
                            print(a)
                            print(b)
                            print(c)
                            end
                            add(1,2,3)  --> 1 2 3
                            

                            函数返回值可以有多个,这点和Java不太一样

                            function f(a,b)
                            return a,b
                            end
                            x,y=f(11,22)	--> x=11,y=22
                            
                            thread

                            thread翻译过来是线程的意思,在Lua中,thread用来表示执行的独立线路,用来执行协同程序。

                            userdata

                            userdata是一种用户自定义数据,用于表示一种由应用程序或C/C++语言库所创建的类型。

                            Lua控制结构

                            Lua 语言提供了一组精简且常用的控制结构,包括用于条件执行的证 以及用于循环的 while、 repeat 和 for。 所有的控制结构语法上都有一个显式的终结符: end 用于终结 if、 for 及 while 结构, until 用于终结 repeat 结构。

                            if then elseif else

                            if语句先测试其条件,并根据条件是否满足执行相应的 then 部分或 else 部分。 else 部分 是可选的。

                            function testif(a)
                             if a>0 then
                             	print("a是正数")
                             end
                            end
                            function testif(a)
                             if a>0 then
                             	print("a是正数")
                             else
                             	print("a是负数")
                             end
                            end
                            

                            如果要编写嵌套的 if 语句,可以使用 elseif。 它类似于在 else 后面紧跟一个if。根据传入的年龄返回不同的结果,如

                            age<=18 青少年,
                            age>18 , age <=45 青年
                            age>45 , age<=60 中年人
                            age>60 老年人
                            function show(age)
                            if age<=18 then
                             return "青少年"
                            elseif age>18 and age<=45 then
                             return "青年"
                            elseif age>45 and age<=60 then
                             return "中年人"
                            elseif age>60 then
                             return "老年人"
                            end
                            end
                            
                            while循环

                            顾名思义,当条件为真时 while 循环会重复执行其循环体。 Lua 语言先测试 while 语句 的条件,若条件为假则循环结束;否则, Lua 会执行循环体并不断地重复这个过程。

                            语法:

                            while 条件 do
                              循环体
                            end
                            

                            例子:实现数组的循环

                            function testWhile()
                             local i = 1
                             while i<=10 do
                              print(i)
                              i=i+1
                             end
                            end
                            
                            repeat循环

                            顾名思义, repeat-until语句会重复执行其循环体直到条件为真时结束。 由于条件测试在循环体之后执行,所以循环体至少会执行一次。

                            语法

                            repeat
                             循环体
                             until 条件
                            
                            function testRepeat()
                             local i = 10
                             repeat
                              print(i)
                              i=i-1
                             until i < 1
                            end
                            
                            for循环

                            数值型for循环

                            语法

                            for param=exp1,exp2,exp3 do
                             循环体
                            end
                            

                            param的值从exp1变化到exp2之前的每次循环会执行 循环体,并在每次循环结束后将步长(step)exp3增加到param上。exp3可选,如果不设置默认为1

                            for i = 1,100,10 do
                            print(i)
                            end
                            

                            泛型for循环

                            泛型for循环通过一个迭代器函数来遍历所有值,类似于java中的foreach语句。

                            语法

                            for i,v in ipairs(x) do
                            	循环体
                            end
                            

                            i是数组索引值,v是对应索引的数组元素值,ipairs是Lua提供的一个迭代器函数,用来迭代数组,x是要遍历的数组。

                            例如:

                            arr = {"TOME","JERRY","ROWS","LUCY"}
                            for i,v in ipairs(arr) do
                             print(i,v)
                            end
                            

                            上述实例输出的结果为

                            1	TOM
                            2	JERRY
                            3	ROWS
                            4	LUCY
                            

                            但是如果将arr的值进行修改为

                            arr = {"TOME","JERRY","ROWS",x="JACK","LUCY"}
                            

                            同样的代码在执行的时候,就只能看到和之前一样的结果,而其中的x为JACK就无法遍历出来,缺失了数据,如果解决呢?

                            我们可以将迭代器函数变成pairs,如

                            for i,v in pairs(arr) do
                             print(i,v)
                            end
                            

                            上述实例就输出的结果为

                            1	TOM
                            2	JERRY
                            3	ROWS
                            4	LUCY
                            x	JACK
                            

                            ngx_lua模块概念

                            淘宝开发的ngx_lua模块通过将lua解释器集成进Nginx,可以采用lua脚本实现业务逻辑,由于lua的紧凑、快速以及内建协程,所以在保证高并发服务能力的同时极大地降低了业务逻辑实现成本。

                            ngx_lua模块环境准备

                            方式一:lua-nginx-module

                            1. LuaJIT是采用C语言编写的Lua代表的解释器。

                            官网地址为:http://luajit.org/

                            在官网上找到对应的下载地址:http://luajit.org/download/LuaJIT-2.0.5.tar.gz

                            在centos上使用wget来下载: wget http://luajit.org/download/LuaJIT-2.0.5.tar.gz

                            将下载的资源进行解压: tar -zxf LuaJIT-2.0.5.tar.gz

                            进入解压的目录: cd LuaJIT-2.0.5

                            执行编译和安装: make && make install

                            image.png

                            1. 下载lua-nginx-module

                            下载地址:https://github.com/openresty/lua-nginx-module/archive/v0.10.16rc4.tar.gz

                            在centos上使用wget来下载: wget https://github.com/openresty/lua-nginx-module/archive/v0.10.16rc4.tar.gz

                            将下载的资源进行解压: tar -zxf lua-nginx-module-0.10.16rc4.tar.gz

                            更改目录名:mv lua-nginx-module-0.10.16rc4 lua-nginx-module

                            导入环境变量,告诉Nginx去哪里找luajit

                            export LUAJIT_LIB=/usr/local/lib
                            export LUAJIT_INC=/usr/local/include/luajit-2.0
                            

                            进入Nginx的目录执行如下命令:

                            ./configure --prefix=/usr/local/nginx --add-module=../lua-nginx-module
                            make && make install
                            

                            注意事项:

                            (1)如果启动Nginx出现如下错误:

                            image.png

                            解决方案:

                            设置软链接,使用如下命令

                             ln -s /usr/local/lib/libluajit-5.1.so.2 /lib64/libluajit-5.1.so.2
                            

                            (2)如果启动Nginx出现以下错误信息

                            image.png

                            分析原因:因为lua-nginx-module是来自openrestry,错误中提示的resty.core是openrestry的核心模块,对其下的很多函数进行了优化等工作。以前的版本默认不会把该模块编译进去,所以需要使用的话,我们得手动安装,或者禁用就可以。但是最新的lua-nginx-module模块已经强制性安装了该模块,所以此处因为缺少resty模块导致的报错信息。

                            解决方案有两个:一种是下载对应的模块,另一种则是禁用掉restry模块,禁用的方式为:

                            http{
                            	lua_load_resty_core off;
                            }
                            
                            1. 测试

                            在nginx.conf下配置如下内容:

                            location /lua{
                                default_type 'text/html';
                                content_by_lua 'ngx.say("

                            HELLO,LUA

                            ")'; }

                            配置成功后,启动nginx,通过浏览器进行访问,如果获取到如下结果,则证明安装成功。

                            image.png

                            方式二:OpenRestry

                            概述
                            前面我们提到过,OpenResty是由淘宝工程师开发的,所以其官方网站([http://openresty.org/](http://openresty.org/))我们读起来是非常的方便。OpenResty是一个基于Nginx与 Lua 的高性能 Web 平台,其内部集成了大量精良的 Lua 库、第三方模块以及大多数的依赖项。用于方便地搭建能够处理超高并发、扩展性极高的动态 Web 应用、Web 服务和动态网关。所以本身OpenResty内部就已经集成了Nginx和Lua,所以我们使用起来会更加方便。
                            
                            安装
                            (1) 下载OpenResty:https://openresty.org/download/openresty-1.15.8.2.tar.gz
                            (2)使用wget下载: wget https://openresty.org/download/openresty-1.15.8.2.tar.gz
                            (3)解压缩: tar -zxf openresty-1.15.8.2.tar.gz
                            (4)进入OpenResty目录: cd openresty-1.15.8.2
                            (5) 执行命令:./configure
                            (6) 执行命令:make && make install
                            (7)进入OpenResty的目录,找到nginx:cd /usr/local/openresty/nginx/
                            (8)在conf目录下的nginx.conf添加如下内容
                            location /lua{
                                default_type 'text/html';
                                content_by_lua 'ngx.say("

                            HELLO,OpenRestry

                            ")'; } (9)在sbin目录下启动nginx (10)通过浏览器访问测试

                            image.png

                            image.png

                            ngx_lua的使用

                            使用Lua编写Nginx脚本的基本构建块是指令。指令用于指定何时运行用户Lua代码以及如何使用结果。下图显示了执行指令的顺序。

                            image.png

                            先来解释下*的作用

                            *:无 , 即 xxx_by_lua ,指令后面跟的是 lua指令
                            *:_file,即 xxx_by_lua_file 指令后面跟的是 lua文件
                            *:_block,即 xxx_by_lua_block 在0.9.17版后替换init_by_lua_file
                            

                            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代码。
                            

                            需求:

                            http://192.168.200.133?name=张三&gender=1
                            Nginx接收到请求后,根据gender传入的值,如果gender传入的是1,则在页面上展示
                            张三先生,如果gender传入的是0,则在页面上展示张三女士,如果未传或者传入的不是1和2则在页面上展示张三。
                            

                            实现代码

                            location /getByGender {
                            	default_type 'text/html';
                            	set_by_lua $name "
                            		local uri_args = ngx.req.get_uri_args()
                            		gender = uri_args['gender']
                            		name = uri_args['name']
                            		if gender=='1' then
                            			return name..'先生'
                            		elseif gender=='0' then
                            			return name..'女士'
                            		else
                            			return name
                            		end
                            	";
                            	header_filter_by_lua "
                            		ngx.header.aaa='bbb'
                            	";
                            	return 200 $name;
                            }
                            

                            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环境

                            连接地址
                            host= 192.168.200.111
                            port=6379
                            

                            image.png

                            步骤二:准备对应的API

                            lua-resty-redis提供了访问Redis的详细API,包括创建对接、连接、操作、数据处理等。这些API基本上与Redis的操作一一对应。
                            (1)redis = require "resty.redis"
                            (2)new
                            	语法: redis,err = redis:new(),创建一个Redis对象。
                            (3)connect
                            	语法:ok,err=redis:connect(host,port[,options_table]),设置连接Redis的连接信息。
                            	ok:连接成功返回 1,连接失败返回nil
                            	err:返回对应的错误信息
                            (4)set_timeout
                            	语法: redis:set_timeout(time) ,设置请求操作Redis的超时时间。
                            (5)close
                            	语法: ok,err = redis:close(),关闭当前连接,成功返回1,失败返回nil和错误信息
                            (6)redis命令对应的方法
                            	在lua-resty-redis中,所有的Redis命令都有自己的方法,方法名字和命令名字相同,只是全部为小写。
                            

                            步骤三:效果实现

                            location / {
                                default_type "text/html";
                                content_by_lua_block{
                                    local redis = require "resty.redis" -- 引入Redis
                                    local redisObj = redis:new()  --创建Redis对象
                                    redisObj:set_timeout(1000) --设置超时数据为1s
                                    local ok,err = redisObj:connect("192.168.200.1",6379) --设置redis连接信息
                                    if not ok then --判断是否连接成功
                                     ngx.say("failed to connection redis",err)
                                     return
                                    end
                                    ok,err = redisObj:set("username","TOM")--存入数据
                                    if not ok then --判断是否存入成功
                                     ngx.say("failed to set username",err)
                                     return
                                    end
                                    local res,err = redisObj:get("username") --从redis中获取数据
                                    ngx.say(res)	--将数据写会消息体中
                                    redisObj:close()
                                }
                            }
                            

                            步骤四:运行测试效果

                            image.png

                            image.png

                            ngx_lua操作Mysql

                            MySQL是一个使用广泛的关系型数据库。在ngx_lua中,MySQL有两种访问模式,分别是使

                            (1)用ngx_lua模块和lua-resty-mysql模块:这两个模块是安装OpenResty时默认安装的。

                            (2)使用drizzle_nginx_module(HttpDrizzleModule)模块:需要单独安装,这个库现不在OpenResty中。

                            lua-resty-mysql

                            lua-resty-mysql是OpenResty开发的模块,使用灵活、功能强大,适合复杂的业务场景,同时支持存储过程的访问。

                            使用lua-resty-mysql实现数据库的查询

                            步骤一:

                            准备MYSQL

                            host: 192.168.200.111
                            port: 3306
                            username:root
                            password:123456
                            

                            创建一个数据库表及表中的数据。

                            create database nginx_db;
                            use nginx_db;
                            create table users(
                               id int primary key auto_increment,
                               username varchar(30),
                               birthday date,
                               salary double
                            );
                            insert into users(id,username,birthday,salary) values(null,"TOM","1988-11-11",10000.0);
                            insert into users(id,username,birthday,salary) values(null,"JERRY","1989-11-11",20000.0);
                            insert into users(id,username,birthday,salary) values(null,"ROWS","1990-11-11",30000.0);
                            insert into users(id,username,birthday,salary) values(null,"LUCY","1991-11-11",40000.0);
                            insert into users(id,username,birthday,salary) values(null,"JACK","1992-11-11",50000.0);
                            

                            数据库连接四要素:

                            driverClass=com.mysql.jdbc.Driver
                            url=jdbc:mysql://192.168.200.111:3306/nginx_db
                            username=root
                            password=123456
                            

                            步骤二:API学习

                            (1)引入"resty.mysql"模块
                            	local mysql = require "resty.mysql"
                            (2)new
                            	创建一个MySQL连接对象,遇到错误时,db为nil,err为错误描述信息
                            	语法: db,err = mysql:new()
                            (3)connect
                            	尝试连接到一个MySQL服务器
                            	语法:ok,err=db:connect(options),options是一个参数的Lua表结构,里面包含数据库连接的相关信息
                                host:服务器主机名或IP地址
                                port:服务器监听端口,默认为3306
                                user:登录的用户名
                                password:登录密码
                                database:使用的数据库名
                            (4)set_timeout
                            	设置子请求的超时时间(ms),包括connect方法
                            	语法:db:set_timeout(time)
                            (5)close
                            	关闭当前MySQL连接并返回状态。如果成功,则返回1;如果出现任何错误,则将返回nil和错误描述。
                            	语法:db:close()
                            (6)send_query
                            	异步向远程MySQL发送一个查询。如果成功则返回成功发送的字节数;如果错误,则返回nil和错误描述
                            	语法:bytes,err=db:send_query(sql)
                            (7)read_result
                            	从MySQL服务器返回结果中读取一行数据。res返回一个描述OK包或结果集包的Lua表,语法:
                            	res, err, errcode, sqlstate = db:read_result() 
                            	res, err, errcode, sqlstate = db:read_result(rows) :rows指定返回结果集的最大值,默认为4
                            	如果是查询,则返回一个容纳多行的数组。每行是一个数据列的key-value对,如
                                {
                                  {id=1,username="TOM",birthday="1988-11-11",salary=10000.0},
                                  {id=2,username="JERRY",birthday="1989-11-11",salary=20000.0}
                                }
                            	如果是增删改,则返回类上如下数据
                                {
                                	insert_id = 0,
                                	server_status=2,
                                	warning_count=1,
                                	affected_rows=2,
                                	message=nil
                                }
                            	返回值:
                            		res:操作的结果集
                            		err:错误信息
                            		errcode:MySQL的错误码,比如1064
                            		sqlstate:返回由5个字符组成的标准SQL错误码,比如42000
                            

                            步骤三:效果实现

                            location /{
                                content_by_lua_block{
                                    local mysql = require "resty.mysql"
                                    local db = mysql:new()
                                    local ok,err = db:connect{
                                        host="192.168.200.111",
                                        port=3306,
                                        user="root",
                                        password="123456",
                                        database="nginx_db"
                                    }
                                    db:set_timeout(1000)
                                    db:send_query("select * from users where id =1")
                                    local res,err,errcode,sqlstate = db:read_result()
                                    	ngx.say(res[1].id..","..res[1].username..","..res[1].birthday..","..res[1].salary)
                                	db:close()
                                }
                            }
                            

                            问题:

                            1.如何获取返回数据的内容
                            2.如何实现查询多条数据
                            3.如何实现数据库的增删改操作
                            
                            使用lua-cjson处理查询结果

                            通过上述的案例学习,read_result()得到的结果res都是table类型,要想在页面上展示,就必须知道table的具体数据结构才能进行遍历获取。处理起来比较麻烦,接下来我们介绍一种简单方式cjson,使用它就可以将table类型的数据转换成json字符串,把json字符串展示在页面上即可。具体如何使用?

                            步骤一:引入cjson

                            local cjson = require "cjson"
                            

                            步骤二:调用cjson的encode方法进行类型转换

                            cjson.encode(res)
                            

                            步骤三:使用

                            location /{
                                content_by_lua_block{
                                    local mysql = require "resty.mysql"
                                    local cjson = require "cjson"
                                    local db = mysql:new()
                                    local ok,err = db:connect{
                                        host="192.168.200.111",
                                        port=3306,
                                        user="root",
                                        password="123456",
                                        database="nginx_db"
                                    }
                                    db:set_timeout(1000)
                                    --db:send_query("select * from users where id = 2")
                                    db:send_query("select * from users")
                                    local res,err,errcode,sqlstate = db:read_result()
                                    ngx.say(cjson.encode(res))
                                     for i,v in ipairs(res) do
                                   ngx.say(v.id..","..v.username..","..v.birthday..","..v.salary)
                                    end
                                	db:close()
                                }
                            }
                            
                            lua-resty-mysql实现数据库的增删改

                            优化send_query和read_result

                            本方法是send_query和read_result组合的快捷方法。

                            语法:

                            res, err, errcode, sqlstate = db:query(sql[,rows])
                            

                            有了该API,上面的代码我们就可以进行对应的优化,如下:

                            location /{
                                content_by_lua_block{
                                    local mysql = require "resty.mysql"
                                    local db = mysql:new()
                                    local ok,err = db:connect{
                                    host="192.168.200.1",
                                    port=3306,
                                    user="root",
                                    password="123456",
                                    database="nginx_db",
                                    max_packet_size=1024,
                                    compact_arrays=false
                                    }
                                    db:set_timeout(1000)
                                    local res,err,errcode,sqlstate = db:query("select * from users")
                                    --local res,err,errcode,sqlstate = db:query("insert into users(id,username,birthday,salary) values(null,'zhangsan','2020-11-11',32222.0)")
                                    --local res,err,errcode,sqlstate = db:query("update users set username='lisi' where id = 6")
                                    --local res,err,errcode,sqlstate = db:query("delete from users where id = 6")
                                    db:close()
                                }
                            }
                            

                            综合小案例

                            使用ngx_lua模块完成Redis缓存预热。

                            分析:

                            (1)先得有一张表(users)

                            (2)浏览器输入如下地址

                            http://191.168.200.133?username=TOM
                            

                            (3)从表中查询出符合条件的记录,此时获取的结果为table类型

                            (4)使用cjson将table数据转换成json字符串

                            (5)将查询的结果数据存入Redis中

                            init_by_lua_block{
                            	redis = require "resty.redis"
                                mysql = require "resty.mysql"
                                cjson = require "cjson"
                            }
                            location /{
                            			default_type "text/html";
                            			content_by_lua_block{
                            				
                            				--获取请求的参数username
                            				local param = ngx.req.get_uri_args()["username"]
                            				--建立mysql数据库的连接
                            				local db = mysql:new()
                            				local ok,err = db:connect{
                            					host="192.168.200.111",
                            					port=3306,
                            					user="root",
                            					password="123456",
                            					database="nginx_db"
                            				}
                            				if not ok then
                            				 ngx.say("failed connect to mysql:",err)
                            				 return
                            				end
                            				--设置连接超时时间
                            				db:set_timeout(1000)
                            				--查询数据
                            				local sql = "";
                            				if not param then
                            					sql="select * from users"
                            				else
                            					sql="select * from users where username=".."'"..param.."'"
                            				end
                            				local res,err,errcode,sqlstate=db:query(sql)
                            				if not res then
                            				 ngx.say("failed to query from mysql:",err)
                            				 return
                            				end
                            				--连接redis
                            				local rd = redis:new()
                            				ok,err = rd:connect("192.168.200.111",6379)
                            				if not ok then
                            				 ngx.say("failed to connect to redis:",err)
                            				 return
                            				end
                            				rd:set_timeout(1000)
                            				--循环遍历数据
                            				for i,v in ipairs(res) do
                            				 rd:set("user_"..v.username,cjson.encode(v))
                            				end
                            				ngx.say("success")
                            				rd:close()
                            				db:close()
                            			}
                            			
                            		}