Nginx介绍和应用

Nginx简介

应用场景:在互联网项目中担任高性能服务器、负载均衡器,在大型系统架构担任高性能流量网关、应用网关、日志服务器、文件存储服务器等,支持模块化扩展和复杂功能的二次开发,通过简单调优就能高效提升系统性能

nginx是普通程序员进阶高程和架构师的捷径,nginx本身调优没啥用处,需要配合操作系统联动调优

nginx的官方文档:https://nginx.org/en/docs/,文档分为非常多的模块,官网中的很多配置,网上的文章都没有涉及,官方文档的解释有时候很模糊,很多时候表达都很模糊并且没有配备实际的场景

nginx官方中文文档:HttpGzip模块 | Nginx 中文官方文档 (gitbooks.io)

CentOS是linux发型版本中比较流行的版本,使用的redhat的linux内核,迷你版的linux系统非常干净,连网卡都没法用

  • 使用命令ip addr能够查看当前系统的ip环境,lo

Linux系统的Lo接口

  1. 在LINUX系统中,除了网络接口eth0,还可以有别的接口,比如lo(本地环路接口)。环回地址lo是主机用于向自身发送通信的一个特殊地址(也就是一个特殊的目的地址)。

    本地回环地址指的是以127开头的地址(127.0.0.1 – 127.255.255.254),通常用127.0.0.1来表示。127.0.0.1,通常被称为本地回环地址(Loop back address),不属于任何一个有类别地址类。它代表设备的本地虚拟接口,所以默认被看作是永远不会宕掉的接口。

  2. Lo接口的作用

    • 通常在不安装网卡前就可以ping通这个本地回环地址。

      一般都会用来检查本地网络协议、基本数据接口等是否正常的。测试本机的网络配置,能PING通127.0.0.1说明本机的网卡和IP协议安装都没有问题。

    • 提供一个回环接口,很多需要网络的程序(比如mpd,xmms2),需要这个接口来通信。

Nginx的版本

Nginx常用的有四个发行版本,

  1. Nginx开源版:http://nginx.org

    最原始的版本,什么额外的功能都没有,主要功能就是成为网站服务器、代理服务器和负载均衡器,做二次开发难度大,需要集成很多的第三方组件,所以有很多公司对nginx在该版本的基础上做了加强

  2. Nginx plus商业版:https://www.nginx.com

    F5官方出品【F5是硬件厂商,专门做负载均衡器】,该版本对微服务和云原生的整合,对k8s的整合非常的好,但是收费

  3. Openresty:http://openresty.org

    免费开源,主要讲nginx和lua脚本进行了完美整合,Nginx Plus中的功能基本用户自己也可以进行开发,用lua脚本开发优雅高性能,代码难度小,也支持个性化定制,还有中文官网

  4. Tengine:http://tengine.taobao.org

    免费开源,没有二次开发的需求,只要求性能稳定,集群负载均衡、反向代理在原版基础上更安全、更稳定、性能更高可以使用tengine,这是淘宝开发出来的【相当于原版的增强】,以C语言的形式,模块化开发扩展原始nginx的功能

Nginx的安装

使用nginx1.20.2,安装包下载:http://nginx.org/en/download.html

安装步骤

基础部分学习使用最原始版本

  1. 将nginx的安装包nginx-1.20.2.tar.gz上传到linux的/opt/nginx目录下

  2. 使用命令mkdir /usr/local/nginx创建/usr/local/nginx目录

  3. 使用命令tar -zxvf nginx-1.20.2.tar.gz解压文件到/opt/nginx目录

  4. 进入解压目录,进入nginx解压文件,使用命令./configure [--prefix=/usr/local/nginx]【–prefix是可选项,指定安装目录】尝试检查是否满足安装条件,期间会提示缺少的依赖,以下是需要依赖的安装

    成功安装的标志是没有报错

    • 使用命令yum install -y gcc安装c语言编译器gcc【-y是使用默认安装,不提示信息】
    • 使用命令yum install -y pcre pcre-devel安装perl库【pcre是perl的库】
    • 使用命令yum install -y zlib zlib-devel安装zlib库
    • 检查没有问题后执行命令make进行编译
    • 执行make install安装nginx
安装成功测试
  1. 使用命令cd /usr/local/nginx进入nginx安装目录,查看是否有相应文件

  2. 进入sbin目录,使用命令./nginx启动nginx服务

    启动时会启动多个线程

  3. 使用命令systemctl stop firewalld.service关闭防火墙服务

    虚拟机是内网上的机器,外网接不进来,关闭防火墙不一定意味着不安全,当然放行端口80更完美;学习过程不需要开启,生产的时候多数时候也不需要开启,除非机器有外网直接接入,或者公司比较大,要防外边和公司里的程序员,可能开启内部的监控和日志记录,一般中小型公司是不会开内网的防火墙的,因为有硬件防火墙或者云的安全组策略

  4. 使用请求地址http://129.168.200.132:80访问nginx

    nginx的默认端口就是80端口,一定要关梯子进行访问,靠北

  5. 使用命令./nginx -s stop快速停止nginx

  6. 使用命令./nginx -s quit在退出前完成已经接受的链接请求

    如用户下载文件,等用户下载完成后再停机,此时不会再接收任何新请求

  7. 使用命令./nginx -s reload重新加载nginx配置

    可以让nginx更新配置立即生效而不需要重启整个nginx服务器,机制是执行过程中优雅停止nginx,保持链接,reload过程开启新的线程读取配置文件,原有线程处理完任务后就会被杀掉,加载完最新配置的线程继续杀掉线程的工作

  8. 此时启动nginx比较麻烦,需要使用nginx的可执行文件,意外重启的时候很麻烦,需要登录到控制台手动启动,将nginx安装成系统服务脚本启动就会非常简单

    • 使用命令vim /usr/lib/systemd/system/nginx.service创建服务脚本

      粘贴文本普通模式粘贴可能丢字符,插入状态粘贴就不会丢字符
      WantedBy=multi-user.target 属于[install],shell脚本不能有注释,否则无法设置开机自启动

      [Unit] 
      Description=nginx - web server
      After=network.target remote-fs.target nss-lookup.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 -c /usr/local/nginx/conf/nginx.conf
      ExecReload=/usr/local/nginx/sbin/nginx -s reload
      ExecStop=/usr/local/nginx/sbin/nginx -s stop
      ExecQuit=/usr/local/nginx/sbin/nginx -s quit 
      PrivateTmp=true
         
      [Install]   
      WantedBy=multi-user.target 
      
    • 使用命令systemctl daemon-reload重新加载系统服务并关闭nginx服务

    • 使用命令systemctl start nginx.service用脚本启动nginx【启动前注意关闭nginx,避免发生冲突】

    • 使用命令systemctl status nginx查看服务运行状态

    • 使用命令systemctl enable nginx.service设置nginx开机启动

      nginx.service中的[Install]部分中的WantedBy=multi-user.target不能有注释,不能拼写错误,否则无法设置开机自启动

Nginx的基础用法

Nginx的目录结构

/usr/local/目录相当于windows的programfile目录,主要是为了方便管理

  1. nginx的主要目录

    其中以temp结尾的目录都是运行过程生成的临时文件

    • conf是核心配置文件的存放目录,里面放着nginx的主配置文件nginx.conf,该文件里面会引用其他的配置文件,

      nginx中的一切几乎都可以配置,比如日志文件放在哪儿,日志记录的格式,日志文件的大小,包括pid以及存放位置都可以进行配置

    • html目录中放一些网页和静态资源,其中的index.html就是nginx正常启动访问80端口的欢迎页,这个目录在使用的过程中都会进行更改

    • logs目录用来记录日志,

      • acess.log是用户访问的日志【记录用户访问的时间、请求的文件、访问是否有附加的参数】,每个人的每次访问都会记录在其中,不能在让这个文件无限扩增,在配置文件中可以对文件大小和记录位置进行配置,不配置当记录到达磁盘大小时可能因为用户访问日志无法记录而出现各种莫名其妙的问题【磁盘满了内部无法记录日志而无限的报错】,此时配置可以限制日志文件大小并转移记录到其他文件

      • 当系统出现错误的时候会将用户访问错误信息和状态码记录到error.log文件中

      • niginx.pid是用于记录nginx运行的主进程id号,第二个数字1090就是nginx的主进程nginx: master的pid,这个进程号也可以配置

    • sbin目录只有nginx一个文件,作为nginx的主程序,用来启动nginx,也是nginx的主进程文件

  2. 编辑nginx欢迎页

    使用xftp可以在线以记事本的形式编辑文本文件

    • 将index.html编写成"hello world"

Nginx的运行原理

  1. nginx的原理架构图

    访问虚拟机也是通过网络请求由请求地址访问到的nginx

    • nginx的可执行文件运行后后开启一个master主进程,主进程会读取主配置文件,对配置文件做一次校验检查配置文件是否存在错误,没有错误会开启子进程worker;主进程不会处理业务,负责协调worker进程执行业务;【如配置文件更改后重新加载,会优雅的杀掉当前的worker进程,生产新的worker进程去读取新的配置文件接替旧的子进程的工作】
    • worker进程启动后会等待用户请求,worker通过解析配置文件判断用户请求是否能够处理,worker目录会去配置文件中查找当前存在哪些站点,并根据用户请求去获取对应的资源【即通过配置文件去html目录找到index.html这个资源】,并将资源响应给用户

Nginx的基础配置

  1. 核心配置文件nginx.conf

    • 最小配置

    #是注释,nginx.confg中不带#的部分,也是能满足nginx正常运行的最小化配置文件版本

    #配置nginx在启动的时候需要启动多少个worker子进程,这个参数基本会设成对应当前服务器对应的CPU内核数,如果虚拟机只有一个内核,此时分配10个子进程,没有意义,因为一个内核分成10个时间段去处理十个进程效率反而会变低;基本的配置逻辑就是一个Cpu内核对应一个子进程
    worker_processes  1;
    
    #events是事件驱动模块,worker_connections指的是每个worker进程最多可以创建多少个连接,默认就是1024,一般不需要动
    events {
        worker_connections  1024;
    }
    
    #http模块,
    http {
    	#include命令可以将后面的配置文件引入到当前的配置文件中,后续会使用include引入其他配置文件,一个配置文件写的内容过多不便于后续配置的管理,且多个对象对同一个配置文件进行更改需要竞争锁,但是多个配置文件可以各自改各自的,这样设计效率更高;mime.types是所有http协议的头标注的文件类型,这个头中会包含服务器返回给客户端的文件类型,如文本文档、html文档,可执行程序或flash文件,这个头的信息有服务器发送给浏览器告诉浏览器这是一个什么文件,由服务器定义;服务器传递给浏览器的文件是0101类型的字节码,浏览器只看文件后缀是看不懂具体是什么文件,必须通过服务器在协议头中加上当前传输文件的文件类型、如图片就加img、jpg到协议头中;不同的类型效果也会不同,图片的.png会直接在浏览器展示出来,不会直接下载;如果是.exe会弹出下载框对文件进行下载;但实际展示还是下载的行为不是由后缀决定的,是由协议头中的mime.types决定的,该类型可以对应到文件的后缀名
        include       mime.types;
        #mime.types不可能包含所有的类型,如果实在没有匹配的类型,就使用默认的类型以application/octet-stream;以数据流的方式传递给浏览器
        default_type  application/octet-stream;
    
    	#开启sendfile功能,数据直接读取到网络接口,不走nginx内存
        sendfile        on;
    
    	#不做详细介绍,理解成保持链接超时的时间,将反向代理再细讲这个问题
        keepalive_timeout  65;
    
    	#http模块下的server模块,一个server表示一个主机,一个nginx可以同时运行多个主机,这种开启多个主机的方式称为虚拟主机,虚拟主机又称为VHost,
        server {
        	#nginx当前服务器监听的端口号,可以通过端口号来区分不同的主机,比如可以设置下一个主机在8080端口运行,主机的端口号不能重复,nginx服务就启动不起来
            listen       80;
            #server_name指的是当前主机的主机名,可以写域名或者主机名。必须要能解析,localhost能解析是因为计算机的Hosts文件中定义了localhost的映射是127.0.0.1
            #server_name是指检测到别人访问到某个域名如blog.concurrecy.cn就会跳转到对应的server下
            server_name  localhost;
    
    		#location表示一个主机有一个独立的站点,相互之间互不干扰,暂时理解为域名后的根路径或者整体看做uri【专业就叫uri,就是请求路径端口号之后的部分】,location是用来完整或者模糊匹配uri的,一个主机下可以配置很多个location,可以配置到不同目录下相互之间还不影响
            location / {
            	#root是配置用户请求进来之后从哪个目录下去找相应的网页,初始设置的就是html目录,即欢迎页所在目录,这个路径是一个相对路径,html是相对于nginx的主目录`/usr/local/nginx`目录下的html目录
                root   html;
                #该index只对当前location生效,对应html目录下的index.html页面
                index  index.html index.htm;
            }
    
    		#error_page是发生服务端错误的时候,50x错误码,对于列举的500 502 503 504四个错误码会转向到当前站点的/50x.html地址,这个地址相当于替代整个uri部分,相当于跳转http://192.168.200.132:80/50x.html
            error_page   500 502 503 504  /50x.html;
            
            #一旦用户请求的uri为/50x.html就会去以html为根目录去找html中的50x.html
            location = /50x.html {
                root   html;
            }
        }
    }
    
    • mime.types

      • 前一个是协议头中的mime.type类型,后一个是文件后缀名

      • 可以设置自定义文件后缀指定现有mime.type实现浏览器以视频的方式直接播放自定义后缀mp5的文件的能力

    types {
    	#返回文件的后缀名为html,就在返回的HTTP头中加入该文件类型是text/html,告诉浏览器用text/html的方式来解析当前响应的文件
        text/html                                        html htm shtml;
        text/css                                         css;
        text/xml                                         xml;
        image/gif                                        gif;
        image/jpeg                                       jpeg jpg;
        application/javascript                           js;
        application/atom+xml                             atom;
        application/rss+xml                              rss;
    
        text/mathml                                      mml;
        text/plain                                       txt;
        text/vnd.sun.j2me.app-descriptor                 jad;
        text/vnd.wap.wml                                 wml;
        text/x-component                                 htc;
    
        image/png                                        png;
        image/svg+xml                                    svg svgz;
        image/tiff                                       tif tiff;
        image/vnd.wap.wbmp                               wbmp;
        image/webp                                       webp;
        image/x-icon                                     ico;
        image/x-jng                                      jng;
        image/x-ms-bmp                                   bmp;
    
        font/woff                                        woff;
        font/woff2                                       woff2;
    
        application/java-archive                         jar war ear;
        application/json                                 json;
        application/mac-binhex40                         hqx;
        application/msword                               doc;
        application/pdf                                  pdf;
        application/postscript                           ps eps ai;
        application/rtf                                  rtf;
        application/vnd.apple.mpegurl                    m3u8;
        application/vnd.google-earth.kml+xml             kml;
        application/vnd.google-earth.kmz                 kmz;
        application/vnd.ms-excel                         xls;
        application/vnd.ms-fontobject                    eot;
        application/vnd.ms-powerpoint                    ppt;
        application/vnd.oasis.opendocument.graphics      odg;
        application/vnd.oasis.opendocument.presentation  odp;
        application/vnd.oasis.opendocument.spreadsheet   ods;
        application/vnd.oasis.opendocument.text          odt;
        application/vnd.openxmlformats-officedocument.presentationml.presentation
                                                         pptx;
        application/vnd.openxmlformats-officedocument.spreadsheetml.sheet
                                                         xlsx;
        application/vnd.openxmlformats-officedocument.wordprocessingml.document
                                                         docx;
        application/vnd.wap.wmlc                         wmlc;
        application/x-7z-compressed                      7z;
        application/x-cocoa                              cco;
        application/x-java-archive-diff                  jardiff;
        application/x-java-jnlp-file                     jnlp;
        application/x-makeself                           run;
        application/x-perl                               pl pm;
        application/x-pilot                              prc pdb;
        application/x-rar-compressed                     rar;
        application/x-redhat-package-manager             rpm;
        application/x-sea                                sea;
        application/x-shockwave-flash                    swf;
        application/x-stuffit                            sit;
        application/x-tcl                                tcl tk;
        application/x-x509-ca-cert                       der pem crt;
        application/x-xpinstall                          xpi;
        application/xhtml+xml                            xhtml;
        application/xspf+xml                             xspf;
        application/zip                                  zip;
    
    	#octet-stream以数据流的方式去加载并提示用户是否需要下载下来
        application/octet-stream                         bin exe dll;
        application/octet-stream                         deb;
        application/octet-stream                         dmg;
        application/octet-stream                         iso img;
        application/octet-stream                         msi msp msm;
    
        audio/midi                                       mid midi kar;
        audio/mpeg                                       mp3;
        audio/ogg                                        ogg;
        audio/x-m4a                                      m4a;
        audio/x-realaudio                                ra;
    
        video/3gpp                                       3gpp 3gp;
        video/mp2t                                       ts;
        video/mp4                                        mp4;
        video/mpeg                                       mpeg mpg;
        video/quicktime                                  mov;
        video/webm                                       webm;
        video/x-flv                                      flv;
        video/x-m4v                                      m4v;
        video/x-mng                                      mng;
        video/x-ms-asf                                   asx asf;
        video/x-ms-wmv                                   wmv;
        video/x-msvideo                                  avi;
    }
    
    • 数据传输过程

      • Nginx作为一款软件跑在linux系统上,请求从客户端通过ip找到linux服务器,由操作系统的网络接口将请求转发到nginx,在启动java网络程序的过程中会向操作系统注册一个端口,相当于告诉操作系统,有请求过来要通过请求对应端口转发给该程序
      • nginx拿到请求解析后通过配置文件找资源的目录,想要将该资源文件响应给用户,从找到该文件开始就开始决定是否开启sendfile的过程,sendfile开启和不开启的流程是不同的
      • 不开启sendfile的情况下,分为read和write两个过程,read是nginx去读对应的文件,将完整文件加载到nginx应用程序的内存中,读完之后将当前的文件发送给计算机操作系统的网络接口【即网卡的驱动程序】,期间还会经历DMA的调度和网卡驱动程序以及内核缓存的一个过程,存在文件内容的二次复制,读取到nginx内存一次,复制给网络接口缓存一次【不同进程间如果不使用共享内存,内存不能相互访问,所以这里是必定多了一次拷贝的。】

      【不开启sendfile下的流程】

      【开启sendfile下的流程】

      开启sendfile的过程是不需要nginx复制到自身内存再复制给网络接口缓存的过程,nginx不去读取相应文件,而是通过nginx向操作系统内核发送一个信号【执行sendfile方法向网络接口传递socket和文件位置】,由网络接口来读取对应的文件直接将文件发送给客户端

      先将数据写入到内核态的缓存中,然后直接写入到socket缓存,socket缓存再发送到网卡,网卡再执行转发,就是在内核状态,将数据直接读取给网卡,而不是用户态->内核态->数据->内核态->用户态->内核态->网卡;少了一次读到应用内存的次数,减少了应用内存的消耗【来自弹幕组合分析,再深入了解一下零拷贝的概念,操作系统和计算机原理的知识】

虚拟主机与域名解析

本地域名配置解析

这个比较假,只能内网用,公网是无法访问的

  1. 浏览器请求过程

    • 电脑从DNS服务器拿到ip地址,会发起tcp/ip请求,

      HTTP协议在tcp/ip协议之上,HTTP协议是高级的网络协议,tcp/ip协议叫做基础的网络协议,不能叫低级,基础的含义是能兼容一切上层的协议,HTTP的应用广泛,几乎所有上网的设备都支持HTTP协议,nginx和浏览器都实现了HTTP协议,

      • tcp/ip协议规定了网络只能传递二进制的数据,数据以数据流的形式发送给目标服务器,流的含义就像一段水流一样,tcp/ip协议中并没有对数据传递结束的约束和限制
      • HTTP协议中最重要的规定是通信双方数据传递结束的标准,类似与说一段话,话怎么开始,怎么结束,需要用协议规范;HTTP协议中请求数据的数据报文究竟有多长会直接写在HTTP协议中,HTTP协议是上层的高级应用协议,其中包含的约束比较多,约束了通信双方都要按照一定规则传递数据,
      • HTTPS协议在HTTP协议的基础上额外增加了一层数据安全的保障,路由器就是一层网关,需要通过路由器接入互联网,路由器也不是直接接入主干网,在接入主干网之前还要接入小区的网关,服务供应商的网关,区一级的网关,市一级的网关,再到全国的总的网关入口,才能接入到主干网,网关传递数据非常多,用HTTP协议能被网关劫持解析数据看见个人隐私数据,又比如转账消息,因此在原有HTTP协议的基础上考虑到安全又弄出了Https协议【后续详细讲】

  2. 虚拟主机

    • 一台主机上开一个站点,可能压根就没多少人访问,对资源是极大的浪费,
    • 虚拟主机是将一台真实主机虚拟出来多台主机,一台虚拟主机对应一个域名【ip】,外部请求访问虚拟主机时让多个域名对应同一个真实的主机ip,由主机上的nginx来根据请求域名进行判断请求究竟要访问那个虚拟主机,nginx将请求对应指向不同站点的目录响应对应的资源即可
  3. 配置域名解析

    因为买不到域名,相当于在自己的系统里玩,外网访问不到

    • 域名解析配置在windows的hosts文件中

      因为在hosts中已经配置了域名对应的ip,以后本机访问eureka7001.com就不会再去DNS服务器中进行查找,而直接从hosts文件中拿,这个就叫本地域名解析

      • 在hosts文件中设置虚拟机的ip:192.168.200.132对应的域名为GG.com,在浏览器访问http://GG.com/就能直接访问虚拟机上启动的nginx

      • 修改hosts域名解析

        系统目录下文件更改需要的权限比较高,可以更改该文件的编辑权限,点击hosts,右键属性,在安全选项卡下,SYSTEM和Administrators都有修改的权限,但是users没有修改权限,只要添加用户组的修改权限用户就能更改该文件,选中users点击编辑勾选完全控制即可使用户获得系统文件的全部权限,改完系统文件最好再还原回来【只勾选读取以及读取和执行两个权限,这里实现不了,因为只有超级管理员才有更改权限的权限】,避免一些程序更改系统配置,比如正常的站点访问会自动被劫持到错误的ip上

        • 还有个办法将host文件从系统文件夹中复制到有权限的文件夹,修改配置后粘贴替换原系统文件夹下的hosts文件
        • 可以以管理员的身份启动editPlus,就能修改该文件
        • 还可以将当前用户Earl添加到超级管理员组,这样当前用户就能获取超级管理员权限,但是这样不安全,除非把超级管理员的完全权限也禁用掉【以上办法都需要当前用户有超级管理员权限,如果把当前用户从超级管理员组中剔除,那么当前用户连更改权限都做不到,甚至想让超级管理员账户启用都做不到,此时只能shift+重启,重启过程中一直按住shift进入高级选项,在高级选项中选择启动设置并以安全模式启动有点难找,但是确实找得到,选择超级管理员账户进入,将此前用户添加到超级管理员的分组中,让Earl用户拥有超级管理员权限】,现在的办法是给Earl超级管理员分组,取消超级管理员的写入功能,其他不要动,需要的时候再打开,避免出现超级管理员权限也丢失的情况
        • 而且注意hosts文件的编码格式必须为ANSI,不能为UTF-8,否则即使写对了也识别不了

        【域名映射】

        【还原用户权限】

        【修改用户组】

        【修改用户组效果】

      • 测试域名解析生效

        开启对应虚拟机,使用命令ping GG.com观察是否ping通

        经过测试,域名不区分大小写

        【浏览器使用域名访问虚拟机】

公网域名配置解析

域名的售卖商很多,最大的是万网,万网被阿里云收购了,小型域名商存在跑路的风险,我买的是腾讯云,用腾讯云做演示

  1. 域名记录类型

    除了列举的其他都不太常用,最常用的就是列举的几个记录,A是最最最普遍的

    • A是域名匹配一个独立的ip地址,直接将该域名转向到ip地址上,记录到服务器上,常用的就是A记录
    • CNAME是将该域名转向到另外一个已经解析好的域名上,有可能不知道域名的ip地址或者域名对应的ip地址会变,不需要管ip就能直接访问
    • AAAA是IPV6的地址,目前IPV6还没有普遍起来
    • NS是做DNS服务器的
    • MX是做邮件服务器的【申请企业邮局,会要求将SMPT、Pub3这种域名解析到相应的ip地址上】

  2. 填写说明

    • 主机记录是域名的前缀www如www.concurrency.cn,即二级域名,三级域名二级域名都可以如法添加,www前缀在浏览器不填前缀默认就是www,

    • 解析线路可以根据访问用户的通信运营商将用户对域名的访问指向到对应运营商如联通或电信访问速度较快的ip上,在DNS服务器上就能完成根据用户通讯运营商分配到访问速度较快的相应运营商ip上,还可以根据教育地址和搜索引擎分配不同的IP,把不同来源的流量分隔开,这就是智能的DNS服务器了;传统的DNS服务器只是k-v键值对的方式根据域名匹配ip地址,

    • 记录值就是IP地址,就是虚拟机的那个地址192.168.200.132

    • TTL(Time to live),是指各地 DNS 服务器缓存解析记录的时长。

      假设 TTL 设定为10分钟,当各地的 DNS 服务器接收到域名的解析请求时,会向权威服务器发出请求获取到解析记录,并在本地服务器保存10分钟,在10分钟内,解析请求将从本地缓存中读取,缓存失效后才会重新获取记录值。建议正常情况下设定10分钟即可,使用不同套餐版本的解析能设定的 TTL 最低值不同。

  3. 测试域名解析是否成功

    • cmd窗口ping www.concurrency.cn是否能ping通

    • 浏览器访问 www.concurrency.cn能否访问到虚拟机的nginx欢迎页

      此时也只能内网访问,公网无法访问

  4. 泛解析

    • 对主机记录使用通配符*,二级域名不论是什么都会解析到设置的ip地址上

      想要做二级域名系统,即多用户的域名系统【多个请求打到同一个ip真实主机,nginx再根据请求分配对应的站点】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

Nginx虚拟主机配置

虚拟机中配置多个站点,端口号和主机名的并集不能完全相同,启动的时候会报错,不同虚拟主机的主机名和端口号其中一个可以不同或者都不同

  1. 配置虚拟主机站点目录

    • 在linux系统下的根目录下创建www目录
    • 在www目录下创建www目录作为主站点,创建video站点作为视频站点
    • 在video目录下创建一个欢迎页index.html,编写文字this is vod web site
    • 在www目录下创建一个欢迎页index.html,编写文字this is www web site
  2. 更改nginx配置文件nginx.confg

    创建文件用shell比较方便,编辑文件用xftp比较方便;不要怕改错了,nginx.confg.default是该文件的一个默认备份

    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
        
        #虚拟主机vhost
        server {
            listen       80;
            #域名、主机名
            server_name  localhost;
            
            location / {
                #设置该虚拟主机的主站点,绝对路径
                root   /www/www;
                index  index.html index.htm;
            }
            
            #出错跳转nginx目录下的错误页
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
        
        #虚拟主机vhost
        server {
            listen       88;
            #域名、主机名
            server_name  localhost;
            
            location / {
                #设置该虚拟主机的主站点,绝对路径
                root   /www/video;
                index  index.html index.htm;
            }
            
            #出错跳转nginx目录下的错误页
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    
  3. 改完以后使用命令systemctl reload nginx 重新加载nginx,使用命令systemctl status nginx查看nginx运行状态

  4. 浏览器访问效果

    实现了不需要区分域名只需要区分端口号就可以访问多个站点

  5. 配置域名

    配置相同端口不同二级域名,此时根据域名和端口的并集进行匹配

    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
    
    	#虚拟主机vhost
        server {
            listen       80;
            #域名、主机名,让这个域名www.concurrency.cn进入到www这个站点目录
            server_name  www.concurrency.cn;
    
            location / {
            	#设置该虚拟主机的主站点,绝对路径
                root   /www/www;
                index  index.html index.htm;
            }
    
    		#出错跳转nginx目录下的错误页
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
        
        #虚拟主机vhost
        server {
        	#同一个端口号下可以通过域名访问不同的站点目录,这种演示需要将端口改成相同的,否则会优先走端口
            listen       80;
            #域名、主机名,让域名vod.concurrency.cn进入到vedio这个站点目录
            server_name  vod.concurrency.cn;
    
            location / {
            	#设置该虚拟主机的主站点,绝对路径
                root   /www/video;
                index  index.html index.htm;
            }
    
    		#出错跳转nginx目录下的错误页
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    
  6. 使用命令systemctl reload nginx使配置生效,在浏览器输入不同的二级域名测试访问效果

    实现不区分端口只区分域名就能实现不同站点目录的访问

  7. 没有域名加端口的匹配站点目录的情况下默认访问第一个匹配端口的站点目录

    证明:将vod站点配置在www前面,再次访问域名和ip没有匹配的请求地址vodq.concurrency.cn,访问的站点目录就变成video目录了

ServerName匹配规则

如果两个域名vod.concurrency.cnvodq.concurrency.cn都想解析到video目录下【不考虑将video站点目录作为首个80端口站点目录,用不能匹配的方式强制让vodq.concurrency.cn对应到video目录的情况下】,此时如果给两个域名都指定不同的虚拟主机server且都匹配到相同的站点目录,就显得很不优雅笨重

server_name匹配是非常有用的技术,尤其是配置虚拟主机的时候,把所有的请求都接入到一个server中,server通过反向代理的方式把请求转到后端业务逻辑处理的服务器上,再根据用户请求做不同的处理响应不同的结果

  1. 可以在一个server的server_name中配置多个域名

    第一个80端口站点目录是www,如果vodq.concurrency.cn没有匹配上会优先跳转www目录

    【浏览器禁用缓存设置】

    • 即使点了也不一定好使,

    • 也可以使用ctrl+F5强制刷新缓存,

    • 也可以在请求链接后面加vodq.concurrency.cn?xxx进行访问,浏览器缓存找不到对应请求路径,就会直接去请求服务器

    【测试效果】

  2. 通配符匹配二级域名前缀

    • 注意通配符匹配二级域名前缀*.concurrency.cn不能识别一级域名concurrency.cn,直接通过concurrency.cn是访问不到虚拟主机的

    • *匹配二级域名到video站点目录,将www站点目录放在通配符的后面,观察www.concurrency.com是否仍然访问www目录

    • 经过测试,域名匹配总是先走精确匹配,匹配不到再走通配符匹配

    【测试效果】

  3. 通配符匹配域名后缀

    *作为域名结束匹配可以匹配以不同字符.com.cn结尾的域名到同一个站点,由于只购买了域名concurrency.cn,所以需要在hosts文件中添加www.concurrency.orgwww.concurrency.net本地域名解析

    • 测试效果

      只要能被本地解析或者远程解析出主机ip的域名即使无法精确匹配,只要存在对应端口的站点目录,就会自动匹配到对应端口的第一个站点目录,hosts中配置的默认是以www作为二级域名,像vod.concurrency.org这种既无法被本地解析又无法被远程解析出主机IP的就会直接报错;如果找到主机IP,但是nginx配置文件中没有找到对应的端口,也会报错

      同样的,优先进行精确匹配,再进行通配符匹配或者直接用端口进行匹配

  4. 正则匹配

    • 配置

      正则匹配前缀是纯数字的,即concurrency.cn纯数字.concurrency.cn跳转www站点目录,非纯数字.concurrency.cnconcurrency.netconcurrency.org匹配video站点目录

    • 测试效果

域名解析技术架构

  1. 多用户二级域名

    二级域名系统就是指有很多的二级域名来访问该系统

    • 原理

      • 所有的二级域名通过泛解析全部接入同一台服务器中,nginx通过反向代理将所有的请求转发到另一台业务服务器上【不是虚拟主机】,比如Tomcat和java实现的服务器,Tomcat能够获取到访问请求访问的二级域名,业务服务器处理完请求将结果返回给反向代理,反向代理将数据传递给nginx,nginx把数据响应给用户

      • 感觉课上讲的像反向代理将请求转发给业务服务器,业务服务器解析二级域名前缀,拿着前缀去数据库获取该二级域名前缀的信息并传递给反向代理再传递给nginx,最后响应给客户端

  2. 短网址

    • 原理
      • 短网址就是用户发送一个请求如dwz.cn/aidfjoasidfj21739给nginx服务器,nginx服务器将请求转发到短网址运维系统,该系统在数据库存储了短网址的uri部分即aidfjoasidfj21739和真实地址的对应关系【uri常用UUID保证唯一性,怎么处理真实地址获取到UUID的没有说】,用户使用短网址被打到对应的短网址运维系统,系统根据uri从数据库查询到真实地址,使用重定向将请求转发到真实的请求地址上
  3. Httpdns

    DNS服务器走的UDP协议【去全网广播,就是在腾讯云上配置的域名解析】,HttpDNS走的Http协议

    具体原理还是要看计算机网络和操作系统,图也只是讲了个大概,这些只是nginx域名解析的应用场景,了解个大概

    • 走HTTP协议需要有HTTPDNS服务器的IP地址,用HttpDNS就是想获取相应目标域名的IP地址,一般HTTPDNS服务器地址都存在客户端,HTTPDNS一般不适合网页即浏览器使用,一般是给手机的APP或者基于C/S架构的应用进行使用,【可以在这种软件中预埋几个ip地址,这几个IP地址就是nginx服务器,浏览器记不住这些HTTPDNS服务器的IP地址】,系统启动后,会向预埋的几个IP发起请求,请求传参某个域名获取其当前真实的ip地址,HTTPDNS将真实的IP地址返回

      直接理解成发送请求前先使用预埋IP请求HTTPDNS服务器获取目标域名的IP,再次使用真实的IP发起用户的请求;浏览器无法实现因为浏览器无法持久化HTTPDNS服务器的IP,无法向HTTPDNS服务器自动发起请求,所以只适用于C/S架构的应用,HTTPDNS服务器就是

Nginx的错误页配置

  1. 错误页配置详解

    错误页配置的目的是为了处理请求过程中发生了错误通过对错误进行捕获,用补偿的信息替代掉错误信息返回给客户

    可以自定义发生错误时修改响应的状态码并且配置对应展示的资源或者跳转的网站,让浏览器认为没有报错,正常响应了资源或者友好提示,可以通过选项返回首页

    以下展示了发生错误根据错误码展示对应的站点目录资源;

    发生错误将响应错误码替换成302跳转到指定网站;

    发生错误将响应错误码替换成200并响应指定站点目录资源

    ...
    http {
        ...
        server {
            listen       80;
            server_name  vod.concurrency.cn;
    
            location / {
                root   /www/video;
                index  index.html index.htm;
            }
    
    		#发生404错误,发生错误就跳转到对应的网站上去,等号和302之间不能有空格,有空格会启动报错,跳转地址和分号间也不能有空格;HTTP状态码错误是可以处理的,系统级别的错误是用户是处理不了的,配置响应码302需要给重定向的地址给浏览器
    		#error_page 404=302 http://www.atguigu.com;
    		
    		#发生404错误响应200状态码,响应/401.html站点资源;此外错误页还可以设置响应图片或者其他东西,但是一般都是使用网页给用户响应
    		error_page 404=200 /401.html;
    		
    		#error_page的相关设置,如果是500系列错误就会跳转到location站点/50x.html下,/50x.html是一个location,不是一个地址;如果是APP或者前后端分离的项目也可以返回一个友好提示的json数据
            error_page   500 502 503 504  /50x.html;
            #location的根目录是html目录,没有指定uri对应的资源名就会去访问uri对应的同名资源
            location = /50x.html {
                root   html;
            }
            
            error_page 401 /401.html;
            location=/401.html{
            	root html;
            }
        }
    }
    

    【404错误的响应效果】

    【响应站点目录资源的效果】

匿名location

匿名站点目录的匿名针对的是客户端,客户端访问该站点是访问不到的,主要用于希望location只在内网中被使用的情况,在内网调用的情况下就可以把匿名站点的内容展示给用户

  1. 匿名location的配置

    此前配置的站点目录用户通过对应的uri都是可以访问到的,包括错误页配置

    匿名location的写法是location后面不使用等号使用@符号

    location @666 {
        root html;
    }
    

    【访问匿名站点目录的效果】

    html站点目录下有index.html资源,但是匿名站点目录无法访问,返回错误码404,404此前设置错误页响应200并响应资源401.html

  2. 希望用户无法访问但是内部可以跳转的匿名站点目录

    这种配置下找不到资源会报错404,但是会被跳转到配置的@666站点目录下并响应return中响应的内容,如果只有200状态码,没有指定响应的资源,会响应一个请求uri作为文件名的空文件

    如果状态码不是200比如886,浏览器只会在响应头中看到状态码886,不会再显示下载文件;因为状态码200告诉浏览器要读取响应内容,对200状态码下载的行为是浏览器做出的判断,在响应头中没有Content-Type类型,不知道如何展示响应内容,就只能选择以文件下载的方式处理响应内容

    #发生404错误跳转@666匿名站点,这个等号和@666之间有空格也能成功启动nginx
    error_page 404= @666;
    #@666是站点的名字,html是站点目录;return命令可以直接返回后面跟的资源,无需进行资源和uri的匹配;只有状态码200会开始下载一个空文件
    location @666 {
        return 200;
        #return 886;
    }
    

    【上述找不到资源404错误跳转匿名站点@666并响应站点目录return后面的资源内容】

    名字经过确认就是对应uri,且大小为0K

    【状态码886的响应效果】

    只有return不设置响应内容类型Content-Type,响应头中就没有Content-Type参数,浏览器不知道如何去展示响应数据

  3. location中return响应状态码和文件内容

    【匿名站点响应200状态码并添加资源】

    #发生404错误跳转@666匿名站点,这个等号和@666之间有空格也能成功启动nginx
    error_page 404= @666;
    #@666是站点的名字,html是站点目录;return命令可以直接返回后面跟的资源,无需进行资源和uri的匹配;只有状态码200会开始下载一个空文件
    location @666 {
        return 200 "hi world!";
    }
    

    【响应效果】

  4. 在使用return的同时添加Content-Type参数

    设置成响应类型为文本类型,浏览器就能对响应内容进行展示

    error_page 404= @666;
    location @666 {
    	#向响应头信息中添加头信息content-type
    	add_header content-type "text/html";
        return 200 "hi world!";
    }
    

    【响应效果】

反向代理

反向代理是nginx应用中极为重要的功能,也是系统架构选用nginx使用的主要功能,基于反向代理可以衍生出非常多的应用场景和需求的解决方案

网关、代理和反向代理

  1. 反向代理

    • 用户访问系统通过互联网将请求打到机房的网关上,网关会把所有的请求都打到nginx服务器上,nginx作为反向代理服务器时会将所有的请求全部转发到后端的应用服务器,这些应用服务器可以是java和tomcat做的项目集群,tomcat是不会被用户直接访问到的,应用服务器响应之后再将结果响应给nginx,在nginx和应用服务器之间形成内网,就是nginx在中间做了中间商,这种系统结构就是反向代理,相当于nginx代表用户去访问应用服务器,发现访问成功,数据也都拿回来了
    • 机房内的网关和应用服务器也不互通,网关想要直接连接应用服务器连不上

  2. 正向代理

    • 正向代理系统结构

      比如用户无法直接连通外网,此时需要一个代理服务器,用户能够连接代理服务器,代理服务器能够连接外网,用户就能通过代理服务器连接到外网【有点像vpn】

      正向代理和反向代理的区别就是正向代理是用户主动配置代理服务器【用户和代理服务器是一家子】,需要这个代理服务器去访问某些网络;反向代理是nginx代理服务器与用户想要访问的网络是一家子【即该代理服务器不是用户主动配置的】,用户甚至机房网关都不能直接访问应用服务器,由服务提供商提供给用户一个访问入口来对访问不到的应用服务器进行访问;实际上两种代理服务器都起到一样的作用,即在用户和网络资源之间起到中继和网络传递的作用,本质上都是用户–代理服务器–网络资源的结构,就看代理服务器是谁提供的

  3. 网关

    • 手机连上家里的路由器,把所有的数据都发送给路由器,由路由器转发给下一跳的路由中继或网关服务,一跳接一跳地跳到目标服务器的位置,目标服务器收到请求处理后再一跳一跳的返回回来;家里的路由器其实就是网关,网关其实就是一种代理服务器,在正向代理结构中的爱丽服务器就是网关,网关就是访问网络的入口,就像学校的大门,需要从大门进出学校

    • 网关的特点:在用户和目标服务器之间中转所有的数据,这种特点注定如果网关的带宽不足【比如路由器的带宽只有10M,但是下载的东西是100M,意为着最大地下载速度只有10M,千兆网络如果路由器的下载速度只有10M,下载速度也会卡在10M,请求越多越卡,多个请求访问外网会竞争路由器的带宽资源,越竞争路由器的分配效率越低,认为带来很多额外的操作,避免这种情况只能提升代理服务器即网关的带宽】,在反向代理结构中,nginx服务器的网络带宽就是整个应用集群的带宽,nginx服务器的带宽只有10M,后续网络即使千兆,数据传输速度也只有10M;这种特点决定了在较高品IO操作的情况下nginx做反向代理服务器就不合适了,这种数据一进一出必须走一个nginx代理服务器的模型称为隧道式代理模型,这种模型有天然的网关性能瓶颈;

    • 有其他办法可以避免这种隧道式代理模型带来的网关性能瓶颈,即用户请求打到nginx反向代理服务器,nginx将请求转发到应用服务器,应用服务器直接将数据返回给用户,而不是将数据返回给网关由网关将数据返回给用户【只有请求进应用服务器走代理服务器,响应数据的时候不再走代理服务器】,这样的模型叫DR模型,是LVS提供的一种功能,LVS是一种性能比nginx还要高的负载均衡器,但是功能比nginx简单的多,LVS是专业的负载均衡器,在反向代理的时候既可以做隧道式反向代理,也可以做DR模型的反向代理【请求进入系统通过代理服务器,响应返回的时候不过代理服务器】,DR模型的应用举例:必须想获取应用服务器一个500MB大小的文件,可能发送的是一个get请求,get请求只有1KB大小,1KB的请求消耗的网络带宽不大,此时返回数据500MB数据如果走nginx,在高并发情况下nginx的网络就首先扛不住了,此时由Tomcat把数据包直接传递给用户【也不是直接传递,而是先将数据包传递到机房的网关上,即使用DR模型需要应用服务器能和机房网关通讯,此时应用服务器也是接入外网的,应用服务器此时不处于纯粹的内网环境,但是这种情况一般应用服务器被设置成只能向外网传数据,不能接收任何请求】就会极大地减小nginx的压力,DR模型是靠虚拟伪装IP完成的,逻辑在LVS中会详细讲解

      Lvs是一款很简单的软件,直接内嵌在linux内核中,甚至不需要额外装软件就可以直接使用LVS,

反向代理的应用场景

超大型的互联网项目还是传统公司里面的ERP、CRM、CMS、OA这种项目基本上都有nginx的反向代理,主要还是性能高,成本低,体验好

  1. 传统公司系统架构

    最简单的应用架构,这种架构适用于传统小型项目,像一些提供给内网用户使用的系统并发量不大直接用nginx代理请求即可

    • 用户接入层

      用户请求经过网关,经过域名解析,通过互联网访问到公司系统的流量接入层

    • 机房网关加代理服务器

      用户请求打到机房网关,机房网关中转请求并通过防火墙,转发带nginx反向代理服务器,反向代理服务器将用户请求转发到对应的业务服务器

    • 业务服务器

      举例:英语培训在线教育APP,以nginx代理后边的三台tomcat,系统性能非常好,3台tomcat服务器能抗住300的并发量,并发量就是QPS,QPS300对传统的互联网项目来说已经非常可以了,这个产品的价值也已经非常可观,不是同时在线人数,是每秒钟有300的点击量,实际上大部分时间都是浏览,所以点击量是低于在线人数的

      backend Server是后端服务器,GATEWAY是后端的网关路由服务器,数据库服务器、测试用的服务器、文件存储服务器、日志服务器,业务逻辑服务器、保证高可用的HA服务器、权限管理的Auth Server服务器

      GATEWAY服务器是将所有的业务服务器统一的管理起来,在中间起到查找和一定的鉴权作用,并不是所有的请求都可以直接访问业务服务,而是需要在GATEWAY做一次权限认证

      Nginx是服务器级别的网关, 当后端的网关需要做成集群时需要Nginx做负载均衡, 后端的网关是项目级网关

  2. 中小型互联网项目

    • 用户接入层

      这一块后面再说

    • 系统

      nginx作为反向代理服务器需要起到更多的功能,如伪装当前访问资源的真实地址【发送请求的URI为/item/100,能在nginx中改写成真实访问的URI/itemService?id=100,这样能显得更高级;电商类似京东会将/itemService?id=100这样的访问URI改写成/item/100.html的形式,用户看起来会觉得这是一个独立的页面,实际上数十亿的商品根本不可能存在这么多静态页面,这种方式会让商品在计算网页排名的算法PageRank中占到的权重更高】

      nginx服务器对业务逻辑的转发,转发的数据包一般就是json数据,可能就几KB大小【比如修改密码】,这种情况下nginx很能抗,一台nginx服务器就能代理后边的数十台业务逻辑服务器

      对于多媒体静态资源,流媒体资源的服务器,nginx代理服务器因为带宽就会成为瓶颈,文件存储服务器存储的都是文件且数据比较大,电影、软件等;如果此时用nginx做反向代理就需要多配置一些nginx代理服务器,分成好几组去代理后边的文件服务器

    • 全网静态资源分发

      不清楚这个是干什么的,没讲,先mark

Nginx反向代理配置

Nginx代理一个网址
  1. proxy_pass属性

    • proxy_pass在nginx.conf文件找那个的server.location中配置,在其他位置配置启动会报错,proxy_pass属性和root属性是二选一的关系,proxy_pass一旦配置,root属性【配置寻找静态文件的目录】就不能使用了,包括静态资源目录和对应的路径静态资源映射。proxy_pass的属性值有两种,一种是要让nginx代理的地址,可以是一台具体的主机,也可以是一个网址;另一种是配置一组服务器

      • 代理一个网址时的nginx配置文件配置

        能实现访问当前站点的根目录能跳转到网址https://www.baidu.com/上,但是跳转到百度上可能因为重定向的原因地址栏显示百度的网址了,使用尚硅谷的网址就不会出现这种问题

        过程是用户访问nginx代理服务器,代理服务器根据proxy_pass将请求转发过去,拿到数据后返回给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 / {
                	#这里使用https协议的目前的nginx配置加载配置文件会报错
                	#与前面类似,访问百度或者京东
                    proxy_pass http://www.atguigu.com;
                    root html;
                    index index.html index.htm;
                }
        
                #出错跳转nginx目录下的错误页
                error_page   500 502 503 504  /50x.html;
                location = /50x.html {
                    root   html;
                }
            }
        
        }
        
      • 使用命令systemctl reload nginx将nginx配置文件重新加载

      • 使用命令systemctl daemon-reload用重启nginx服务

      • 访问效果

        【跳转百度】

        【跳转尚硅谷】

        完全显示的是nginx代理服务器的ip

        【使用自己的域名跳转尚硅谷】

    • 如果proxy_pass的属性值不带www,请求nginx代理服务器会发生重定向

      • nginx.conf

        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 / {
                    proxy_pass http://atguigu.com;
                    root html;
                    index index.html index.htm;
                }
        
                #出错跳转nginx目录下的错误页
                error_page   500 502 503 504  /50x.html;
                location = /50x.html {
                    root   html;
                }
            }
        
        }
        
      • 代理效果

        配置文件中的proxy_pass不带www,此时再用nginx地址访问192.168.200.132地址栏会直接显示尚硅谷的地址

        【执行效果】

        使用IE浏览器才能看见,Chrome是看不到第一个请求的,从信息中能看到nginx服务器的版本是1.20.2,原请求返回状态码302【http协议中的重定向状态码,让浏览器重新加载响应头中的location】,要求重新跳转,响应头中的跳转地址是http://www.atguigu.com/

        跳转https协议的时候会发生相同情况,即302状态,location中是https的重定向地址;而且nginx.conf的proxy_pass不支持反向代理https服务器,https服务器需要与域名对应上【后面会讲证书和域名之间的关系】,据说https可以代理,只是https报错是因为没装openssl以及编译时没开启ssl模块

        重定向的原因没讲,猜测是服务提供商针对其他域名做了重定向处理

Nginx代理一台主机
  1. nginx代理一台主机

    创建第二台虚拟机上192.168.200.133的nginx,配置文件修改如下

    • nginx.conf

      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服务器访问效果

      更改/usr/local/nginx/html目录中欢迎页添加第二台虚拟机的IP信息,用浏览器访问查看效果

    • 第一台nginx服务器的proxy_pass指向第二台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 / {
                  proxy_pass http://192.168.200.133;
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 通过第一台nginx代理服务器访问第二台nginx代理服务器效果

      这只是一台nginx代理一台服务器,还可以配置nginx代理到多台服务器上

Nginx代理多台服务器

创建第三台nginx虚拟机

  1. nginx服务器访问效果

基于反向代理的负载均衡

一台nginx把请求代理到应用服务器后还会有非常多的服务器集群

  1. 负载均衡器

    女朋友一个月总有几天不能用,不能用怎么办,找一个备胎,不能用的时候备胎续上,服务不行了就下线

    • nginx对服务集群做反向代理可以添加负载均衡功能,负载均衡策略(算法)包括轮询、IP哈希、定向流量分发、随机,以满足服务下线维护,短时间故障期间的服务正常提供
    • 存在请求已经发送给服务器,但是才发现服务宕机,此时产生一种retry【重试】机制来解决这个问题,再去找一台正常的机器来处理该请求
  2. 配置负载均衡

    负载均衡的配置需要和proxy_pass配合使用,配置了三台nginx服务器,想要实现在第一台nginx服务器上负载均衡访问第二台和第三台nginx服务器,访问第一台nginx服务器一会儿显示133服务器,一会儿显示144服务器

    • 当反向代理服务器需要代理多台服务器时,需要将proxy_pass属性的属性值弄成http://自定义名,这个自定义名是随便起的,可以用来代替服务器IP,但是要和upstream 自定义名中添加多台代理机器的IP和端口号【为了统一,将端口号也配置上,80端口不写其实也是可以的,规范一点】,upstream 自定义名和server是同一级的配置,配置详情见nginx.conf

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
          upstream proxyServers{
              server 192.168.200.133:80;
              server 192.168.200.134:80;
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://proxyServers;
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 多台机器负载均衡效果

      但是没有讲怎么配置nginx对多台服务器的代理,这里添加了负载均衡,去掉负载均衡就是配置对多台服务器的代理效果

      负载均衡策略也是轮询策略,轮询应该是nginx默认的负载均衡策略

Nginx负载均衡策略

准备了一台nginx代理服务器和三台用来测试负载均衡策略的nginx服务器。ip分别为192.168.200.132192.168.200.133192.168.200.134192.168.200.135,用132作为代理服务器剩余三台作为被代理服务器测试负载均衡策略,以上服务器都在欢迎页添加了服务器IP信息

动态分配nginx服务器的上下线,仅靠nginx的基础命令如down和backup是不行的,一般来说,所有在线机器都挂掉,很有可能是程序运行导致服务器出现共性问题宕机,备用服务器上线也不一定好使,所以实际down和backup都不常用,down需要修改配置文件并在服务器reload一下,这样一般是来不及的,有这个时间可以考虑直接再向集群中再加入一台服务器,所以down和backup实际生产中使用比较少,负载均衡的权重是比较有用的

权重
  1. 权重负载均衡策略

    给每台服务器设置权重值,有的服务器配置比较高或者网络出口带宽比较高,比如133机器的网络带宽为1000M,134机器的网络带宽为100M,设置133的权重为8,设置134的权重为2,设置135的权重为1;使用nginx代理服务器做负载均衡,每八次请求访问133机器后,每两次请求访问134机器,每一次请求访问135【并不是完全按照8-2-1的顺序执行的,是多次请求中的访问比例满足8:2:1】

    • nginx.conf

      配置被代理服务器的权重是在upstream中server后面配置weight属性

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
          upstream proxyServers{
              server 192.168.200.133:80 weight=8;
              server 192.168.200.134:80 weight=2;
              server 192.168.200.135:80 weight=1;
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://proxyServers;
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 测试效果

  2. Down服务下线

    down可以让某些机器不参与负载均衡,相当于服务直接下线,不再提供服务

    • nginx.conf

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
          upstream proxyServers{
              server 192.168.200.133:80 weight=8 down;
              server 192.168.200.134:80 weight=2;
              server 192.168.200.135:80 weight=1;
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://proxyServers;
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 测试效果

  3. Backup备用服务器

    备用服务器,指正常情况下不使用被backup标记的服务器,只有其他的机器实在没得用了,才用备用的服务器

    • nginx.conf

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
          upstream proxyServers{
              server 192.168.200.133:80 weight=8 down;
              server 192.168.200.134:80 weight=2 backup;
              server 192.168.200.135:80 weight=1;
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://proxyServers;
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 测试效果

      使用命令systemctl stop nginx能停掉nginx服务

其他负载均衡策略

除了轮询和权重,其他的负载均衡策略声场上基本不使用,只做了解即可,最核心的问题是这些负载均衡策略无法做到服务器动态上下线,即即时的上下线服务器;类似hash值映射服务器的对应映射都写死在配置文件中,服务器上下线非常的不灵活死板,特殊场景使用这些方式需要结合Lua脚本在nginx中去编程来动态的管理当前服务器列表,可以动态的监测后端服务器的上下线情况,用程序配置权重值,做定向流量转发以及定向用户转发【定向用户转发存在严重的流量倾斜问题,非常容易造成几台服务器繁忙,几台服务器空闲的情况】,实际生产要么直接使用轮询的方式【存在无法保持会话的问题】,要么就用lua脚本去自定义请求转发规则,直接使用第三方插件的方式非常不灵活

轮询存在无法保持会话的问题:用户登录tomcat服务器,会将用户的状态存储在session中,这个用户状态只是存储在当前这一台tomcat服务器中【但是不是可以通过token令牌实现单点登录吗? 】,其他tomcat服务器中没有存储对应的session,此时用户的访问会被拒绝,凡是需要session的操作都会出现问题,单纯使用轮询负载均衡策略是会出现问题的

  1. ip_hash

    • nginx会根据请求来源的ip地址将来源相同的请求转发给同一台服务器

      存在问题,比如手机的信号时好时坏【比如乘坐高铁飞机移动速度很快】,通信商为了维持信号,将信号切换了一个移动基站,此时ip地址变化,没法保证处在不同地方的移动请求一定访问同一台服务器,无法保持会话状态

      所以一般也不会用ip_hash来做负载均衡保持会话状态

  2. least_conn

    • 最少连接数的访问,目的是让后端服务器更加均衡,原理是将每次请求转发到连接数最少的服务器上,

    • 这种方式在实际生产中也有很大的缺陷,服务器的连接数少可能是考虑到机器性能给其配置的权重比较低,才会造成流量倾斜,

      • 正常情况下,不同服务器之间的连接数差异很大这是不大可能发生的;
      • 即使在新加入机器的时候,此时除了新加机器,其他服务器全部进入reload阶段,即不接收新的请求,只处理已经接收的请求,等到新加机器正式上线,所有服务器此时的连接数都趋近于0。只有比较耗时的请求,比如需要两分钟才能解决的请求,才会出现连接数偏差较大的情况,但是几乎见不到这样的请求处理方式【用户点击一下,等待2分钟跳转页面,如文件上传,异步任务呢?如文件上传下载服务器?我真牛逼,马上就讲了除非异步化的处理,以消息队列的方式等待慢服务处理完成以后讲处理状态通知给用户即可,也不需要业务服务器进行长时间的处理并让用户一直等待】,所以这种负载均衡方式没有常用的场景,权重方式下还要慎用,所以几乎看不着这种方式的使用
  3. fair

    • 这种方式需要使用第三方的插件,配置到原始的nginx中才能使用,这种方式根据后端服务器的响应时间来转发请求,这种方式也不是很合理,

      响应时间长短还和网络延迟【交换机过热就会造成网络延迟比较高,当前服务器和其他服务器接入的网络交换机不是同一个,就可能存在网络延迟差异的情况,导致网络延迟小的服务器会短时间处理大量的请求,可能直接就将服务器压垮了】、业务逻辑有关系,如果只根据响应时间来判断转发逻辑,会造成短时间内响应时间短的服务器的请求突然急剧增加,短时间内就可能压垮服务器,实际响应时间长的服务器空闲的很,

    • 因为存在流量倾斜的风险并且需要下载第三方插件,所以实际生产这种方式使用的也比较少

  4. url_hash【定向流量转发】

    • url_hash默认也是不支持的,也需要第三方插件,url_hash是定向流量转发,不一定是定向的用户转发,ip_hash就是定向的用户转发,根据用户的请求url如http://atguigu.com/register计算哈希值,相同哈希值的url转发到同一台服务器上,这就是定向流量转发,比如登录和注册的哈希值不同会转发到不同的服务器上,在这种情况下,会话状态也无法保持,比如刚注册就要用户登录,这种方式比较适合访问固定资源不在同一个服务器【比如100个文件散落在不同的服务器,此时就可以根据URL去定位文件在哪台服务器上】的场景使用,几乎只有这样一种情况才需要用到url_hash
  5. 通过$request_uri做负载均衡

    使用场景:访问相同url会转发到同一个服务器,适合用在没有cookie的场景如手机APP,将jsessionid直接带在url的最后,即使相同的请求也能根据jsessionid的不同将用户分配到特定的服务器来管理会话【如果只是集群系统并不是分布式系统下能这么用吧,而且uri整体计算的哈希值也可能不同啊,为什么能保持会话,这不是和url_hash是一回事吗?】

    这个是将系统扩容会话管理的时候讲的,不知道和url_hash有什么关联没

    • 在不支持cookie的情况下【手机上的APP不支持cookie】可以直接把jsessionid直接带在URL的后面,根据用户的jsessionid来对用户进行会话管理【存疑,是对jsessionid做哈希还是对整个uri做哈希,对整个uri做哈希还是可能被派发到其他服务器上,一样无法维持会话,而且还得是单一系统的集群才行啊】
    • 第二种情况就是类似url_hash的情况,当资源有所倾斜的时候,根据uri去确定资源位于哪一台服务器上【比如流媒体服务器,文件太大,不可能分发到所有的流媒体服务器上,也没有必要】,文件上传的时候根据请求的url算出哈希值放上对应的服务器【还是有一些问题,如做集群,其实都能解决,再代理一台给集群做负载均衡,虽然不太优雅,实际方案不了解,有机会学习一下】
会话状态保持

保持会话状态一般不用nginx自带的负载均衡策略,这些负载均衡策略为了会话状态保持业务方面肯定会出问题

  1. 轮询无法保持会话状态
    • ip_hash可以保持会话状态,但是这种方式并不好,存在严重的流量倾斜问题且移动端ip随基站变化会发送变化,
    • java中存在基于客户端的会话保持工具,因为后端服务器只要做到轮询就没办法做到有状态【有状态指在独立的服务器上存储用户的固定信息,比如session就是用户状态信息,服务器存储了session才会 】,解决方案:
      • springSession默认解决方案把session单独存在redis服务器上,根据请求的cookie在本机找session,找不到就去redis服务器上找session【这也是最基本的集群化session共享,session不会每台服务器都存一份】,这种方式不适用于高并发场景,
      • 高并发情况下需要使用真正的无状态会话token;用户请求到nginx服务器,nginx服务器会转发到一台专门负责权限校验认证的服务器,当用户登录校验完权限后给用户下发权限,权限记录成一个比较长的字符串或者记录在文件中并根据用户登录时间和有效期限以及服务器密码生成加密token,将token下发到客户端,客户端无法对token进行更改,客户请求带token,服务器不存储客户状态,只拿token做校验,只有服务端有密码可以解开token的加密信息【?拿到别人的token在别的客户端使用怎么办,大佬们说是可以的,但是拿不到token,以后看能不能回答这个问题】,这也是现在比较主流的方式

动静分离

动静分离适合中小型的网站,大型网站一般不这么操作,因为小型网站的并发量不高,且需要分离出来的静态资源不是特别多,把这些静态资源挪niginx中,如果是大型系统那么文件就太多了【淘宝,用户上传的文件就非常多,买家秀,卖家秀,商品介绍都属于静态资源,不适合使用动静分离这种简单的技术架构,适合初创企业、网站H5内嵌到APP或者网站中展示,也适用于ERP系统等传统项目】

  1. 动静分离架构

    动静分离可以起到给系统加速的作用,用户请求通过网关,很多时候只需要展示一些图片,js、css等静态文件;此时可以把这些文件放在nginx中,不让这些请求进入后端的服务器,因为一个网页一般都是一个动态响应的主页+内嵌的数个静态资源,不使用动静分离的情况下,所有的请求都会被代理到tomcat,一个网页可能会有上百个请求,还有nginx代理转发的过程,这样不好;如果把静态资源全部前移至nginx,让nginx来响应这些静态资源的请求,让tomcat专心于动态请求的处理,通过动静分离给系统加速

    【传统静态资源架构】

    用户请求被nginx代理到tomcat,原本项目全部部署到tomcat服务器的某个目录下如webApp,webApp中包含项目类似于SpringBoot相关的jar包,jar包的static目录中会包含一些静态资源,static目录下的静态资源可能存在文件无法上传的问题【可以通过配置配置在外部目录中】,第二个问题是tomcat会响应很多非动态的请求,用户请求打到网页,网页会内嵌很多的图片、css、js资源,一次请求无法完成任务,用户请求到tomcat,tomcat会返回一个主页index.html,这个主页中会镶嵌很多的图片等静态资源,用户此时还会发起请求给nginx,再被代理到tomcat上去找相关的静态资源

    【多次请求说明】

    www.jd.com的响应就是一个静态页面,这个响应一般是后端的动态服务器返回的静态页面,该静态页面关联了非常多的图片,css文件,js文件资源,会同时再次并发发起N多个请求给服务器,使用传统架构,这些请求都会并发打到tomcat上,而且页面只要移动,很可能还会发起新增请求,比如鼠标滚轮加载页面,课程演示时又多了100多个请求,都是页面内嵌的图片等静态资源;tomcat的功能主要响应动态的请求,将这些静态资源前置到nginx可以将动态请求和静态请求分离开,静态资源的请求不需要再被代理到tomcat,直接就被nginx处理了响应了

配置动静分离

传统项目经过这样的改造能够大大提高系统的并发量,因为静态文件全部都交给nginx来处理,nginx处理静态文件的能力和tomcat处理静态文件的能力不相上下,不像网上传闻nginx的QPS有两万,tomcat为800完全是胡扯,因为现在java、tomcat已经比较先进了,先进在于利用操作系统底层的NIO,操作系统内核支持高性能网络数据的传输,才能达到这么高的性能,但是每次建立连接tomcat还是会有额外的会话级别的开销,开销还是比nginx高,性能比nginx略低,但是低的不多,因为现在还有keepalive技术的存在,

  1. tomcat服务器中的静态资源

    tomcat动态服务器上的静态资源,tomcat跑在8080端口

    没有资料,有资料我高低也部署一个

    【tomcat中欢迎页的访问】

    【源码】

    F12Source选项卡下

  2. nginx对tomcat服务器的初始配置

    nginx和tomcat在两台虚拟机上,没有搭建项目,借用尚硅谷张一鸣老师的配置演示效果

    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
    
    	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
        upstream proxyServers{
            server 192.168.44.102:80 weight=8 down;
            server 192.168.44.103:80 weight=2 backup;
            server 192.168.44.104:80 weight=1;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            location / {
            	#proxy_pass指向tomcat服务器的ip和端口
                proxy_pass http://192.168.44.104:8080;
                root html;
                index index.html index.htm;
            }
    
            #出错跳转nginx目录下的错误页
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    

    【测试效果】

    104ip上的8080端口的tomcat成功101nginx代理服务器代理

  3. 访问tomcat过程解析

    • 用户首次访问tomcat有一个会话的概念,即session,不管用户请求啥都会去检查session,而且tomcat运行在JVM中,执行效率从语言层面来说略低于nginx,首次连接tomcat会生成session并保持链接【keepalive】,因为keepalive可以复用之前的网络连接通道,不至于创建出无数个连接诶;比如同时有200个请求打个tomcat,可能中间会握手连接十几次,连接都会得到复用,因为大多文件请求是瞬时的过程,
    • 以京东为例,请求后的页面加载不是一次性加载完毕,分多个阶段,当滚轮滑到一定位置需要展示时才会去加载资源,不至于资源加载请求的并发量特别的高,比如一次并发二三十个请求,建立十几二十个链接,再有这种请求时还会复用之前的连接,
    • tomcat还会利用底层的一些技术如epoll【高性能网络接口,网络数据传输变得很快,只是java和tomcat调用的系统底层实现】、
    • 静态和缓存一类的东西利客户端越近越好,减少网路开销,在内网环境下,nginx转发静态资源请求给服务器,nginx再接收服务器的静态资源是没必要的开销,让nginx读取静态资源响应给客户端效率最高,资源开销最小
简单配置

缺点:根据请求路径匹配站点目录【路径匹配规则,优先匹配最长的,最长的匹配不了再匹配短的。所以先匹配下面的】,目录多的情况下location要配置非常多,不像虚拟主机的server_name的配置属性值能写很多个

  1. nginx对静态资源的第一种简单配置

    适用于静态资源少,目录少的情况

    • 为了演示效果,上传完成后将104上的静态资源全部删掉

      【删掉后的访问效果】

      样式没了,下拉菜单也没了,部分图片也没了,注意此时nginx服务器上还没有对应的静态资源

    • 在nginx代理服务器上虚拟几个目录,将静态资源上传到nginx服务器对应的目录下,

      【使用proxy_pass,配置目录失效】

      此时考虑的是静态资源放在nginx服务器的哪个地方,因为使用poxy_pass,此时直接使用html目录是无效的,所有访问html目录下的请求都会被代理到配置的tomcat服务器上,此时考虑如何对静态资源目录进行配置,此时需要配置多个location【后面详细讲location配置规则】,使得根据请求路径判断文件的地址

      【location即静态资源目录配置】

      配置location添加站点URI以/css打头的访问nginx服务器下的html目录

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
      	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.103:80 weight=2 backup;
              server 192.168.44.104:80 weight=1;
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
              	#proxy_pass指向tomcat服务器的ip和端口
                  proxy_pass http://192.168.44.104:8080;
              }
              
              #uri打头为/css的请求会走nginx在该站点下的对应的根目录去寻找资源
              location /css {
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      

      【将css文件拷贝到根目录下】

      注意配置的uri打头为css,此时也只上传了css文件,并没有上传另外两个img和js文件到该目录下

    • 测试效果

      此时样式已经有了,注意此时样式相关的文件在nginx服务器的根目录html下的css目录下,tomcat中的该文件已经删除

      【对应css文件的请求路径】

      uri打头带/css

    • 完整配置

      将静态资源目录都配置到nginx的html目录下,即将html目录配置为’/css’,‘/js’,'/img’作为uri打头情况下的请求的资源根目录

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
      	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.103:80 weight=2 backup;
              server 192.168.44.104:80 weight=1;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
      		#默认规则,匹配所有以'/'打头的请求URI,优先级比较低,测试过程能够看到凡是有/的子路径,就会去匹配请求的子路径/css,符合优先精确匹配的原则
              location / {
              	#proxy_pass指向tomcat服务器的ip和端口
                  proxy_pass http://192.168.44.104:8080;
              }
              
              #uri打头为/css的请求会走nginx在该站点下的对应的根目录html去寻找资源
              location /css {
                  root html;
                  index index.html index.htm;
              }
              
              #uri打头为/js的请求会走nginx在该站点下的对应的根目录html去寻找资源
              location /js {
                  root html;
                  index index.html index.htm;
              }
              
              #uri打头为/img的请求会走nginx在该站点下的对应的根目录html去寻找资源
              location /img {
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      

      【静态资源文件上传】

      不同字符打头的uri请求路径都要单独配置location,但是都可以配置成同一个根目录

    • 最终测试效果

正则配置

写正则表达式可以将上述三个匹配路径合并成一个正则表达式,只需要多写一个location

  1. 配置详情

    【nginx.conf】

    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout  65;
    
    	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
        upstream proxyServers{
            server 192.168.44.102:80 weight=8 down;
            server 192.168.44.103:80 weight=2 backup;
            server 192.168.44.104:80 weight=1;
        }
    
        server {
            listen       80;
            server_name  localhost;
    		
    		#默认规则,匹配所有以'/'打头的请求URI,优先级比较低,测试过程能够看到凡是有/的子路径,就会去匹配请求的子路径/css,符合优先精确匹配的原则
            location / {
            	#proxy_pass指向tomcat服务器的ip和端口
                proxy_pass http://192.168.44.104:8080;
            }
            
            # ~* 表示要开始写正则表达式了,区分大小写的正则 ~ 开头,不区分大小写的正则 ~* 开头,(css|js|css)表示\后匹配括号中内容, | 表示或,
            location ~*/(css|js|css) {
                root html;
                index index.html index.htm;
            }
    
            #出错跳转nginx目录下的错误页
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    
  2. 测试效果

    开启Disable cache的情况下反复刷新,让请求不要走缓存

  3. 动静分离存在的问题

    注意使用nginx管理静态css、js文件和图片,此时再直接访问tomcat服务器,tomcat中没有对应的静态资源,页面的样式、js函数和照片都无法加载

URLRewrite

能够隐藏真实的后端地址

  • http://192.168.44.101/index.jsp?pageNum=2这是查询分页评论的链接,连接长且暴露入参,可以将该连接隐藏起来,让请求连接伪静态变成http://192.168.44.101/2.html
  1. 配置URLRewrite伪静态

    • 通过proxy_pass同一个站点的rewrite关键字指定,属性值使用正则表达式

    • nginx.conf

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
      	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.103:80 weight=2 backup;
              server 192.168.44.104:80 weight=1;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
      		#默认规则,匹配所有以'/'打头的请求URI,优先级比较低,测试过程能够看到凡是有/的子路径,就会去匹配请求的子路径/css,符合优先精确匹配的原则
              location / {
              
              	#正则表达式以^开头,以$结尾,正则表达式匹配访问nginx服务器的该站点下的URI部分;演示用的是静态的形式,即定死2.html,入参也定死了2
              	#/index.jsp?pageNum=2是真实的访问地址;
              	#最后的break是请求转发的形式,转发形式FLAG 有 4 种:break、last、redirect、permanent;break 表示本条规则匹配完成即终止, 不再匹配后面的任何规则。
              	rewrite ^/2.html$ /index.jsp?pageNum=2 break;
              	
              	#proxy_pass指向tomcat服务器的ip和端口
                  proxy_pass http://192.168.44.104:8080;
              }
              
              # ~* 表示要开始写正则表达式了,区分大小写的正则 ~ 开头,不区分大小写的正则 ~* 开头,(css|js|css)表示\后匹配括号中内容, | 表示或,
              location ~*/(css|js|css) {
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 伪静态页面配置效果

      显示的是访问静态资源2.html,但是本机实际没有2.html这个资源

      不足:没有介绍怎么实现动态的将客户端的参数传递给实际的请求地址,仅仅只是配置了一个静态页面

  2. 配置动态参数的伪静态URLRewrite

    • nginx.conf

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
      	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.103:80 weight=2 backup;
              server 192.168.44.104:80 weight=1;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
      		#默认规则,匹配所有以'/'打头的请求URI,优先级比较低,测试过程能够看到凡是有/的子路径,就会去匹配请求的子路径/css,符合优先精确匹配的原则
              location / {
              
              	#[0-9]+表示匹配任意位数字,正则表达式中使用小括号括起来的部分表示URLRewrite时用来入参的参数,实际地址中的$1表示入参选择正则表达式中第一个括号括起来的内容,如果有第二个入参可以用$2表示使用第二个括号括起来的参数,可以使用多个参数
              	rewrite ^/([0-9]+)$ /index.jsp?pageNum=$1 break;
              	
              	#proxy_pass指向tomcat服务器的ip和端口
                  proxy_pass http://192.168.44.104:8080;
              }
              
              # ~* 表示要开始写正则表达式了,区分大小写的正则 ~ 开头,不区分大小写的正则 ~* 开头,(css|js|css)表示\后匹配括号中内容, | 表示或,
              location ~*/(css|js|css) {
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 配置效果

      根据客户端地址的参数将入参参数动态的传递给真实请求地址

  3. 参数说明

    • rewrite是实现URL重写的关键指令,根据regex (正则表达式)部分内容,重定向到replacement,结尾是flag标记。

      rewrite/*关键字*/ <regex>/*正则*/ <replacement>/*替代内容*/ [flag]/*flag标记*/

    • 参数要领

      • 关键字:关键字中的error_log不能改变,rewrite关键字可以使用的标签段位置:server、location、if;

      • 正则:perl兼容正则表达式语句进行规则匹配

      • 替代内容:将正则匹配的内容替换成replacement

      • flag标记:rewrite支持的flag标记,有四种

        rewrite关键字有优先级区分,break的优先级最高,

        flag标记类型标记含义
        last本条规则匹配完成后,继续向下匹配新的location URI规则,一直匹配到配置文件最下面的URI规则
        break本条规则匹配完成即终止,不再匹配后面的任何location URI规则,继续执行后续的proxy_pass
        redirect返回302临时重定向,响应页面后浏览器地址会显示配置文件中当前页面真实的URL地址
        permanent返回301永久重定向,浏览器地址栏会显示跳转后的URL地址

        【flag标记为redirect的显示效果】

        相当于nginx发起302请求,让客户端自己重定向到真实地址

        临时重定向和永久重定向对用户来说没有区别,是给网络爬虫看的,比如百度来爬取网页,临时重定向以后还可以爬取,永久重定向永远记录重定向地址,这里讲的不清楚,无伤大雅,有机会了解网络爬虫再结合学习

        注意:重定向ip地址还是nginx的地址192.168.44.101,不是tomcat104的地址

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网关服务器

同时带有代理服务器、负载均衡和URLRewrite功能的服务器就能被称为网关服务器,网关配置可以直接用cloud的gateway的filter的RewritePath去写,可以不用nginx配置

目前nginx已经用上了反向代理、负载均衡、动静分离和URLRewrite的功能了

  1. 当前系统架构

    nginx在此处担任起了反向代理,动静分离和URL重写的作用,此时直接访问tomcat会导致一些资源无法访问,需要设置tomcat只能从内网访问,不能从外网访问;

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 设置防火墙关闭tomcat外网访问

    直接开启防火墙不设置端口通讯会切断一切外网tcp端口的连接,但是注意这种情况下xshell还是能连接的,此时打开tomcat所在的8080端口,但是又不希望外网访问到,只希望nginx能访问到

    • 使用命令firewall-cmd --permanent --add-rich-rule="rule family="ipv4" source address="192.168.44.101" port protocol="tcp" port="8080" accept"添加nginx反向代理服务器的ip地址192.168.44.101为可信任的ip地址,并对该地址在104机器上开启tcp协议上的8080端口通讯

    • 此时104机器上的tomcat只能被反向代理服务器101访问,外网已经访问不了该tomcat了,实现了不介入互联网,通过nginx反向代理服务器将一切资源转交给外网,如果nginx挂掉了,在这种配置的前提下这台tomcat服务器就彻底访问不到了

      此时nginx就相当于tomcat服务的大门,大门一旦关闭了tomcat服务器的任何服务都访问不了了,此时nginx换个更专业的名字就是网关服务器,之前叫代理服务器、负载均衡器都很片面,无法包含他的一切功能【反向代理服务器、负载均衡器、URLRewrite、】

  3. 整合nginx的反向代理、负载均衡、URLRewrite完整功能

    proxy_pass使用upstream时ip和端口号都写在upstream中,rewrite还是写在proxy_pass的上面,只是匹配uri

    • nginx.conf

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
      	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.104:8080 weight=1 backup;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
              location / {
              	rewrite ^/([0-9]+)$ /index.jsp?pageNum=$1 break;
              	
                  proxy_pass http://proxyServers;
                  
              }
              
              location ~*/(css|js|css) {
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 访问效果

      102服务down了,此时只会显示备用的tomcat服务器

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

防盗链

防盗链指存在服务器上的资源只能由我们自己的服务器进行访问,其他引用的服务器无法访问,

  1. 盗链

    当用户访问某个主页,主页响应以后会二次甚至多次去请求当前站点主页中用到的引用资源【css、js文件和图片资源】,二次请求的时候会在请求的时候在请求头中添加referer参数,参数值就是首次请求的请求地址的URL,这是http协议规定的,由浏览器遵守的规定,只会出现在二次请求中,首次请求的请求头中是没有的,表示本次请求是从第一次请求的响应页面过来的;

    此时可以根据请求头中是否含有referer属性来判断该请求的来源是一次请求响应结果进行的二次请求,还是某个用户单独进行的请求【用户无法操纵请求头中的referer的属性值,因为网络数据想要安全,涉及到的HTTP协议内容都必须由浏览器本身来进行填充?但是我试过向请求头中添加token】

    • 如果别的站点对相同资源进行了引用,二次请求时可以根据请求头中的referer属性判断对资源的请求是否是自己的站点,因为referer属性是浏览器进行填充的,合法的浏览器是无法进行篡改的,如果不是自己的站点就可以判断是非法请求【说明有其他的网站引用了自己不想让别人引用的资源,允许别人引用就不需要对防盗链进行配置】;
    • 一般来说,网站上的资源希望别的站点使用,增加曝光量,不是所有的资源都需要防盗链;一般防盗链的使用场景如公众号上的图在自己的网站上引用就会提示不能在外地引用该图片资源【也是不缺流量或者资源的size很大,或者资源很稀缺,只希望用户只能在该站点才能看到该资源才会这样设置】
  2. 防盗链配置

    新建一个站点102,配置站点102的proxy_pass为101,此时访问102会直接跳到101,

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    此时我们想实现被别的站点代理的二次请求的css、js和图片文件不能被访问的配置方法

    • 更改101的配置文件nginx.conf【此时101是静态资源存放的服务器】

      需要把对资源的限本站点访问的设置配置到资源站点即location下,不能配置在service下

      本例中不希望css、js文件和图片不能被访问,需要配置在location ~*/(css|js|css)站点下

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
      	#因为proxy_pass已经指向tomcat服务器的ip和端口号,这个upstream实际已经不认识了,但是不会报错,显示也应该只显示tomcat服务器内容
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.104:8080 weight=1 backup;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
              location / {
              	rewrite ^/([0-9]+)$ /index.jsp?pageNum=$1 break;
                  proxy_pass http://proxyServers;
              }
              
              location ~*/(css|js|css) {
              
              	#检测二次请求的主机ip是否符合配置项,设置有效的referer的ip为101,如果访问的referer不是有效的referer,就返回错误码403,该站点下的下述配置指定站点目录的配置就不走了,就达到了访问该站点下限制访问的静态资源的站点目录找不到了
              	#有效的ip可以设置多个,referer为这些ip的时候可以不限制访问
              	valid_referers 192.168.44.101;
              	#注意此处if和()之间必须有空格
                  if ($invalid_referer) {
                  	return 403;
                  }
                  
                  root html;
                  index index.html index.htm;
              }
      
              #出错跳转nginx目录下的错误页
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      
    • 配置效果

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【请求状态】

      二次请求的response中没有数据,头信息中的状态码为403

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【其他服务器单独访问图片效果】

      仍然无法访问,还是报错403

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【地址栏输入地址访问效果】

      不仅是引用访问非法,直接地址栏访问也是不行的,直接在地址栏输入其他服务器ip加资源路径也是不行的,只要访问了非本机服务器,而是中途被代理的服务器,就无法访问被限制访问的资源,只能访问对应的站点才能访问相应的资源

      也可以使用none关键字配置非应用但是地址栏访问允许的设置,即检测到请求中不带referer就允许访问

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 配置非引用状态下的资源允许访问

      从被引用界面点击新标签打开图片,这种情况下带referer,这种情况下是无法访问的,虽然地址栏的地址是一样的,只能通过在地址栏输入地址且请求头不带referer属性才能访问加了none关键字对应资源

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.104:8080 weight=1 backup;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
              location / {
              	rewrite ^/([0-9]+)$ /index.jsp?pageNum=$1 break;
                  proxy_pass http://proxyServers;
              }
              
              location ~*/(css|js|css) {
              
              	#添加了none关键字,此时请求非引用即不带referer就可以从别的服务器访问对应的资源,但是引用不可以
              	valid_referers none 192.168.44.101;
                  if ($invalid_referer) {
                  	return 403;
                  }
                  
                  root html;
                  index index.html index.htm;
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
          }
      }
      

      【地址栏直接访问效果】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【通过引用在新标签打开图片的方式访问效果】

      带referer不行

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 关键字意义

      • none, 检测 Referer 头域不存在的情况。即请求没有referer属性即是直接访问不是引用访问的情况

      • blocked,检测 Referer 头域的值被防火墙或者代理服务器删除或伪装的情况。这种情况该头域的值不以“http://” 或 “https://” 开头。

        不常用,即请求的referer不带http或者https的情况下资源可以访问,目前可以看到referer中是带http

  3. 使用curl测试防盗链

    curl工具,该工具是linux系统下的工具,默认不会安装,windows是自带的,curl在做测试方面更加纯粹,更加方便一些;浏览器为了给用户做访问加速会在多个地方做缓存,导致用户刷新时可能无法即时刷新最新的页面,可以使用curl测试配置是否生效更加方便准确

    • 使用命令yum install -y curl在linux上安装curl

    • 使用命令curl -I http://192.168.44.101/img/logo.png对目标地址发起请求,返回响应的头报文信息,不带-I会完整的显示响应内容,包括响应体

    • 使用命令curl -e "http://baidu.com" -I http://192.168.44.101/img/logo.png对目标地址发起带引用的访问请求,即在请求头信息中添加referer参数

      注意,带引用的引用值不匹配设置的引用值,即使是访问原服务器地址的资源请求也一样会返回设置的403错误码

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

防盗链友好提示配置

实际生产中防盗链拒绝访问不会直接返回错误码,会响应一个对应错误提示的图片或者错误的提示页面

  1. 防盗链生效返回错误提示页面

    • 在101服务器的根目录html中创建对应的错误页面401.html

    • 在nginx.conf中配置错误代码对应的站点资源

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.104:8080 weight=1 backup;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
              location / {
              	rewrite ^/([0-9]+)$ /index.jsp?pageNum=$1 break;
                  proxy_pass http://proxyServers;
              }
              
              location ~*/(css|js|css) {
              
              	#添加了none关键字,此时请求非引用即不带referer就可以从别的服务器访问对应的资源,但是引用不可以
              	valid_referers none 192.168.44.101;
                  if ($invalid_referer) {
                  	return 401 ;
                  }
                  
                  root html;
                  index index.html index.htm;
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
              
              error_page   401  /401.html;
              location = /401.html {
              	#在root目录即html目录下去找资源401.html
                  root   html;
              }
          }
      }
      
    • 访问效果

      在页面引用中还是和原来一样,原因是文档的编码方式和图片的编码方式不同,当单独在新地址栏带引用访问对应资源就会显示错误页信息

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 整合URLRewrite返回报错图片

    当引用的图片发生拒绝访问的情况,如果是跳转一个报错页面,内嵌于引用网页的图片不能显示对应的报错页面,但是这种方式本质上已经消耗了本地服务器的流量,请求经过处理也响应了内容,用URLRewrite的方式能返回一个友好提示的图片替代原来访问的图片

    • 准备好对应的友好提示图片x.png【名字随意】,

    • nginx.conf

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
      
          upstream proxyServers{
              server 192.168.44.102:80 weight=8 down;
              server 192.168.44.104:8080 weight=1 backup;
          }
      
          server {
              listen       80;
              server_name  localhost;
      		
              location / {
              	rewrite ^/([0-9]+)$ /index.jsp?pageNum=$1 break;
                  proxy_pass http://proxyServers;
              }
              
              location ~*/(css|js|css) {
              
              	#添加了none关键字,此时请求非引用即不带referer就可以从别的服务器访问对应的资源,但是引用不可以
              	valid_referers none 192.168.44.101;
                  if ($invalid_referer) {
                  
                  	rewrite ^/ /img/x.png break;
                  	
                  	#需要用图片提示友好信息,要注释掉返回的错误码,使用rewrite来指定满足条件后的真实访问地址
                  	#return 401 ;
                  }
                  
                  root html;
                  index index.html index.htm;
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
              
              error_page   401  /401.html;
              location = /401.html {
              	#在root目录即html目录下去找资源401.html
                  root   html;
              }
          }
      }
      
    • 引用图片访问效果

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

nginx的高可用配置

高可用配置【Highly Available】,一台nginx做代理服务器、负载均衡器可能会发生单点故障导致后端服务完全不可用,此时要考虑配置多台nginx服务器来避免nginx不可用的情况,

此时会发生要实现nginx的切换或者nginx服务器的负载均衡需要再加一台代理服务器或者负载均衡器在nginx服务器集群之前,但是此时又会存在负责nginx服务器集群服务器切换和负载均衡问题的服务器需要设置集群,那么这样永远存在不停地在nginx服务器集群前加机器的问题,这样在nginx服务器集群前加机器控制nginx服务器的切换无法解决nginx服务器集群的控制切换问题

通过keepalived技术可以实现在不在nginx服务器集群前加机器的前提下实现nginx服务器的切换和控制,keepalive就是一个小小的软件,跑在nginx服务器集群的每个nginx服务器中,keepalived的作用是实现不同机器上的keepalived的通讯,检测集群中机器是否存在宕机,

  1. nginx集群的运行

    • 局域网内不同的nginx服务器的ip地址不能相同,用户请求主要用111的nginx服务器进行请求代理,如果nginx服务器111挂掉,keepalived会发现111机器挂掉,并将用户请求代理的地址改成112nginx服务器

    • 但是如何判断一台机器是真的宕机了,还是假死状态【交换机过热、网络分区故障】,

      网络分区故障:机房一个机柜中存在多台不同ip的主机,每个机柜对应有自己的小型交换机,如果交换机和交换机之间发生了通讯故障就发生了网络分区故障【即同一个机柜中不同ip的机器之间的通讯没问题,由于交换机之间的通讯故障,两个机柜之间的机器通讯断掉了,即网络分区故障】

      当发生故障原nginx服务器假死,如果此时通过修改新nginx服务器的ip地址为原故障nginx服务器的ip【两台nginx服务器所处的机柜不同】,当原nginx服务器正常恢复以后,会发生ip冲突的情况;

      多个nginx服务器是以主从的方式对外提供服务,并不会同时对外提供服务,从nginx服务器作为主服务器的备用,如果同时对外提供服务就变成了集群的形式,

      如果采用主机宕机,将主机的ip直接切成备用机的ip,即修改备用机的ip为主机ip,此时在逻辑上确实能实现nginx服务器的切换,但是在使用场景中存在nginx服务器假死导致原nginx服务器重启发生两台nginx服务器ip地址冲突的情况

    • 解决办法:

      • 在局域网内虚拟出一个ip地址,术语叫做虚拟IP【VIP】,keepalived不去修改nginx服务器的真实ip地址,而是将虚拟IP来回漂移指向不同真实ip的nginx服务器,而用户直接访问入口的是虚拟IP【基于VRRP实现】,
      • 一旦主机宕机,虚拟IP漂移到备用机上,如果此时主机恢复,此时涉及到主机的竞选机制,可以通过配置机器优先级【优先级越高,竞选称为主机的概率就越高】、keepalived只负责对虚拟IP进行漂移,不会修改真实的IP,不会导致出现较大的问题
      • 注意,一台主机可以配置多个IP地址,不是一个机器只有一个ip地址,一个网卡【不是网关】上也可以配置多个地址,【一个机器可以有多个网卡,不同的网卡接入不同的网络,比如一个网卡接内网访问后端的应用服务器,另一个网卡接外网来接入电信、联通等主干网】,一个网卡上还可以配置好多个ip,只是最好不要配置太多
      • keepalived实现将用户的请求从主机迁移到备用机上,因为用户访问的ip一般不能主动更改【ip解析也不能老是更换,因为用户本机上的DNS缓存的域名解析还没来得及更新】,解决办法就是用户固定访问的ip地址在不同物理ip的机器上来回漂移

keepalived

keepalived是比较简单的软件,原理是检测keepalived的进程是否存在,不是依靠特别高明的机制,keepalived进程如果存活,就能相互之间发送数据包进行通信,如果无法通信了,就把虚拟IP按优先级漂移到可以通信的机器上

keepalived还可以通过编写脚本来控制keepalived监听的具体进程,当前keepalived只是监听自身的进程,并没有监听nginx的进程,如果nginx出问题但是keepalived没出问题,虚拟IP也是无法漂移的;脚本和keepalived也是分开的,和nginx也是分开的,脚本运行在目标机器上,三者唯一的联系就是运行在同一台机器上,脚本在设定时间间隔检测nginx当前是否报错,响应请求是否正常的200,如果连续监测到nginx的响应码不是200,就会将keepalived的进程kill掉,从而实现虚拟IP的漂移【此处不讲,可以自己研究一下】

keepalived的选举方式也很简单,就是根据配置文件优先级确定的,一旦新加入一台机器的keepalived的优先级更高,就自动将当前master改成BACKUP了,keepalived的应用不止nginx,是对主机和进程的检测,只是检测机器的keepalived是否或者,所以可以检测一切服务如多态mysql、redis、消息中间件、应用服务器等,都可以通过脚本检测进程运行状态,设定条件杀死keepalived进程实现虚拟IP的漂移

安装keepalived
编译安装

安装包下载地址:https://www.keepalived.org/download.html#

  1. 解压安装包,在当前目录下使用命令./configure查看安装环境是否完整,如果有如下报错信息需要使用命令yum install openssl-devel安装openssl-devel的依赖

    configure: error:
        !!! OpenSSL is not properly installed on your system. !!!
        !!! Can not include OpenSSL headers files. !!!
    
  2. 编译安装以前安装过,后面补充,包括上一条命令添加安装的位置

安装步骤
  1. 使用命令yum install -y keepalived安装keepalived

    需要在线监测的所有机器都要安装keepalived

    • 如果安装提示缺少需要:libmysqlclient.so.18()(64bit),依次使用命令wget https://dev.mysql.com/get/Downloads/MySQL-5.7/mysql-community-libs-compat-5.7.25-1.el7.x86_64.rpmrpm -ivh mysql-community-libs-compat-5.7.25-1.el7.x86_64.rpm安装对应的依赖【是否需要注意mysql的版本和本机匹配,我这里匹配了没有问题】,然后再次执行yum install -y keepalived安装keepalived即可

    • keepalived的配置文件在目录/etc/keepalived/keepalived.conf,使用命令vim /etc/keepalived/keepalived.conf修改keepalived的配置文件

      用来做nginx服务器在线监测的keepalived配置

      【默认的keepalived配置文件】

      ! Configuration File for keepalived
      
      global_defs {
      
      	#这一段是机器宕机以后发送email通知,作用不大
          notification_email {
               acassen@firewall.loc
               failover@firewall.loc
               sysadmin@firewall.loc
          }
          notification_email_from Alexandre.Cassen@firewall.loc
          smtp_server 192.168.200.1
          smtp_connect_timeout 30
          
          #global_defs中只有router_id有点用,其他的都可以删掉
          router_id LVS_DEVEL
          
          vrrp_skip_check_adv_addr
          vrrp_strict
          vrrp_garp_interval 0
          vrrp_gna_interval 0
      }
      
      vrrp_instance VI_1 {
          state MASTER
          interface eth0
          virtual_router_id 51
          priority 100
          advert_int 1
          authentication {
              auth_type PASS
              auth_pass 1111
          }
          virtual_ipaddress {
              192.168.200.16
              192.168.200.17
              192.168.200.18
          }
      }
      
      #仅仅用来检查nginx服务器的存活状态不用关心从这里往下的所有配置,即virtual_server都可以删掉
      virtual_server 192.168.200.100 443 {
          delay_loop 6
          lb_algo rr
          lb_kind NAT
          persistence_timeout 50
          protocol TCP
      
          real_server 192.168.201.100 443 {
              weight 1
              SSL_GET {
                  url {
                    path /
                    digest ff20ad2481f97b1754ef3e12ecd3a9cc
                  }
                  url {
                    path /mrtg/
                    digest 9b3a0c85a887a256d6939da88aabd8cd
                  }
                  connect_timeout 3
                  nb_get_retry 3
                  delay_before_retry 3
              }
          }
      }
      
      virtual_server 10.10.10.2 1358 {
          delay_loop 6
          lb_algo rr 
          lb_kind NAT
          persistence_timeout 50
          protocol TCP
      
          sorry_server 192.168.200.200 1358
      
          real_server 192.168.200.2 1358 {
              weight 1
              HTTP_GET {
                  url { 
                    path /testurl/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  url { 
                    path /testurl2/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  url { 
                    path /testurl3/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  connect_timeout 3
                  nb_get_retry 3
                  delay_before_retry 3
              }
          }
      
          real_server 192.168.200.3 1358 {
              weight 1
              HTTP_GET {
                  url { 
                    path /testurl/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334c
                  }
                  url { 
                    path /testurl2/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334c
                  }
                  connect_timeout 3
                  nb_get_retry 3
                  delay_before_retry 3
              }
          }
      }
      
      virtual_server 10.10.10.3 1358 {
          delay_loop 3
          lb_algo rr 
          lb_kind NAT
          persistence_timeout 50
          protocol TCP
      
          real_server 192.168.200.4 1358 {
              weight 1
              HTTP_GET {
                  url { 
                    path /testurl/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  url { 
                    path /testurl2/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  url { 
                    path /testurl3/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  connect_timeout 3
                  nb_get_retry 3
                  delay_before_retry 3
              }
          }
      
          real_server 192.168.200.5 1358 {
              weight 1
              HTTP_GET {
                  url { 
                    path /testurl/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  url { 
                    path /testurl2/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  url { 
                    path /testurl3/test.jsp
                    digest 640205b7b0fc66c1ea91c463fac6334d
                  }
                  connect_timeout 3
                  nb_get_retry 3
                  delay_before_retry 3
              }
          }
      }
      

【做nginx代理服务器的keepalived配置文件】

   ! Configuration File for keepalived
   
   global_defs {
       #global_defs中只有router_id有点用,其他的都可以删掉,这个是当前机器的自定义名字,随便起名的,当前机器的ip是131
       router_id nginx131
   }
   
   #vrrp是keepalived在内网中通讯的协议【vrrp虚拟路由冗余协议】,atlisheng是实例名称,也是自定义的
   vrrp_instance atlisheng {
   	#state是当前机器的状态,当前机器是master
       state MASTER
       #interface需要和本机的网卡的名称对应上,这里修改ip地址为ens32,需要和interface对应
       interface ens32
       #这个不用管
       virtual_router_id 51
       #主备竞选的优先级,谁的优先级越高,谁就是master;Keepalived 就是使用抢占式机制进行选举,一旦有优先级高的加入,立即成为 Master
       priority 100
       #检测间隔时间
       advert_int 1
       #内网中一组keepalive的凭证,因为内网中可能不止一组设备跑着keepalived,需要认证信息证明多个keepalived属于一组,同一组的authentication要保持一致,这样能决定一组keepalived中的一台机器宕机,虚拟IP是否只在本组内进行漂移,而不会漂移到其他组上
       authentication {
           auth_type PASS
           auth_pass 1111
       }
       #虚拟IP,虚拟IP可以填写好几个,意义不大,一般虚拟一个IP即可,这里设置成192.168.200.200,用户访问的是虚拟的ip,不再是真实的ip地址
       virtual_ipaddress {
           192.168.200.200
           #192.168.200.17
           #192.168.200.18
       }
   }

【备用机的keepalived配置】

注意同一组的实例名、virtual_router_id、authentication得是一样的

注意备机的priority 要设置的比主机小,state需要改成BACKUP

且备用机使用命令ip addr不会看到虚拟ip的信息

   ! Configuration File for keepalived
   
   global_defs {
      router_id nginx132
   }
   
   vrrp_instance atlisheng {
       state BACKUP
       interface ens32
       virtual_router_id 51
       priority 50
       advert_int 1
       authentication {
           auth_type PASS
           auth_pass 1111
       }
       virtual_ipaddress {
           192.168.200.200
       }
   }
  • 使用命令systemctl start keepalived运行keepalived
安装成功测试
  1. 启动keepalived后使用命令ip addr查询ip信息

    在ens32下在原来真实的ip下会多出来一个inet虚拟ip:192.168.200.200,master即主机上才有,备机上是没有的,即虚拟IP漂移到哪一台机器上才会出现这个虚拟IP

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 在windows命令窗口使用命令ping 192.168.200.200 -t在windows系统ping一下这个虚拟IP,观察该ip是否能ping通

    没有-t只会ping4次,像下图这种情况

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  3. 在linux系统下使用命令init 0关掉master机器模拟nginx服务器宕机,在windows命令窗口观察网络通信情况

    windows通信切换时丢包一次,然后再次ping通,在备用机132上使用命令ip addr能够观察到虚拟ip漂移到132机器上

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

网络协议

客户端与服务器网络通信是通过客户端最底层的socket,即基于网卡的特别基础的网络接口,有一门课叫socket编程,学习socket编程会涉及TCP的编程和UDB的编程,这两种网络协议就已经是较为上层的网络协议了,socket是比操作系统更底层的协议,和物理硬件完成交互,通过操作系统内核和网卡驱动去传输数据,TCP/IP协议是中间层的网络协议,用TCP/IP协议传输的网络数据,对方服务器时不知道数据包传输何时结束的,基本的TCP/IP协议只是用来传递数据的;通常在TCP协议上加一层形成应用层的高级协议,越高级的协议在使用的时候比较简单,但是底层比较复杂;HTTP请求头中包含请求参数,请求体中是真实想要传递的透明数据【比如图片】

HTTP的安全性缺陷

  1. 因为Http协议的底层协议没有一个是保证数据传输是安全不会被劫持的,

    • 防火墙不能防止数据传输被劫持,防火墙只能防止本机的端口莫名其妙的向外传递数据,防一些病毒和未知的程序,服务器的防火墙一般只会开启固定的对外通信端口,还可以根据用户的行为设定一些限制访问规则,比如频繁访问等功能,但是没有针对数据网络传递过程中的数据安全性
    • 家里的网络传递经过自家的网关会经过小区的网关,然后到接到会有一个网关,然后区里还有一个大的,市里还有一个大的路由网关,才会接入到主干网络,如果访问的服务器在美国,期间经过的网关路由可能会多达十几二三十个,每个节点都要将数据包传递到下一个节点,最终传递到服务器;在每个网关节点内都是可以看到传递的网络数据的,如果能猜出来传输数据是按照什么协议编码的可以直接按照协议去数据包解码,看到传递的明文内容,所以http协议在数据传输的过程中是不安全的,而且有很多的节点都能监听到【比如公共场合的WiFi不要去传递很私密的东西,可能数据包会被截取做处理】
  2. 网络数据传输加密的解决方案

    • 最简单的加密算法是凯撒加密算法

      • 凯撒加密算法就是将明文向后挪动一位【123明文变成231密文】
      • 用户数据经过凯撒加密算法从明文变成密文经过网络传输到服务器,服务器通过凯撒加密算法将数据解密以后处理将响应数据加密成密文再传递给客户端
      • 客户端和服务器采用同一种算法来对数据明文加密解密的过程称为对称加密,优点是简单,缺点是对称加密算法一定要内置在服务器端,如果一个项目是开源的,就能获取到加密算法列表,并在中间节点使用这些加密算法去破解这些密文;并且客户端也需要这些加密算法来对用户的数据来进行加密,
      • 即对称加密算法的保密性无法保证,又因为服务器和客户端都要内置加密算法导致对称加密算法不够灵活
    • 在对称加密的基础上

      • 如果客户端在首次向服务端传递数据时传递一个加密密码,即服务器需要使用该密码作为加密协议的因子才能解开密文,关键是用户的密码如何安全传递给服务器不被中间节点拦截住
      • 所以对称加密算法实际上是无法解决安全问题的,即便使用密码进行解密,但是密码的传递还是不安全的

HTTPS原理

  1. 非对称加密算法基本原理

    • 服务器生成一个加密因子,该加密因子不在互联网上进行传播;非对称加密算法加密和解密是不同的算法,客户端存在一个公钥,服务器存在一个私钥,用户首次请求去服务器主机的443端口而非80端口获取公钥,通过公钥和加密算法在数据传输前对明文进行加密生成密文,服务器接收到密文后用私钥和解密算法解开密文得到明文;响应请求以后用私钥和加密算法对响应进行加密【公钥私钥都是加密算法的因子】,密文传递给客户端以后可以用公钥和算法进行解密【请求是公钥加密,私钥解密;响应是私钥加密,公钥解密】

    • 只是以上两点还无法满足安全性,此外还有第三点特征,公钥加密,公钥无法解开密文【因为公钥还是会经过各个节点,存在窃取的风险】

      对称加密算法要安全一定要保证私钥要安全,这套非对称加密算法就是https协议的底层算法

  2. 存在问题

    • 响应内容也存在被解密的风险
    • 除此以外,这种非对称加密算法也不是绝对安全的,有些节点会存在模拟服务器的行为,拦截用户的请求,替用户去请求目标服务器的443端口获取真实服务器想要颁发给用户的公钥,然后自己给用户颁发自己的公钥,此后拦截用户的请求解密用户密文篡改用户请求信息并替代用户用服务器颁发的公钥进行加密向真实服务器发起请求
  3. 进一步优化

    • 这种模拟目标服务器拦截用户请求篡改用户请求信息代替用户请求的行为类似于将银行颁发的银行卡存在自己手上,而向用户颁发自己发行的银行卡

    • 此时需要一个第三方,该第三方不在网络中进行信息传递,同时当目标服务器颁发凭证时知晓和确认凭证且在用户使用凭证时对凭证进行确认,这个机构就叫CA机构,对服务器下发的公钥进行认证,CA机构也是一套系统,提供认证服务

    • 服务器下发公钥之前会向CA机构提交公钥,CA机构会对公钥的提交者的身份进行验证,验证方式一般是给提交公钥的服务器提供一个即时生成的随机验证码文件,要求服务器将该文件上传放在目标域名服务器的指定目录下,CA机构通过指定目录访问该验证文件从而判定是可信任受客户管理的服务器地址【正常情况下拦截者是无法向提交公钥的服务器上传文件的】

    • 当CA机构对提交公钥的服务器验证后,CA机构会使用CA机构的私钥和加密算法对服务器提交的公钥再进行一次非对称加密并生成CA证书【浏览器的地址栏最前面的菜单中有链接是安全的,点开就能查到对应的安全证书,网站单位和颁发机构、时效等等】

    • 再有用户请求443端口想要公钥直接下发CA证书,不再下发公钥了

    • 操作系统中内置了CA机构的公钥,使用CA机构的公钥能解开CA机构私钥加密的CA证书,CA证书也只能通过操作系统内置的CA公钥去解密,中间节点能拦截到CA证书,也能通过CA机构的公钥解密证书,但是无法篡改并生成新的CA证书,因为CA机构的私钥不可能丢,只要不是用这个私钥加密的CA证书,使用CA机构公钥就无法解密,无法被正常解密的CA证书就不认可为可信任的CA证书【非私钥加密,即使是公钥加密公钥也无法解开】

      这也是为什么要用正版操作系统,由其是证书方面,避免在系统上被恶意软件安装了root根证书,导致外来root根证书成了可信任的状态,一般杀毒软件也会防范这种根证书的植入,任何操作系统都有内置的CA公钥,包括移动端

      同时还要使用正版的浏览器,一系列的认证流程都是浏览器完成的,如果浏览器有问题,即便证书再安全,安全性也无法保证

      所有内嵌在操作系统的证书可以通过win+r呼出的窗口输入certmgr.msc显示的界面进行查看,第三方根证书颁发机构都是非常知名的机构,这些证书就是CA机构的公钥

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • https被称为21世纪互联网最伟大的发明,在以前网络数据传输的安全性非常不可信任,数据非常容易被篡改,https发明以后数据安全性就好了很多,像银行这种对数据加密性要求很高的机构除了引入数据传输安全加密,还会引入其他的加密方案,包括加密键盘控件,避免输入账户密码的时候被恶意软件监听键盘输入;当然这种情况下还是可能被录屏导致不安全

  4. 证书自签名

    证书自签名意思就是CA机构的角色是服务器自己扮演的,这种应用场景非常少,因为这种情况下,用户访问时浏览器不会有受信任证书的提示,浏览器也不会认为这是一个安全可靠的连接,因为CA机构不是系统内置的,而是用户自己签的CA证书,使用场景非常少,只有内网环境下才会用到自签名,CS的连接也可能用到自签名,但是用处也不大

    • 自签名可以用OpenSSL实现

      OpenSSL是一款非常出名的开源软件,相当于在开源界扛把子的地位,罗永浩在2015年左右拿出200w直接赞助给OpenSSL,对互联网安全贡献非常大,还赞助过OpenResty,OpenResty是提供给nginx做高并发的,OpenSSL是提供nginx上https的,OpenSSL基本上内嵌在操作系统上,没有可以使用yum源进行安装,基于命令行的工具,可以生成CA证书

      XCA是基于OpenSSL的一款图形化工具,下载地址:https://www.hohnstaedt.de/xca/index.php/download

  5. 在线申请CA证书

    中小企业可能会负责这件事,在线申请一个证书,绑定到一台服务器上,给服务器安装上nginx,对nginx进行配置,让nginx跑一些开源的程序

    • 购买域名

      域名的提供商非常多,最著名的就是万网,net.cn;注册域名到大的平台商,小商户可能带着域名跑路

      注意顶级域名中含有中文的域名最好不要买,健壮性不行,稍微老一点的软件都不支持中文的编码,虽然目前流行的浏览器都支持中文

      直接阿里云购买

    • 购买主机

      主机云服务器就是阿里云ECS,大陆的云服务器需要备案,香港和国外的云服务器不需要备案,CentOS7.4用的比较多,系统也比较靠谱

      轻量级服务器有专门的产品界面,不在ESC中

      使用xshell连接公网IP能连接上系统,需要输入设置的root账户和密码

    • 在云服务器上安装nginx,该nginx版本除了基础的tengine和OpenResty,还集成了运行环境,包括PHP、数据库等等

      • 使用命令lnmp,l表示nginx、n表示nginx、m表示mysql、p表示php【安装php是为了演示bbs】

      • oneinstack.com是一个集成环境,可以选择安装的产品并生成安装命令

        【云服务器安装nginx配置】

        【生成的安装命令】

        阿里云的云服务器默认是安装了wget的

        我这儿安装好了没有位置提示,这里记老师的,nginx安装目录/usr/local/nginx、数据库的安装目录/usr/local/mysql、数据库的data dir:/data/mysql、PHP的安装目录/usr/local/php、Opcache控制面板URL:http://172.18.45.195/ocp.php

        我这儿安装非常快,一分钟可能都没到,老师上课装了27分钟

        wget -c http://mirrors.oneinstack.com/oneinstack-full.tar.gz && tar xzf oneinstack-full.tar.gz && ./oneinstack/install.sh --nginx_option 1 --php_option 9 --phpcache_option 1 --db_option 2 --dbinstallmethod 1 --dbrootpwd 个人数据库密码 --reboot

      • 配置云服务器的安全组【防火墙】

        • 80、443、22端口,mysql数据库最好不要开放远程通讯端口,线上mysql管理最好安装一个PHPMyAdmin,redis开放6379端口一定要设置密码,很容易被黑

        • 开放对应端口后浏览器访问对应公网ip,对应的大陆服务器没有备案会显示内容禁止访问,没有设置CA认证,地址前面也会提示不安全,使用oneinstack安装的nginx访问80端口会显示oneinstack的控制面板,控制面板中的Virtual host中的./vhost.sh是其提供的修改nginx.conf的脚本文件,oneinstack修改了nginx的配置,在最小配置的基础上还增加了其他配置,暂时先不管;此外还将欢迎页的根目录设置成了oneinstack的控制台,对应的root目录被改成了/data/wwwroot/default,暂时还是用html,将server 80端口的服务的root改成html,其他的配置先不管,里面还配置了对php脚本的访问

          【oneinstack修改的nginx配置文件】

          这里和老师的情况不同,我这里默认的nginx配置被注释掉了,老师的是生效的,等服务器备案下来了再说

          user www www;
          worker_processes auto;
          
          error_log /data/wwwlogs/error_nginx.log crit;
          pid /var/run/nginx.pid;
          worker_rlimit_nofile 51200;
          
          events {
            use epoll;
            worker_connections 51200;
            multi_accept on;
          }
          
          http {
            include mime.types;
            default_type application/octet-stream;
            server_names_hash_bucket_size 128;
            client_header_buffer_size 32k;
            large_client_header_buffers 4 32k;
            client_max_body_size 1024m;
            client_body_buffer_size 10m;
            sendfile on;
            tcp_nopush on;
            keepalive_timeout 120;
            server_tokens off;
            tcp_nodelay on;
          
            fastcgi_connect_timeout 300;
            fastcgi_send_timeout 300;
            fastcgi_read_timeout 300;
            fastcgi_buffer_size 64k;
            fastcgi_buffers 4 64k;
            fastcgi_busy_buffers_size 128k;
            fastcgi_temp_file_write_size 128k;
            fastcgi_intercept_errors on;
          
            #Gzip Compression
            gzip on;
            gzip_buffers 16 8k;
            gzip_comp_level 6;
            gzip_http_version 1.1;
            gzip_min_length 256;
            gzip_proxied any;
            gzip_vary on;
            gzip_types
              text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
              text/javascript application/javascript application/x-javascript
              text/x-json application/json application/x-web-app-manifest+json
              text/css text/plain text/x-component
              font/opentype application/x-font-ttf application/vnd.ms-fontobject
              image/x-icon;
            gzip_disable "MSIE [1-6]\.(?!.*SV1)";
          
            ##Brotli Compression
            #brotli on;
            #brotli_comp_level 6;
            #brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
          
            ##If you have a lot of static files to serve through Nginx then caching of the files' metadata (not the actual files' contents) can save some latency.
            #open_file_cache max=1000 inactive=20s;
            #open_file_cache_valid 30s;
            #open_file_cache_min_uses 2;
            #open_file_cache_errors on;
          
            log_format json escape=json '{"@timestamp":"$time_iso8601",'
                                '"server_addr":"$server_addr",'
                                '"remote_addr":"$remote_addr",'
                                '"scheme":"$scheme",'
                                '"request_method":"$request_method",'
                                '"request_uri": "$request_uri",'
                                '"request_length": "$request_length",'
                                '"uri": "$uri", '
                                '"request_time":$request_time,'
                                '"body_bytes_sent":$body_bytes_sent,'
                                '"bytes_sent":$bytes_sent,'
                                '"status":"$status",'
                                '"upstream_time":"$upstream_response_time",'
                                '"upstream_host":"$upstream_addr",'
                                '"upstream_status":"$upstream_status",'
                                '"host":"$host",'
                                '"http_referer":"$http_referer",'
                                '"http_user_agent":"$http_user_agent"'
                                '}';
          
          ######################## default ############################
          #  server {
          #    listen 80;
          #    server_name _;
          #    access_log /data/wwwlogs/access_nginx.log combined;
          #    root html;
          #    index index.html index.htm index.php;
          #    #error_page 404 /404.html;
          #    #error_page 502 /502.html;
          #    location /nginx_status {
          #      stub_status on;
          #      access_log off;
          #      allow 127.0.0.1;
          #      deny all;
          #    }
          #    location ~ [^/]\.php(/|$) {
          #      #fastcgi_pass remote_php_ip:9000;
          #      fastcgi_pass unix:/dev/shm/php-cgi.sock;
          #      fastcgi_index index.php;
          #      include fastcgi.conf;
          #    }
          #    location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
          #      expires 30d;
          #      access_log off;
          #    }
          #    location ~ .*\.(js|css)?$ {
          #      expires 7d;
          #      access_log off;
          #    }
          #    location ~ ^/(\.user.ini|\.ht|\.git|\.svn|\.project|LICENSE|README.md) {
          #      deny all;
          #    }
          #    location /.well-known {
          #      allow all;
          #    }
          #  }
          ########################## vhost #############################
            include vhost/*.conf;
          }
          
          
        • oneinstack默认将nginx配置成了服务,可以直接使用systemctl的相关命令【服务器备案中,后面补效果】

          目前图用的课堂的

          外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 在线生成证书

      在云服务商搜索SSL证书【应用安全】

      整DV单域名证书是免费的,但是限制主体域名【只能设置单个二级域名,一年可以设置20个】,类似通配符等自定义域名需要付费购买,还挺贵,一年几百上千,企业小或者单人没必要买付费的

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【申请填写信息】

      域名验证方式选择自动DNS验证,后续验证的时候点击一下验证就会自动验证,如果是手动需要去配置指定的域名解析供认证机构去进行验证,一般只有域名和服务器在同一个平台才可以自动DNS验证,以文件的形式验证还需要点击上传文件

      密钥算法RSA就是非对称的加密算法,CSR是证书文件【直接系统生成即可】

      成功提交以后会显示申请审核中,审核成功后会显示已签发,签发速度极快,几秒钟就能签发

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 证书下载

      将证书下载下来传到服务器上,nginx的下载下来是一个zip压缩包

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【证书文件】

      一共有两个文件,以.key结尾的文件是私钥,以.pem结尾的文件是证书,两个文件都要传到服务器上nginx的conf目录下,默认会从conf目录下找这两个文件

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 证书安装

      直接复制粘贴到nginx.conf的server80服务器的同级,端口是443,ssl_certificate是证书的位置,使用相对路径【注意文件要在conf目录下】

      server{
          #所有的请求都会去443端口获取公钥
          listen 433 ssl;
          #这个名字localhost是因为没有其他域名写的
          server_name www.concurrency.cn;
      
          ssl_certificate CA认证文件的pem文件带后缀名称;
          ssl_certificate_key CA认证文件的key文件带后缀名称;
      	
      }
      

      【证书安装】

      重新加载nginx没报错就表示安装成功,此时输入https://域名就会显示安全,http开头还是会显示不安全;这种配置如果二级域名不是www就会显示不安全

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  6. 安装开源社区BBS

    BBS就是Discuz,非常出名,国内的开源社区鼻祖,现在已经被腾讯收购,网站https://discuz.dismall.com/【原网址已经被改了】

    很多的大型互联网网站bbs系统用的都是discuz,可以基于discuz进行二次改造,和现有系统进行集成,直接在官网下载安装:Discuz! X3.5 正式版【2023-10-01】 - Discuz! X 程序发布 - Powered by Discuz! (dismall.com)

    discuz是PHP做的,但是可以和系统进行整合,整合之后系统相当于进行了一次升级,成为了异构系统【使用了不同的语言,不同的社群解决方案组成了一个大的系统】,还有其他很多有名的大型开源项目

    【上传discuz的zip文件到nginx的html目录】

    上传Discuz_X3.4_SC_UTF8_20220131到nginx的html目录,类似这样的开源项目还有WordPress【国内外都非常出名】

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 使用命令unzip Discuz_X3.4_SC_UTF8_20220131.zip解压该安装包

      【解压后的文件】

      bbs的安装不是在命令行安装的,是在线安装的,通过访问本机服务器地址来在本机的网站上进行安装

      源码目录是upload目录,使用命令mv upload/ bbs将upload目录改名为bbs

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 注意此时通过https访问安装目录https://concurrency.cn/bbs/install/会失败,因为https默认会先访问443端口,但是443端口在nginx.conf文件中没有对该站点进行php环境的配置,所以直接访问该网址会失败;但是http的80站点,不访问443端口的URL:http://concurrency.cn/bbs/install/是可以使用的

      此时配置所有的请求都走https协议,将http协议下的PHP的所有配置移到443端口下

      注意,似乎不是https会首次访问443端口,而是只会访问443端口,包括后续的请求;http才会始终访问80端口

      【nginx.conf中80端口下php的相关配置】

      user www www;
      worker_processes auto;
      
      error_log /data/wwwlogs/error_nginx.log crit;
      pid /var/run/nginx.pid;
      worker_rlimit_nofile 51200;
      
      events {
        use epoll;
        worker_connections 51200;
        multi_accept on;
      }
      
      http {
        include mime.types;
        default_type application/octet-stream;
        server_names_hash_bucket_size 128;
        client_header_buffer_size 32k;
        large_client_header_buffers 4 32k;
        client_max_body_size 1024m;
        client_body_buffer_size 10m;
        sendfile on;
        tcp_nopush on;
        keepalive_timeout 120;
        server_tokens off;
        tcp_nodelay on;
      
        fastcgi_connect_timeout 300;
        fastcgi_send_timeout 300;
        fastcgi_read_timeout 300;
        fastcgi_buffer_size 64k;
        fastcgi_buffers 4 64k;
        fastcgi_busy_buffers_size 128k;
        fastcgi_temp_file_write_size 128k;
        fastcgi_intercept_errors on;
      
        #Gzip Compression
        gzip on;
        gzip_buffers 16 8k;
        gzip_comp_level 6;
        gzip_http_version 1.1;
        gzip_min_length 256;
        gzip_proxied any;
        gzip_vary on;
        gzip_types
          text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
          text/javascript application/javascript application/x-javascript
          text/x-json application/json application/x-web-app-manifest+json
          text/css text/plain text/x-component
          font/opentype application/x-font-ttf application/vnd.ms-fontobject
          image/x-icon;
        gzip_disable "MSIE [1-6]\.(?!.*SV1)";
      
        ##Brotli Compression
        #brotli on;
        #brotli_comp_level 6;
        #brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
      
        ##If you have a lot of static files to serve through Nginx then caching of the files' metadata (not the actual files' contents) can save some latency.
        #open_file_cache max=1000 inactive=20s;
        #open_file_cache_valid 30s;
        #open_file_cache_min_uses 2;
        #open_file_cache_errors on;
      
        log_format json escape=json '{"@timestamp":"$time_iso8601",'
                            '"server_addr":"$server_addr",'
                            '"remote_addr":"$remote_addr",'
                            '"scheme":"$scheme",'
                            '"request_method":"$request_method",'
                            '"request_uri": "$request_uri",'
                            '"request_length": "$request_length",'
                            '"uri": "$uri", '
                            '"request_time":$request_time,'
                            '"body_bytes_sent":$body_bytes_sent,'
                            '"bytes_sent":$bytes_sent,'
                            '"status":"$status",'
                            '"upstream_time":"$upstream_response_time",'
                            '"upstream_host":"$upstream_addr",'
                            '"upstream_status":"$upstream_status",'
                            '"host":"$host",'
                            '"http_referer":"$http_referer",'
                            '"http_user_agent":"$http_user_agent"'
                            '}';
      
      ######################## default ############################
        server{
      	#所有的请求都会去443端口获取公钥
      	listen 433 ssl;
      	#这个名字localhost是因为没有其他域名写的
      	server_name localhost;
      	
      	ssl_certificate CA认证文件的pem文件带后缀名称;
      	ssl_certificate_key CA认证文件的key文件带后缀名称;
      	
      	}
        
        server {
          listen 80;
          server_name _;
          access_log /data/wwwlogs/access_nginx.log combined;
          root html;
          
          #从此处开始是http协议中包含PHP的配置
          index index.html index.htm index.php;
          #error_page 404 /404.html;
          #error_page 502 /502.html;
          location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            deny all;
          }
          location ~ [^/]\.php(/|$) {
            #fastcgi_pass remote_php_ip:9000;
            fastcgi_pass unix:/dev/shm/php-cgi.sock;
            fastcgi_index index.php;
            include fastcgi.conf;
          }
          location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
            expires 30d;
            access_log off;
          }
          location ~ .*\.(js|css)?$ {
            expires 7d;
            access_log off;
          }
          location ~ ^/(\.user.ini|\.ht|\.git|\.svn|\.project|LICENSE|README.md) {
            deny all;
          }
          location /.well-known {
            allow all;
          }
          #PHP的配置到此结束
          
        }
      ########################## vhost #############################
        include vhost/*.conf;
      }
      

      【更改后的配置文件】

      user www www;
      worker_processes auto;
      
      error_log /data/wwwlogs/error_nginx.log crit;
      pid /var/run/nginx.pid;
      worker_rlimit_nofile 51200;
      
      events {
        use epoll;
        worker_connections 51200;
        multi_accept on;
      }
      
      http {
        include mime.types;
        default_type application/octet-stream;
        server_names_hash_bucket_size 128;
        client_header_buffer_size 32k;
        large_client_header_buffers 4 32k;
        client_max_body_size 1024m;
        client_body_buffer_size 10m;
        sendfile on;
        tcp_nopush on;
        keepalive_timeout 120;
        server_tokens off;
        tcp_nodelay on;
      
        fastcgi_connect_timeout 300;
        fastcgi_send_timeout 300;
        fastcgi_read_timeout 300;
        fastcgi_buffer_size 64k;
        fastcgi_buffers 4 64k;
        fastcgi_busy_buffers_size 128k;
        fastcgi_temp_file_write_size 128k;
        fastcgi_intercept_errors on;
      
        #Gzip Compression
        gzip on;
        gzip_buffers 16 8k;
        gzip_comp_level 6;
        gzip_http_version 1.1;
        gzip_min_length 256;
        gzip_proxied any;
        gzip_vary on;
        gzip_types
          text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
          text/javascript application/javascript application/x-javascript
          text/x-json application/json application/x-web-app-manifest+json
          text/css text/plain text/x-component
          font/opentype application/x-font-ttf application/vnd.ms-fontobject
          image/x-icon;
        gzip_disable "MSIE [1-6]\.(?!.*SV1)";
      
        ##Brotli Compression
        #brotli on;
        #brotli_comp_level 6;
        #brotli_types text/plain text/css application/json application/x-javascript text/xml application/xml application/xml+rss text/javascript application/javascript image/svg+xml;
      
        ##If you have a lot of static files to serve through Nginx then caching of the files' metadata (not the actual files' contents) can save some latency.
        #open_file_cache max=1000 inactive=20s;
        #open_file_cache_valid 30s;
        #open_file_cache_min_uses 2;
        #open_file_cache_errors on;
      
        log_format json escape=json '{"@timestamp":"$time_iso8601",'
                            '"server_addr":"$server_addr",'
                            '"remote_addr":"$remote_addr",'
                            '"scheme":"$scheme",'
                            '"request_method":"$request_method",'
                            '"request_uri": "$request_uri",'
                            '"request_length": "$request_length",'
                            '"uri": "$uri", '
                            '"request_time":$request_time,'
                            '"body_bytes_sent":$body_bytes_sent,'
                            '"bytes_sent":$bytes_sent,'
                            '"status":"$status",'
                            '"upstream_time":"$upstream_response_time",'
                            '"upstream_host":"$upstream_addr",'
                            '"upstream_status":"$upstream_status",'
                            '"host":"$host",'
                            '"http_referer":"$http_referer",'
                            '"http_user_agent":"$http_user_agent"'
                            '}';
      
      ######################## default ############################
        server{
      	#所有的请求都会去443端口获取公钥
      	listen 433 ssl;
      	#这个名字localhost是因为没有其他域名写的
      	server_name localhost;
      	
      	ssl_certificate CA认证文件的pem文件带后缀名称;
      	ssl_certificate_key CA认证文件的key文件带后缀名称;
      	#从此处开始是http协议中包含PHP的配置,没有index.php也会去找index.php,优先级从前到后,前面的文件找不到就去找后面的
          index index.html index.htm index.php;
          #error_page 404 /404.html;
          #error_page 502 /502.html;
          location /nginx_status {
            stub_status on;
            access_log off;
            allow 127.0.0.1;
            deny all;
          }
          location ~ [^/]\.php(/|$) {
            #fastcgi_pass remote_php_ip:9000;
            fastcgi_pass unix:/dev/shm/php-cgi.sock;
            fastcgi_index index.php;
            include fastcgi.conf;
          }
          location ~ .*\.(gif|jpg|jpeg|png|bmp|swf|flv|mp4|ico)$ {
            expires 30d;
            access_log off;
          }
          location ~ .*\.(js|css)?$ {
            expires 7d;
            access_log off;
          }
          location ~ ^/(\.user.ini|\.ht|\.git|\.svn|\.project|LICENSE|README.md) {
            deny all;
          }
          location /.well-known {
            allow all;
          }
          #PHP的配置到此结束
      	
      	}
        
        server {
          listen 80;
          server_name _;
          access_log /data/wwwlogs/access_nginx.log combined;
          root html;
          
        }
      ########################## vhost #############################
        include vhost/*.conf;
      }
      
    • 将http协议的请求转发到443端口

      http协议跳转https,一般服务器上都是这样配置的,在80站点下添加如下配置

      此时任何http协议请求都会自动跳转到https上

      server {
          listen 80;
          #站点域名
          server_name www.concurrency.cn concurrency.cn;
          access_log /data/wwwlogs/access_nginx.log combined;
      
          #该配置实现http协议跳转https协议,server_name是当前的域名,即用户访问的是哪个域名就跳到哪儿,也就是当前80站点配置的域名,重定向的协议是https协议,request_uri是用户实际请求的uri
          return 301 https://$server_name$request_uri;
      
          root html;
      }
      
    • 安装

      在html目录下使用命令chmod -R 777 bbs/将目录权限设置为可写状态,学习环境为了方便可以直接改成777,生产环境不要这么做,要根据用户需求设定具体的权限【生产环境的权限配置比较复杂,需要根据实际情况来】,更改以后刷新页面可以观察到当前状态全部变成绿色

      注意不需要创建目录,只需要更改bbs的权限即可

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【数据库配置】

      数据库会自动帮助用户创建对应自定义名字的数据库,管理员账户和密码需要自己设置,该过程比较慢,耐心等待,成功了会显示论坛已完成安装,此时自己网站上的bbs论坛系统就可以正常使用了,可以测试一下发帖

      后台管理在管理中心,需要输入管理员账户和密码进行登录,功能很多

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

系统扩容

原有的用nginx构建的站点在运行过程中可能会产生一些瓶颈【内容越来越多,访问频次越来越高】,拉低用户的体验,此时需要用扩容来实现让系统支持更高的并发量和吞吐量

扩容方式有:单机垂直扩容、水平扩展、细腻度拆分、数据异构化、服务异步化;这里只重点讲了单机垂直扩容

扩容的原则:无状态原则和弹性原则

实际上从这里开始到多级缓存都在讲扩大系统对并发请求容量的各种手段和扩容衍生出来的一系列问题的解决手段

扩容方式

  1. 单机垂直扩容

    简单粗暴直接增加硬件资源,类似一个井使用的人多了,水不够用把井打的更深;这种方式存在瓶颈,当一个井不够用了,再怎么深挖都不能解决问题【比如人太多了,只有一口井不停地打水还是不能让每个人都分到水】,这种一次性增加的成本或者资产从老板角度来看可能比招几个资深程序员来优化系统要更省钱

    • 对云服务资源的增加,比如项目部署在云服务器上,可以通过增加云服务器的配置【换更高性能的磁盘、增加CPU的核数、增加内存】

    • 自建机房或者云服务的可以考虑提升硬件质量,比如选用更好的产品来增加系统的并发度【知名品牌的高性能服务器厂商有IBM、浪潮、DELL、HP】

    • 将CPU/主板更新到主流的产品

    • 网卡:很多时候系统的瓶颈不是硬件瓶颈,是网络上的瓶颈,网络瓶颈可以通过购买更高的带宽解决【现在的网卡不局限于百兆、千兆;还有万兆即10G/40G的网卡,带宽特别高的网卡非常稀缺,一般还需要特定的渠道才能买的到】,还可以在主板上增加网口和网卡的个数【一个主板不止能插一个网卡,一个操作系统上也可以有不止一个IP地址】,可以通过串行和并行的方式,串行是流量通过多个网口接入系统后汇集到一块;并行的方式是指配置多个IP地址【貌似也是在主板上插入两块网卡】,服务器的一个网卡可以接入电信的网络,另一个网卡接入联通的网络,两个网卡的IP地址不同;流量的串行和并行都是非常成熟的方案,这也是网络工程师一般去做的事;网卡除了提升带宽来提升网络性能,现在的网卡还会内置芯片,提升网卡对数据包,网络请求在经过网卡时就进行初步处理,减少操作系统的工作

    • 磁盘:磁盘控制系统内部数据的传输熟读,常见的磁盘种类:SAS(SCSI)HDD(机械)、HHD(混合)、SATA SSD、PCI-e SSD、MVMe SSD【MVMe是协议,MVMe SSD的速度和效率是最高的,相比如HDD的各种磁盘在读取和写入的速度上都是指数级别的提升】,在服务器上对磁盘的要求更多的是随机读取的效率,一般nginx部署在服务器上主要提供静态网页的资源,网页在磁盘上的读取一般都是随机的读取【因为无法预测用户究竟想要哪个网页】,随机读取方面还是SSD的速度比HDD的速度要高得多的多,但是HDD磁盘也是非常常见的,因为这种磁盘的可靠性会比较高7200转,服务器用的HDD一般是一万转或者一万五千转,转速越快磁头到磁盘的寻址速度就越快,转速快可能会导致故障率变高,HDD磁盘的故障一般都发生寻址过程中,磁盘栈的越快,磨损的几率就越高,一旦磁盘上盘片磨损,就会产生坏道,就可能导致磁盘中数据的丢失,HDD磁盘在服务器上还是比较主流,就是可靠性比较高,但是磁盘速度越快,出现坏道的概率就越大,而且一般转速高容量大的磁盘会比较贵

      SSD速度快,故障率高,过热、读取写入频次过高,使用时间太长都会导致故障,故障可能会导致全盘数据丢失,一般将SSD作为系统主盘【系统盘】,并在SSD上存储热点数据,将数据库mysql等安装在SSD上;由于SSD容易发生故障的原因,一般都采用多副本机制,将SSD上的数据采用多副本的方式进行冗余存储【也有备份的比较成熟的方案raid5,多副本机制的实现有同时写入,单一读取;也有同时写入,同时读取的方式】,一般的公司都会在内网中对线上服务器中的数据再做一次备份

      HDD比较适合冷数据的存储,也适合大文件的存储,因为单位容量成本低【用来存储电影数据等】

  2. 水平扩容

    【集群化系统架构】

    keepalived负责nginx服务器的高可用,后端服务器提供数据的统一称为上游服务器,流量接入层的服务器统一称为中游服务器,集群中的每台上游服务器中运行的代码都是一样的,分布式系统和单一的集群系统的区别是分布式系统中系统功能被拆分到多个服务器中,这些特定的服务器负责特定的服务,分布式系统中不一定有集群;单一的集群系统是每个服务器都是一个完整的系统,集群内的每台服务器都是一样的,集群是分布式系统的子集

    nginx可以通过proxy_pass和UpStream关键字将用户请求中转到上游的集群服务器

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    在原来的单机系统垂直扩容基础上提供集群化增加更多机器的方式来整体对外提供服务,优点是成本低

    集群化比单机扩容还要廉价,投入包含买服务器,研发和运维成本,单机提升达到一定程度的时候再增加单机硬件水平成本会格外的高【比如非常好的内存条可能主板不兼容,还需要更换主板】,更常见的是通过软件的手段提升系统的性能,在系统集群化配置的前提下:可以使用以下方式来分发请求

  3. 细粒度拆分

    分布式,将已经集群化的系统通过数据分区【将一个nginx上存储的过多的文件,将文件拆分成几块分布在不同的nginx服务器上】、上游服务SOA化【SOA:面向服务做技术架构,主要针对后端的应用服务器,将原本庞大的单体系统拆分成更加细腻度、特定模块提供特定服务的分布式系统架构,这些模块运行在单独的服务器上,SpringCloud和Dubbo都属于SOA的一部分,nginx对这些细腻度的每个模块都单独进行代理】、入口细分【入口有浏览器、移动端原生APP和物联网应用、H5内嵌式应用,配置根据用户的请求入口来访问不同的nginx服务器达到分流的效果】再拆分成分布式的系统

  4. 数据异构化

    最初是把nginx作为静态资源的主要载体,数据库和tomcat服务器作为对计算数据文件的存储载体;数据异构化是将数据进行拆分分布在不同的对象上【客户端、CDN、nginx服务器、DB、web服务器】;数据异构化中的一种方式就是做多级缓存,将缓存数据拆分分别做客户端缓存、CDN缓存、异地多活【把数据和缓存放在多个物理的空间如不同地区的机房去存储】、Nginx缓存后端服务器数据【nginx既可以做动静分离,还可以做动态数据的缓存】

  5. 服务的异步化

    同步:用户从发起请求,请求数据通过网络传递、服务器处理响应到数据渲染,用户一直在等待的过程称为同步

    异步:用户请求到服务器后针对需要长时间处理的请求,如上传下载文件可以先简单的给用户一个响应,让用户可以做其他事情,等真正处理完成后再把结果响应并通知到用户

    服务异步化的实现方式:拆分请求【用户下载请求拆分成第一次查询文件大小并选择普通下载还是多线程下载;又比如比较复杂的用户信息填写使用下一步按钮引导的方式让用户完成填写,而不是一股脑的把表单丢给用户】、消息中间件

会话管理

依靠nginx自身的配置对集群化的会话管理在扩容方式的水平扩容中已经进行了介绍,以下介绍利用第三方

水平扩容会话管理
  1. nginx自身对服务器集群会话的管理的配置

    非分布式的集群化系统上游服务以Tomcat服务器为例,会保存用户登录的信息即session在服务器中,集群中的其他服务器没有这个session,也就没有相应的会话信息,维持用户会话状态的方案有以下几种

    • Redis+SpringSession:这是使用上游服务器自身提供的功能来维持会话,SpringSession是一个框架,会把用户的会话状态存储在Redis中,这是通过java来保持用户的会话,但是这种方式由于java的性能相比于nginx偏低,此外还需要额外增加服务器,此外如果上游服务器比较多,此时redis一台服务器很可能不堪重负,整体系统设计的复杂度也会非常高;如果使用nginx解决这个问题,就可以省去很多为了维持会话导致的额外开销

    • ip_hash:主要是为了在非分布式的集群系统中维持用户会话【其实之前已经讲过】,原生开源版本自带,根据客户端IP地址通过哈希算法得到定值,用哈希值对上游服务器取模将请求定向转发到一台服务器上【一致性哈希】,这里面存在一个问题,类似于网吧、学校、稍微大一些的公司或宽带用户共用一个IP地址,可能导致大量的请求取到一个哈希值,全被转发到一个服务器上,造成流量严重倾斜,这种方式特别不适合运行在局域网中的系统,如企业中的ERP、校园中的教务系统、考试系统【考生集中在几个学校考试,一共就只有几个IP】,但是分发效率很高,能够避免像用Redis+SpringSession的额外开销

      • 除此以外,用户使用过程中如果发生服务器宕机的情况,用户的会话状态也会直接消失

      这种ip_hash方式的应用场景一般是中小型项目初期的快速扩容,不想改代码,只需要添加机器就能立即扩大系统的并发度,用nginx来做流量分发原代码根本不需要改;如果使用SpringSession需要改代码,打包测试一系列操作

      缺点:一旦服务器出现宕机,服务器中的会话状态就会丢失,尤其在与钱有关的系统中,所有分步操作都没有持久化全部保存在内存或者session中的情况下,请求在机器宕机后被转发到其他机器中,操作还能继续,但是此前的操作全部丢失,用户的体验极差

      • 模拟使用ip_hash负载均衡策略做会话管理的配置

        使用3台nginx模拟集群情况下的会话管理情况,一台131做代理服务器,另外两台132和133做上游服务器

        【代理服务器的nginx.conf】

        worker_processes  1;
        
        events {
            worker_connections  1024;
        }
        
        
        http {
            include       mime.types;
            default_type  application/octet-stream;
            sendfile        on;
            keepalive_timeout  65;
            upstream ip_hash_test{
            
            	#ip_hash;单独成行,表示使用ip_hash的方式进行负载均衡
                ip_hash;
                server 192.168.200.132;
                server 192.168.200.133;
                
            }
        
            server {
                listen       80;
                server_name  localhost;
        
                location / {
                    proxy_pass http://ip_hash_test;
                    #root   html;
                    #index  index.html index.htm;
                }
        
                error_page   500 502 503 504  /50x.html;
                location = /50x.html {
                    root   html;
                }
        
            }
        }
        

        【访问效果】

        本机的ip被分配到133机器,此后一直都是133机器

        将131服务器的ip_hash;去掉后,再访问192.168.200.131就会采用轮询的策略依次访问两台上游服务器

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

        【删掉ip_hash;后的访问情况】

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 通过$cookie_jsessionid做会话管理

      用户首次访问服务器时,服务器会给用户生成session并下发一个存于cookie的jsessionid,用户cookie缓存没有清空期间,对服务器的访问都会携带这个cookie和其中的jsessionid,用来匹配服务器存储的session,匹配上就认为用户已经登录

      nginx可以根据客户端请求cookie中的jsessionid取哈希值将jsessionid相同的请求始终转发到同一台服务器

      除此以外,nginx除了取cookie中的信息,URI和URL中的信息,还可以取请求头中的其他信息来做用户流量分发

      这种方式可以替代ip_hash做局域网中的请求转发,不会发生类似ip只有几个导致大量请求集中在某台服务器的现象,没有细讲,只是说用jsessionid来做流量分发可以替代ip_hash处理这种场景

      【RequestHeader中的所有信息】

      这里面的参数都可以用来做哈希实现用户流量分发,一般挑选能对用户起区分作用的属性值,uri、ip、jsessionid等

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【代理服务器131的nginx.conf】

      注意jsessionid是tomcat服务器专门下发的,其他服务器不一定是这个参数,如果cookie中没有jsessionid这个参数这个配置会不起作用

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
          upstream request_uri_test{
          
          	#根据请求头中cookie的jsessionid的哈希值来做负载均衡和会话保持
              hash $cookie_jsessionid;
              server 192.168.200.132;
              server 192.168.200.133;
              
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://request_uri_test;
                  #root   html;
                  #index  index.html index.htm;
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
      
          }
      }
      
    • 通过$request_uri做负载均衡

      使用场景:访问相同url会转发到同一个服务器,适合用在没有cookie的场景如手机APP,将jsessionid直接带在url的最后,即使相同的请求也能根据jsessionid的不同将用户分配到特定的服务器来管理会话【如果只是集群系统并不是分布式系统下能这么用吧,而且uri整体计算的哈希值也可能不同啊,为什么能保持会话,这不是和url_hash是一回事吗?】

      这个是将系统扩容会话管理的时候讲的,不知道和url_hash有什么关联没

      • 在不支持cookie的情况下【手机上的APP不支持cookie】可以直接把jsessionid直接带在URL的后面,根据用户的jsessionid来对用户进行会话管理【存疑,是对jsessionid做哈希还是对整个uri做哈希,对整个uri做哈希还是可能被派发到其他服务器上,一样无法维持会话,而且还得是单一系统的集群才行啊】
      • 第二种情况就是类似url_hash的情况,当资源有所倾斜的时候,根据uri去确定资源位于哪一台服务器上【比如流媒体服务器,文件太大,不可能分发到所有的流媒体服务器上,也没有必要】,文件上传的时候根据请求的url算出哈希值放上对应的服务器【还是有一些问题,如做集群,其实都能解决,再代理一台给集群做负载均衡,虽然不太优雅,实际方案不了解,有机会学习一下】

      【模拟使用$request_uri来做负载均衡】

      【131的nginx.conf】

      没有项目,没部署tomcat,测试效果用nginx来代理

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
          upstream request_uri_test{
          
          	#根据请求uri的哈希值来做负载均衡和会话保持
              hash $request_uri;
              server 192.168.200.132;
              server 192.168.200.133;
              
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://request_uri_test;
                  #root   html;
                  #index  index.html index.htm;
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
      
          }
      }
      

      【测试效果】

      没带参数的访问133,带参数的访问132,实际都是访问的index.html,相同的uri访问的都是同一台服务器

      由此也衍生出问题,如果用户改动参数【比如复选框导致uri不同】,前后两次请求可能会打到不同的服务器上,这个要结合实际情况才行,不能瞎用,只是选择之一,这些都是用来在扩容后想要不改代码直接用nginx做扩容的

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 通过lua脚本做更加复杂的定向流量分发

    可以看出,ip_hash或者$request_uri都有很多的问题,lua语言可以实现更加复杂的控制,讲OpenResty的时候再讲

    这些方式结合起来能够应付90%依靠集群和nginx做负载均衡的方式来最小成本的成倍系统扩容

  3. 使用第三方模块对nginx进行升级

    目的是使用sticky模块实现对静态服务器的会话保持,sticky是google开源的一个工具,利用cookie来做负载均衡和会话保持的扩展版本,这个功能是原生nginx开源版本不包含的功能,需要手动安装,sticky的功能比原有的hash $cookie_XXX要多的多得多,原有依靠cookie的jsessionid来做会话保持需要上游服务器下发了jsessionid才能实现会话保持的作用,sticky不需要上游服务器下发任何东西,利用sticky就可以实现nginx服务器集群的负载均衡和会话管理

    sticky对会话的管理和tomcat的jsessionid理念是一样的,用nginx来生成一个专属的cookie,使得不论在上游服务器是tomcat还是不是tomcat都能通过代理服务器生成的cookie进行会话管理

    应用场景:前部一台nginx服务器做路由,上游多台nginx服务器做静态文件存储【视频音乐等】,不希望多次相同客户端的请求和nginx服务器断开连接,因为建立连接是非常大的开销,此时就可以通过sticky来完成客户端与非tomcat服务器的会话状态保持【即sticky适用于不下发cookie来保持会话状态但是又需要保持会话状态的服务器】

    使用nginx的时候经常会使用第三方模块,使用第三方模块前需要甄别一下第三方模块是否稳定,用户数量多不多,不稳定不确定的不要用,用的人不多出了问题都搜不到解决办法;此外使用第三方模块还要考虑场景是否需要,不需要就不要引入

    准备五台虚拟机做演示:三台nginx【131、132、133】,两台tomcat【134、135】,注意tomcat的欢迎页配置的是test目录下的,即test作为webapp,使用192.168.200.131:8080/test/进行访问【tomcat可以使用./startup.sh后台启动或者./catalina.sh前台启动】

    • 安装sticky

      sticky在nginx上的使用说明:http://nginx.org/en/docs/http/ngx_http_upstream_module.html#sticky

      tengine版本中已经有sticky这个模块了,安装的时候需要进行编译;其他的版本还是需要单独安装,sticky模块用的比较多

      下载地址:BitBucket下载地址,里面也有对sticky使用的介绍github下载地址,github是第三方作者的,google主要开源在BitBucket上

      sticky主要还是使用BitBucket下载,该Sticky由google进行的迁移,而且一般都下载这个,下载点击downloads–Tags选择版本【很多年不更新了,但是功能已经算比较完善了,有不同的压缩格式,linux选择.gz】–点击.gz进行下载【sticky安装在nginx负载均衡器上即131】

      • 将文件nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d.tar.gz上传至linux系统/opt/nginx目录下

      • 使用命令tar -zxvf nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d.tar.gz将文件解压到当前目录

      • 在nginx的解压目录/opt/nginx/nginx-1.20.2目录下使用命令./configure --prefix=/usr/local/nginx/ --add-module=/opt/nginx/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d检查环境并配置将nginx编译安装至/usr/local/nginx目录下

        nginx重新编译会生成全新的nginx,如果老nginx配置比较多需要全部备份并编译后进行替换

        –add-module是添加第三方的模块,如果是nginx自带的模块编译的时候用的是–vs-module命令,表示模块已经在nginx的官方安装包里了

      • 在nginx的解压目录/opt/nginx目录下使用命令make安装nginx

        编译过程如果遇到sticky报错,是因为sticky过老的问题,需要修改源码,在sticky的解压文件中找到ngx_http_sticky_misc.h文件的第十二行添加以下代码

        添加后需要重新./configure一下再执行make

        安装sticky模块需要openSSL依赖,如果没有make的时候也会报错没有openssl/sha.h文件,此时使用命令yum install -y openssl-devel安装openssl-devel,再使用./configure检查环境并make进行编译安装,没有报错就安装成功了

        #include <openssl/sha.h>
        #include <openssl/md5.h>
        
      • 在nginx解压目录使用命令make upgrade检查新编译的安装包是否存在问题【在不替换原nginx的情况下尝试跑一下新的nginx】,而不是直接把新的替换掉旧的

        nginx解压目录中objs是nginx编译后的文件,objs目录下的nginx.sh就是新编译的nginx可执行文件,想要平滑升级nginx不破坏原有配置可以将该nginx.sh文件直接替换掉原安装目录中的nginx.sh,但是一定要注意原文件的备份,

      • 使用命令mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old 对原nginx可执行文件进行备份,使用命令cp nginx /usr/local/nginx/sbin/将nginx解压包objs目录下的nginx.sh拷贝到目录/usr/local/nginx/sbin/

        不要直接用命令make install,会直接把老的nginx全部覆盖掉,生成全新的nginx,之前的配置全都会丢失

      • 使用命令./nginx -V来查看nginx的版本和nginx的编译安装参数

      • 使用命令systemctl start nginx启动nginx,并使用浏览器访问nginx观察访问是否正常,访问正常即nginx升级第三方模块成功

        注意被代理的上游服务器要提前正常运行,否则访问代理服务器会报错,还需要进一步排错

  4. 使用sticky进行会话管理

    • sticky的作用域和hash各种参数一样在upstream中【代表某个服务器集群】

    • nginx.conf

      没有添加sticky配置的情况下默认是轮询,只添加了sticky效果是nginx给用户下发一个默认名为route的cookie,根据这个cookie来做用户的会话管理和负载均衡,默认没有有效期

      sticky对tomcat服务器和nginx服务器的效果都是一样的

      sticky常用的配置有:

      sticky name=route expires=6h

      • name为配置sticky下发的cookie属性名为指定属性名route,不配置默认也是route【千万不要将这个cookie的名字设置成和tomcat下发的名字jsessionid一样,会出问题,如果tomcat和nginx都下发jsessionid,nginx和tomcat每次都会因为找不到对应的jsessionid而一直重复更改jsessionid,nginx和tomcat共用一个jsessionid,导致nginx一直新生成jsessionid且对新生成的jsessionid做哈希并做负载均衡,就达不到根据指定cookie做负载均衡的效果了】;

        经过观察,jsessionid只在两个值之间变化,应该是除了cookie还有其他的用户匹配确认机制

      • expires为设置名为route的cookie的有效时间

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout  65;
          
          #代理tomcat服务器集群时upstream的名字stickytest不能带下划线,会报400错误;nginx服务器集群可以用下划线
          upstream stickytest{
          
          	#使用sticky来做会话管理
              sticky;
              server 192.168.200.134:8080;
              server 192.168.200.135:8080;
              
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://stickytest;
                  #root   html;
                  #index  index.html index.htm;
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
      
          }
      }
      

      【配置文件没有配置sticky】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【配置sticky后】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

keepalive

keepalive不是keepalived,keepalive能够极大的减少nginx三次握手的开销,keepalive是HTTP提供与服务端保持链接

【百度的keepalive】

浏览器发起请求默认就期望支持keepalive,keepalive是指通过三次握手一次性建立好连接管道,再次发起请求保持之前建立的TCP连接,在浏览器发起请求的时候请求头中的Connection:keep-alive就已经示意浏览器想要keepalive,响应头中也有Connection:keep-alive是服务器看到请求头中想要用keepalive的方式,服务器自身也支持这种方式,也愿意支持这种方式,就使用keepalive的方式;一般在对服务器主页的访问的请求和响应头中会看到Connection为keepalive,对图片、js文件等请求不带有Connection参数,更多的是缓存相关的参数,表示浏览器没有明显期望使用keepalive的方式

图片、js文件请求头和响应头中都不含Connection:keepalive;看了以下百度翻译的请求中css文件请求头中含有,但是响应头中没有

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. keepalive的使用时机

    • 在一个页面停留的时间不会太长【比如百度的首页】,很快就会发起点击事件再次请求的情况,就可以使用keepalive的方式,复用此前建立的TCP连接;

    • 主请求是一个骨架,内部还有很多对其他资源的引用,这些静态资源短时间一般都不怎么变化,可以走浏览器缓存,速度会更快,对服务器的压力也小

      对引用资源的请求一般只请求一次【?这不对吧,多次访问相同的页面也会再次发起请求啊,难道是走缓存吗,这里不能理解,应该理解成静态资源不咋变化,后续对静态资源的请求可以走浏览器缓存】,更多的时候希望把静态资源缓存在浏览器中,因为静态的请求一般不会变化

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 在nginx中对客户端keepalive做配置

    keepalive既可以对浏览器做配置,也可以通过nginx来设置keepalive

    nginx配置keepalive的官方文档:https://nginx.org/en/docs/http/ngx_http_upstream_module.html#keepalive_time

    • nginx中对客户端keepalive的可配置参数详解

      server是反向代理服务器内部的一个节点,该节点下对keepalive的配置参数如下:

      • keepalive_disable:把某些浏览器的长连接功能给禁用掉,并不是直接把keepalive给禁用掉,取值是浏览器的具体型号,默认值是微软的IE6版本【值是asie6】,该版本的浏览器对服务器端的keepalive是有一些问题的,所以直接把IE6的版本禁掉了,一般默认是不动这个参数的;官方文档中keepalive_disable位于ngx_http_core_module,对浏览器的keepalive配置位于该模块;对上游服务器的keepalive配置位于ngx_http_upstream_module模块下

      • keepalive_requests:建立起可复用的长连接之后,一个连接通道能同时发起多少个并发请求,默认是1000个【这是针对客户端的单个连接的,一般来说,一次请求中发起的并发请求并不是都是打向一个站点的,100个引用资源的并发请求可能打到同一个站点就10来个。1000的限值完全就够用了,不需要像一些nginx调优说的把通道的并发连接限制改大】;一个TCP管道不是单个时刻只能处理一个请求,服务器和客户端的操作系统的网络全部都是异步的,并不是同步阻塞式【单个请求必须处理响应完成后才能发起下一个请求】的,建立起可复用的连接通道以后,可以在连接上并发发起很多请求,以时间回调的机制来区分返回的数据;这个参数经常被调优的文章提起,但是增大了不会起到很明显的作用,因为是针对浏览器单一的可复用连接同时发起的请求个数

      • send_timeout:两次向客户端写操作间的时间间隔大于设置的时间连接通道会被强制关闭,默认值是60s,可以将keepalive_timeout看成请求的间隔时间,send_timeout看成响应数据的间隔时间;这个send_timeout有时候比较坑,比如后台服务器准备数据时间比较长,需要两分钟,此时用户是同步请求,正在等待数据返回,一旦准备数据的时间超过两次响应数据的时间,连接会被强制关闭,准备的数据就返回不回去了,这个对文件下载没影响,如果在下载文件,连接不会断;注意send_timeout时间设置不能设置的太小,如果系统中有耗时操作超过send_timeout的设定时间会直接强制断开连接,而数据还在准备过程中,中间没有数据传输,数据将永远无法响应给用户,表现为用户请求了,但是响应不了数据,一定不能设置的过小,而且有耗时操作还要将该参数适当调大

      • keepalive time:限制keepalive的连接最大时长,即一个连接创建时长达到设置的最大连接时长连接会被强制失效【后面解释说不是强制把TCP连接断开,而是让keepalive的保持时间失效】,默认是一小时,避免因为一些专门消耗nginx连接资源的攻击导致对nginx资源的浪费

      • keepalive_timeout:指的是再次点击和上次点击即任意两次请求间的时长超过65秒才会关闭连接[超过65秒没反应就把链接关掉],即两次客户端活跃的时间间隔高于该设置值就关闭连接【后面也解释会让keepalive失效,但是没说keepalive失效和断开TCP连接是什么关系】,理解成请求间隔时间大于设定值就关闭连接,这个值不能设置的过大[过大消耗资源,因为连接数多],也不能设置的过小[过小建立连接的次数多];keepalive_timeout可以配置一个参数也可以配置两个参数,配置两个参数会多出一个keep-Alive:timeout=65,这和HTTP协议的版本有关系,HTTP1.0版本需要在请求头添加该参数,1.1版本以后就不需要该配置了;而且1.0和1.1版本的HTTP协议在keepalive的实现上也有差异,经可能使用1.1版本以后得HTTP协议

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      上述所有配置客户端keepalive的设置都在nginx代理服务器的http模块下,上述配置都可以单独进行追加

      【nginx反向代理服务器对客户端的keepalive设置】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 对浏览器的keepalive配置

      在很多场景下还会要求避免使用长连接,只想建立短连接【比如高频访问的接口,压根对同一个用户就不可能产生第二次请求,同时对用户的数据也不需要进行缓存;针对很多客户端,每个客户端都只请求一次或者请求间隔比较长,比如开一台时间同步的服务器,有两千台服务器都需要隔段时间更新同步一下时间】,此时可以将keepalive关闭掉,

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          
          #这是nginx默认的对keepalive的配置,意思是当浏览器与nginx建立了一次连接之后,这个连接最长能存在多长时间不会被关闭掉,时间单位是秒,也不是建立连接后65秒就将连接关掉,指的是再次点击和上次点击即任意两次请求间的时长超过65秒才会关闭连接[超过65秒没反应就把链接关掉],这个值不能设置的过大[过大消耗资源,因为连接数多],也不能设置的过小[过小建立连接的次数多]
          keepalive_timeout  65;
          
          upstream stickytest{
              sticky;
              server 192.168.200.134:8080;
              server 192.168.200.135:8080;
          }
      
          server {
              listen       80;
              server_name  localhost;
      
              location / {
                  proxy_pass http://stickytest;
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
      
          }
      }
      
      • keepalive_timeout=0

        该配置表示禁用keepalive功能

        【代理服务器的nginx.conf】

        worker_processes  1;
        
        events {
            worker_connections  1024;
        }
        
        
        http {
            include       mime.types;
            default_type  application/octet-stream;
            sendfile        on;
            keepalive_timeout  0;
            upstream stickytest{
                sticky;
                server 192.168.200.134:8080;
                server 192.168.200.135:8080;
            }
        
            server {
                listen       80;
                server_name  localhost;
        
                location / {
                    proxy_pass http://stickytest;
                    #root   html;
                    #index  index.html index.htm;
                }
        
                error_page   500 502 503 504  /50x.html;
                location = /50x.html {
                    root   html;
                }
        
            }
        }
        

        【禁用keepalive的效果】

        Connection为close的时候说明服务器没有启用keepalive

        有时候可能服务器配置了keepalive禁用,但是实际还是在响应头中显示保持链接的情况,可以借助第三方的工具如charles来抓包观察是否还是显示长连接

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  3. 用charles抓包显示实际连接状态

    charles【青花瓷】是一款测试工具,charles是由mac系统迁移到windows和linux系统上的,可以实现拦截请求和模拟请求,可以用于模拟简单的并发测试,收费的,免费版也能凑合用,抓包用windows版本的就行

    下载地址:https://www.charlesproxy.com/assets/release/4.6.2/charles-proxy-4.6.2-win64.msi?k=fc1457e312

    官网:https://www.charlesproxy.com

    这玩意儿可以用来抓数据包看大公司的数据封装格式,看大公司是怎么封装数据的,http请求可以直接抓取到,https请求数据包抓取需要去系统中安装SSL伪证书【视频又说安装charles的根证书,看到charles再深入学习】,没有安装证书https协议的数据包是经过加密看不到内容的【又说https协议的数据也能通过charles拦截请求伪造证书来破解密文,以后学一学,这儿没听懂,为什么在不知道第三方…好像懂了,应该是拦截本机的请求,封装以后由charles来代替用户向目标发起请求,获取到数据以后由charles解析接口数据并将数据展现给用户接口数据的组织形式,应该是在浏览器无法看到响应的接口数据格式】

    • 使用说明

      • 红色按钮表示当前正在录制记录当前系统发出的所有请求【包括除了浏览器发起的其他请求】,打开应用就是默认开启监控功能的
      • 小扫把表示清理掉之前的所有监控记录
      • 监控的请求根据一个站点来进行分组【ip和端口】,对同一个站点访问的请求归档在一个组下
      • 没有使用keepalive的请求点开会显示keepalive为No,浏览器可能因为各种问题显示错误,此时就可以使用这种抓包工具来看;使用了keepalive会显示keepalive为yes
      • 可以在请求详情中的Timing可以看到各种延迟信息:DNS延迟、连接的延迟、请求耗时、响应耗时、连接的速度等信息
      • 对记录的请求可以右击选择repeat重复发起一次请求,点击选择Advanced Repeat可以设置重复发起多次请求,请求间的时间间隔【可以发起并发请求】,但是一般不用这个来做压力测试
      • 点击window–Active Connections可以看到当前系统开并维持了多少连接【似乎也是被记录的连接而不是实时的连接,而且右键重复发起请求不会显示在Active Connections中,即charles发起的请求不会被连接窗口记录】,在开启keepalive的前提下单个连接中的transactions字段会显示一个连接中发起了多少次请求,在关闭keepalive的前提下浏览器重复发起一次请求会创建一次新的连接

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  4. 在代理服务器nginx中对上游服务器做配置

    上游服务器和nginx之间使用keepalive一定是使用nginx做的七层代理,且代理的上游服务器是基于HTTP协议的

    • nginx对上游服务器keepalive的可配置参数

      Upstream中的配置【一组服务器集群中的配置】

      • keepalive 100:nginx与上游服务器间使用的保持会话的连接池的保留连接个数【线程池也有保留的线程个数】,这里其实还是没有讲清楚,应该是空闲时保留的连接个数最大可以是100个;使用LRU算法对连接数进行管理,不需要配置的过大,因为一个连接可以复用发起N多个请求
      • keepalive_timeout 65:连接保留超时时间,不知道这个超时时间是指向上游服务器连续两次发起请求的间隔时间还是上游服务器连续两次响应的超时时间间隔
      • keepalive_request 1000:一个TCP复用连接通道中,可以同时并发发起的请求个数为1000

      【配置示例】

      upstream stickytest{
      	keepalive 100;
      	keepalive_request 1000;
      	keepalive_timeout 65;
          server 192.168.200.134:8080;
          server 192.168.200.135:8080;
      }
      

      server中的location配置【单个站点的配置】

      • proxy_http_version 1.1:配置上游服务器的http协议版本号,默认使用的HTTP1.0协议,默认是每次发起请求后会将连接关闭,配置keepalive能很有效的减少连接上的开销,但是1.0版本的HTTP协议需要在request的请求头中增加参数Connection: keep-alive才能支持keepalive,而HTTP1.1协议默认就支持,且实现keepalive方式的效率相对于1.0版本更高;每次建立连接都需要进行三次握手,每次消耗的时间对比数据传输来说消耗的时间很长

      • proxy_set_header Connection “”:将nginx代理的请求中的请求头参数设置为空串,改成keepalive也可以【设置成这两个参数都可以【1.1版本是这样,1.0版本要求参数必须写成Connection: keep-alive】,改成空串1.1版本就默认使用长连接】,因为默认是将该Connection参数改成close

        nginx做反向代理服务器代理用户的请求,转发请求过程中会把请求的头信息全部清理掉【像客户端信息,客户端ip等信息】,然后再转发给后端服务器【后端服务器实际上不知道用户的具体信息,但是为啥之前用sticky来生成cookie做负载均衡会话管理的时候,上游服务器能够把jsessionid改成正确的值呢,说明还是知道用户来自于那个ip下的嘛】,而且浏览器和nginx建立起长连接后,nginx默认情况下会将请求头中的Connection参数改成closeConnection:close,明确告诉上游服务器和nginx之间不要使用长连接,每次nginx和上游服务器之间的通讯都单独建立连接

      【配置示例】

      server {
          listen       80;
          server_name  localhost;
      
          location / {
          	
          	#配置示例
          	proxy_http_version 1.1;
          	proxy_set_header Connection "";
          	
              proxy_pass http://stickytest;
              
          }
      
          error_page   500 502 503 504  /50x.html;
          location = /50x.html {
              root   html;
          }
      }
      

      【nginx服务器对上游服务器的keepalive配置】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【上游服务器】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

AB压测工具

AB测试工具是apache基金会下的,全称apache benchmark,通常简称为AB Test,清量简单的小工具,适合机器配置不高的时候使用,这最早是apache httpd服务器中内嵌工具包的一个工具

随便找一台虚拟机安装进行测试,安装在tomcat服务器上对不含该tomcat的后台系统进行压测,本次安装在134上

安装AB工具
安装步骤
  1. 使用命令yum install httpd-tools安装ab工具
AB工具的命令
  1. 命令参数说明

    完整命令行:ab [options] [http[s]://]hostname[:port]/path

    一般用-n-c两个参数就足够了,使用ab命令就能调出以下的参数说明,用options代指,深度使用需要专门学习一下

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • -n

      即requests,用于指定压力测试总共的执行次数。

    • -c

      即concurrency,用于指定的并发数。

    • -t

      即timelimit,等待响应的最大时间(单位:秒)。

    • -b

      即windowsize,TCP发送/接收的缓冲大小(单位:字节)。

    • -p

      即postfile,发送POST请求时需要上传的文件,此外还必须设置-T参数。

    • -u

      即putfile,发送PUT请求时需要上传的文件,此外还必须设置-T参数。

    • -T

      即content-type,用于设置Content-Type请求头信息,例如:application/x-www-form-urlencoded,默认值为text/plain。

    • -v 即verbosity,指定打印帮助信息的冗余级别。

    • -w 以HTML表格形式打印结果。

    • -i 使用HEAD请求代替GET请求。

    • -x 插入字符串作为table标签的属性。

    • -y 插入字符串作为tr标签的属性。

    • -z 插入字符串作为td标签的属性。

    • -C 添加cookie信息,例如:“Apache=1234”(可以重复该参数选项以添加多个)。

    • -H 添加任意的请求头,例如:“Accept-Encoding: gzip”,请求头将会添加在现有的多个请求头之后(可以重复该参数选项以添加多个)。

    • -A 添加一个基本的网络认证信息,用户名和密码之间用英文冒号隔开。

    • -P 添加一个基本的代理认证信息,用户名和密码之间用英文冒号隔开。

    • -X 指定使用的ip和端口号,例如:“126.10.10.3:88”。

    • -V 打印版本号并退出。

    • -k 使用HTTP的KeepAlive特性。

    • -d 不显示百分比。

    • -S 不显示预估和警告信息。

    • -g 输出结果信息到gnuplot格式的文件中。

    • -e 输出结果信息到CSV格式的文件中。

    • -r 指定接收到错误信息时不退出程序。

    • -h 显示用法信息,其实就是ab -help。

测试

实际测试的时候最好不要测试和服务运行都在同一台电脑上,因为发送请求也是非常消耗资源的,在本机上测试自己只能对比几种配置的性能,没法模拟系统真实的性能表现

nginx即使只代理一台服务器也是有意义的,第一有更多的方法来对上游服务器进行统一管理【负载均衡,健康检查,安全管理】,第二是做外网和内网的隔离【只要把代理服务器保护好,黑客就无法通过代理服务器入侵内网中的上游服务器,让系统更安全】,第三是节省ip,每个服务器都要在公网能被访问需要很多的公网ip,第四可以把在每台上游服务器都要处理的业务抽取到nginx、代理服务器上进行前置处理再转发,实现逻辑解耦【比如Kong网关、API six网关就是通过nginx扩展来的,可以把鉴权、网络防火墙WAF等共性功能全部抽取到nginx服务器上】

压测不要设置的请求太少,太少了看不出问题,一般至少都是连续请求五分钟以上,这里100000是很少的,连续跑100000一直跑半个小时系统就很可能崩了,此时才是真正考验代码健壮性【对系统内存的管理】的时候

注意ABtest自身不带keepalive,只是发送请求;发送请求给nginx还是tomcat都没有使用keepalive;nginx和tomcat间有keepalive

  1. 测试直连nginx服务器

    直连的nginx服务器是nginx的keepalive的默认配置

    • 使用命令ab -n 100000 -c 30 http://192.168.200.132/

      并发十万个请求,此前并发过一个一万个请求的测试,注释中有的数据是1万并发量的数据

      必须要添加/,否则请求会失效

      【测试结果】

      [root@node3 ~]# ab -n 100000 -c 30 http://192.168.200.132/
      This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
      Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
      Licensed to The Apache Software Foundation, http://www.apache.org/
      
      Benchmarking 192.168.200.132 (be patient)
      Completed 10000 requests
      Completed 20000 requests
      Completed 30000 requests
      Completed 40000 requests
      Completed 50000 requests
      Completed 60000 requests
      Completed 70000 requests
      Completed 80000 requests
      Completed 90000 requests
      Completed 100000 requests
      #显示一共发了100000个请求
      Finished 100000 requests
      
      
      Server Software:        nginx/1.20.2
      Server Hostname:        192.168.200.132
      Server Port:            80
      
      Document Path:          /
      Document Length:        491 bytes
      
      Concurrency Level:      30
      Time taken for tests:   22.487 seconds
      Complete requests:      100000
      Failed requests:        0
      Write errors:           0
      #Total transferred是总共传输的数据量
      Total transferred:      72400000 bytes
      HTML transferred:       49100000 bytes
      #Requests per second这个就是目标服务器的QPS,简单压测了10000个请求,每秒能够处理的请求是2307.55个,这里改成100000个请求相较于之前的QPS涨了一倍,7000已经是很高的QPS了,因为本机是虚拟机,虚拟机的性能本来就不高,分配的资源也不高,单核2g【为什么我的单核两g比老师的单核1g还要慢这么多?老师是7000】,7000并没有达到真实nginx的性能瓶颈,还要结合系统的复杂度和网络带宽,如果系统复杂度很高QPS也高不了,QPS和网络带宽也有关系,注意老师演示网络还是走的内网,QPS是一个综合因素,和硬件、网络带宽、系统复杂程度都有关系
      Requests per second:    4447.05 [#/sec] (mean)
      #这是响应延迟时间
      Time per request:       6.746 [ms] (mean)
      Time per request:       0.225 [ms] (mean, across all concurrent requests)
      #吞吐量,所有请求全部打到目标服务器总体的下载响应数据的速率,这个速率并没有跑满网络带宽,一个是因为请求量少,还有一个原因是响应的数据少,现在只响应一个很简单的html页面,传输数据的过程很快,但是建立连接返回请求的速度相对较慢,所以速度很难跑满
      Transfer rate:          3144.20 [Kbytes/sec] received
      
      Connection Times (ms)
                    min  mean[+/-sd] median   max
      Connect:        0    2   1.5      1      16
      Processing:     1    5   4.4      3      26
      Waiting:        0    4   4.3      3      24
      Total:          2    7   4.8      5      28
      
      Percentage of the requests served within a certain time (ms)
        50%      5
        66%      5
        75%      6
        80%      7
        90%     16
        95%     19
        98%     20
        99%     21
       100%     28 (longest request)
      
  2. 测试用反向代理服务器代理后端服务器

    代理服务器配置去掉对上游服务器的keepalive配置,同时只代理一台服务器

    【131的nginx.conf】

    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout 65 65;
        keepalive_time 1h;
        keepalive_requests 1000;
        send_timeout 60;
        upstream stickytest{
            server 192.168.200.132;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            location / {
            	proxy_http_version 1.1;
        		proxy_set_header Connection "";
                proxy_pass http://stickytest;
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    

    【测试结果】

    在配置反向代理服务器,代理服务器和客户端有keepalive,代理服务器和上游服务器间没有keepalive的情况下吞吐量和QPS都直线下降【几乎一半还要多一些】

    [root@node3 ~]# ab -n 100000 -c 30 http://192.168.200.131/
    This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking 192.168.200.131 (be patient)
    Completed 10000 requests
    Completed 20000 requests
    Completed 30000 requests
    Completed 40000 requests
    Completed 50000 requests
    Completed 60000 requests
    Completed 70000 requests
    Completed 80000 requests
    Completed 90000 requests
    Completed 100000 requests
    Finished 100000 requests
    
    
    Server Software:        nginx/1.20.2
    Server Hostname:        192.168.200.131
    Server Port:            80
    
    Document Path:          /
    Document Length:        491 bytes
    
    Concurrency Level:      30
    Time taken for tests:   58.312 seconds
    Complete requests:      100000
    Failed requests:        0
    Write errors:           0
    Total transferred:      72400000 bytes
    HTML transferred:       49100000 bytes
    #QPS由4447变成了1714
    Requests per second:    1714.93 [#/sec] (mean)
    Time per request:       17.493 [ms] (mean)
    Time per request:       0.583 [ms] (mean, across all concurrent requests)
    #吞吐量由原来的3000变成1200了
    Transfer rate:          1212.51 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    3   2.2      2      18
    Processing:     3   14   7.2     12      67
    Waiting:        1   13   7.2     10      65
    Total:          6   17   6.9     16      68
    
    Percentage of the requests served within a certain time (ms)
      50%     16
      66%     19
      75%     20
      80%     21
      90%     28
      95%     34
      98%     37
      99%     38
     100%     68 (longest request)
    
  3. 测试用反向代理服务器代理后端nginx服务器并开启反向代理服务器与后端服务器的keepalive

    【nginx.conf】

    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout 65 65;
        keepalive_time 1h;
        keepalive_requests 1000;
        send_timeout 60;
        upstream stickytest{
            keepalive 100;
            keepalive_requests 1000;
            keepalive_timeout 65;
            server 192.168.200.132;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            location / {
                proxy_http_version 1.1;
                proxy_set_header Connection "";
                proxy_pass http://stickytest;
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    

    【测试结果】

    当代理服务器和上游服务器间开启keepalive功能,QPS和吞吐量都极大提升,我这上面逼近测试请求直连nginx服务器的3/5,演示中只达到了直连时的3/4,响应延迟也提升了非常多,但和直连还是有一定差距;

    在实际生产中,增加配置代理服务器和上游服务器间的keepalive一般对系统性能的提升能接近40%左右甚至更多

    [root@node3 ~]# ab -n 100000 -c 30 http://192.168.200.131/
    This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking 192.168.200.131 (be patient)
    Completed 10000 requests
    Completed 20000 requests
    Completed 30000 requests
    Completed 40000 requests
    Completed 50000 requests
    Completed 60000 requests
    Completed 70000 requests
    Completed 80000 requests
    Completed 90000 requests
    Completed 100000 requests
    Finished 100000 requests
    
    
    Server Software:        nginx/1.20.2
    Server Hostname:        192.168.200.131
    Server Port:            80
    
    Document Path:          /
    Document Length:        491 bytes
    
    Concurrency Level:      30
    Time taken for tests:   35.746 seconds
    Complete requests:      100000
    Failed requests:        0
    Write errors:           0
    Total transferred:      72400000 bytes
    HTML transferred:       49100000 bytes
    Requests per second:    2797.50 [#/sec] (mean)
    Time per request:       10.724 [ms] (mean)
    Time per request:       0.357 [ms] (mean, across all concurrent requests)
    Transfer rate:          1977.92 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    2   1.0      1      11
    Processing:     2    9   4.6      8      37
    Waiting:        2    9   4.6      7      37
    Total:          3   11   4.5      9      38
    
    Percentage of the requests served within a certain time (ms)
      50%      9
      66%     10
      75%     11
      80%     12
      90%     17
      95%     23
      98%     25
      99%     25
     100%     38 (longest request)
    
  4. 测试直连tomcat服务器

    一般来说从业人员对tomcat的性能不是特别乐观,讲师说今天的测试要推翻大家常规的认识,实际上tomcat还是比nginx差,一般的服务器配置nginx能跑QPS两万左右,tomcat能跑七千左右,不像传闻的tomcat只能跑几百

    linux上安装的tomcat【直接解压的tomcat】,默认应该是以APR的形式运行的网络接口,APR的形式是使用在本机C语言实现的网络接口,极大的优化了tomcat的性能,也可以利用java的nio解决的这个问题【windows上安装的tomcat默认的启动方式是bio的】,有的版本tomcat默认配置的就是APR启动方式,性能会更高

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 测试直连访问tomcat服务器的jsp页面

    【测试效果】

    除了测试直连jsp页面,还测试了以下访问tomcat的静态页面,访问jsp竟然快的多,而且jsp页面的内容更多,html只有一句话;

    jsp的qps是4000左右,好的时候能上4700,但是html只有3000左右

    [root@node3 ~]# ab -n 100000 -c 30 http://192.168.200.135/index.jsp
    This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking 192.168.200.135 (be patient)
    Completed 10000 requests
    Completed 20000 requests
    Completed 30000 requests
    Completed 40000 requests
    Completed 50000 requests
    Completed 60000 requests
    Completed 70000 requests
    Completed 80000 requests
    Completed 90000 requests
    Completed 100000 requests
    Finished 100000 requests
    
    
    Server Software:        nginx/1.20.2
    Server Hostname:        192.168.200.135
    Server Port:            80
    
    Document Path:          /index.jsp
    Document Length:        153 bytes
    
    Concurrency Level:      30
    Time taken for tests:   27.013 seconds
    Complete requests:      100000
    Failed requests:        0
    Write errors:           0
    Non-2xx responses:      100000
    Total transferred:      30300000 bytes
    HTML transferred:       15300000 bytes
    #单机直连在本机上QPS是3000多,基本性能比nginx略差,真实的水平对于一般的服务器配置,nginx的QPS能跑到2万左右,tomcat大概能跑到七八千,
    Requests per second:    3701.86 [#/sec] (mean)
    Time per request:       8.104 [ms] (mean)
    #最小延迟
    Time per request:       0.270 [ms] (mean, across all concurrent requests)
    #返回结果数据大了这个会大一些
    Transfer rate:          1095.37 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    2   1.5      1      12
    Processing:     1    6   6.5      3      31
    Waiting:        0    6   6.5      3      30
    Total:          2    8   6.4      5      31
    
    Percentage of the requests served within a certain time (ms)
      50%      5
      66%      6
      75%      9
      80%     14
      90%     21
      95%     22
      98%     23
      99%     24
    #最长延迟有31个
     100%     31 (longest request)
    
  5. 测试用反向代理服务器代理后端tomcat服务器并开启反向代理服务器与后端服务器的keepalive

    用了反向代理吞吐量和QPS还提升了,这是因为nginx的并发性能比tomcat好,只用ab直连tomcat,由于没有keepalive,性能最差;用ab直连nginx,期间没有keepalive但是nginx性能高,但是nginx连接tomcat有keepalive,总体的性能就高于ab直连tomcat,但是这不是实际情况,只是因为ab没有keepalive功能,把nginx和tomcat服务器的keepalive功能关了,演示结果的QPS直接从4000降成了3000,比ab直连的3200稍低【因为中转了】,随着硬件性能的提升这个比例还会放大

    当 不支持keepalive的时候在tomcat前置nginx才会是系统的性能明显提升【nginx性能高,nginx和tomcat间能keepalive】,一般都是很特殊的场景【客户端不是浏览器或者一些浏览器没有keepalive】,正常情况都会努力的让客户端支持keepalive

    【代理服务器配置】

    对客户端请求的keepalive配置在http模块中,对上游服务器的keepalive配置在upstream中

    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout 65 65;
        keepalive_time 1h;
        keepalive_requests 1000;
        send_timeout 60;
        upstream stickytest{
            keepalive 100;
            keepalive_requests 1000;
            keepalive_timeout 65;
            server 192.168.200.135:8080;
        }
    
        server {
            listen       80;
            server_name  localhost;
    
            location / {
                proxy_http_version 1.1;
                proxy_set_header Connection "";
                proxy_pass http://stickytest;
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
        }
    }
    

    【测试效果】

    关掉nginx和tomcat的keepalive就不试了

    [root@node3 ~]# ab -n 100000 -c 30 http://192.168.200.131/
    This is ApacheBench, Version 2.3 <$Revision: 1430300 $>
    Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/
    Licensed to The Apache Software Foundation, http://www.apache.org/
    
    Benchmarking 192.168.200.131 (be patient)
    Completed 10000 requests
    Completed 20000 requests
    Completed 30000 requests
    Completed 40000 requests
    Completed 50000 requests
    Completed 60000 requests
    Completed 70000 requests
    Completed 80000 requests
    Completed 90000 requests
    Completed 100000 requests
    Finished 100000 requests
    
    
    Server Software:        nginx/1.20.2
    Server Hostname:        192.168.200.131
    Server Port:            80
    
    Document Path:          /
    Document Length:        177 bytes
    
    Concurrency Level:      30
    Time taken for tests:   42.820 seconds
    Complete requests:      100000
    Failed requests:        0
    Write errors:           0
    Total transferred:      41500000 bytes
    HTML transferred:       17700000 bytes
    Requests per second:    2335.35 [#/sec] (mean)
    Time per request:       12.846 [ms] (mean)
    Time per request:       0.428 [ms] (mean, across all concurrent requests)
    Transfer rate:          946.45 [Kbytes/sec] received
    
    Connection Times (ms)
                  min  mean[+/-sd] median   max
    Connect:        0    2   1.3      2      14
    Processing:     2   11   6.7      8     206
    Waiting:        2   10   6.7      8     206
    Total:          4   13   6.5     10     212
    
    Percentage of the requests served within a certain time (ms)
      50%     10
      66%     12
      75%     15
      80%     18
      90%     23
      95%     24
      98%     26
      99%     29
     100%    212 (longest request)
    

Nginx反向代理流程

可以在简历中体现对nginx理解的部分了,这部分在网络编程中详细有讲解

nginx是多进程同时运行业务逻辑,worker进程处理业务,worker进程中又设置多个线程并发地处理请求的读写操作

Nginx代理请求流程

  1. 请求流程和相关配置

    • 浏览器发起的请求一定是http请求,即使https请求也是http请求的进一层封装

      【Http1.x协议报文组成】

      所有发送的请求都会先转换成按一定规则排列的文本,再将文本转换成二进制形式

      • 请求行:第一行请求行中记录了请求方法、请求URL和HTTP协议的版本;并使用空格分隔,以回车符和换行符作为一行的结束标记;
      • 请求的头部:请求头是以key:value的方式一行一行的记录,比如Connection:keepalive,每个参数都独占行,以回车符和换行符结尾,请求头结束以单独的回车符+换行符结尾;
      • 请求数据:也叫请求体,只有post方法和put方法才有请求体,请求体中的参数;get方法的参数是直接写在请求行的URL中的,URL中是以英文问号打头,以&连接参数;

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

      【请求报文】

      • General就是请求行的内容【又补充说General不是原始的报文,还有包装的信息】

      外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • nginx请求处理流程

      • nginx会接收到请求的二进制报文,nginx会将该报文反序列化的可读文本,逐行解析请求报文的内容,nginx解析完请求行的内容,会先去解析请求头,去获取请求希望使用的请求协议、想不想使用keepalive等其他请求参数;当请求头处理完以后处理请求体,由于用户可能上传比较大的文件,在读取请求体的同时设置一层缓冲区,缓冲区用于缓冲【缓冲不是缓存,但是没有解释,缓冲的过程中可以一点一点地读取用户提交的数据,后面解释了一下,缓冲是内存中一块区域,操作结束数据就清除了;缓存不一样,缓存中的数据会长期存在并且会被复用,缓冲更多含义临时中转的意味】客户端提交的已经读取到的内容,配置nginx的参数client_body_buffer_size可以配置缓冲区的可使用空间;

      • 读取的过程中会根据nginx的配置proxy_request_buffering决定nginx是否在读取的过程中就向上游服务器发起请求,如果该参数设置为proxy_request_buffering off,就会一边读取请求体一边并行向上游服务器发起请求;如果proxy_request_buffering on,会完全读取到请求体后再向上游服务器发起请求

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • nginx向上游服务器发起请求流程

      • nginx会先从用户配置的upstream服务器列表中选择一台服务器,默认情况下是以链表的形式轮询去选择服务器【即服务器列表会被nginx读取配置成一个链表】,nginx会用全异步的形式去发起请求,nginx先向操作系统的epoll事件队列中注册一个要连接请求的事件,然后经过TCP三次握手建立与上游服务器的连接,在nginx准备请求数据报文的同时会注册回调函数【怎么听着感觉回调函数是在事件注册到epoll的同时就注册的,在建立好TCP连接的时候触发,这里不清楚回调函数注册和调用的时机,或许学习计算机网络的时候会进一步学习,注意回调函数注册就是连接事件注册在epoll队列的时候,回调是连接建立完成触发可写事件的时候】,【在epoll或者java的nio中所有数据读取、网络连接建立和断开都是以事件的形式触发的,一旦触发就会去执行回调方法/函数中的内容】,当连接建立完成后会触发一个可写的事件,事件触发同时系统向nginx表明连接建立完成,可以向上游服务器执行写入数据的操作【建立连接触发可写事件是异步操作】,nginx此时才会去调取回调函数,回调函数执行的过程中向上游服务器转发客户端请求,转发客户端请求的时候nginx可以设置转发请求请求头的内容,将新的HTTP协议打包成二进制报文的格式发送给上游服务器【以上所有的操作都是异步的】,

      • 上游服务器处理完请求后会返回响应结果给nginx,nginx还是通过epoll事件列表触发回调函数处理上游服务器返回的数据【说netty也会讲,epoll也有专门的课程】,nginx处理上游服务器返回的数据时会根据配置proxy_buffering来判断是否对上游服务器返回的数据进行缓冲,由于上下游服务器网络传输速率的问题,如果上游服务器响应的文件过大,此时就要考虑文件怎么处理的问题,不可能上游服务器传输一部20G的电影全部加载到nginx服务器的内存中;在nginx中有一个proxy_buffering的配置,如果proxy_buffering off,那么nginx将不使用缓冲功能【理解成缓冲区域非常小,几乎忽略不计,确认了proxy_buffering off就是不会使用内存缓冲区】,此时客户端读取多少nginx就从上游服务器获取多少,这种方式最大的问题是nginx中读取上游服务器数据的线程一直处于占用状态,和上游服务器的网络连接一直终断不了【比如20G的电影传递到nginx只要20s,但是传递给用户需要20h,没有缓冲区该nginx与上游服务器的连接就会持续20h无法终断,nginx的读取线程也会一直处于被占用的状态,夸张手法】;一般都将nginx的缓冲参数设置成proxy_buffering on,让上游服务器的数据缓冲到缓冲区,从而断开网络连接和释放读取线程,当缓冲区的数据全部传递给客户端后再次建立或者复用连接再次读取数据到缓冲区直至数据完全传递给客户端,nginx通过参数proxy_buffers对缓冲区进行配置,proxy_buffers 32 64K表示在内存中分配的缓冲区大小为32个64K大小的内存缓冲块【2M,一般来说传递的数据就只有几百k或者几M大小,20G是非常极端的情况】

        注意不论是否使用请求体的缓冲功能,请求头的缓冲功能是一直开启的,配置请求头缓冲区的参数是proxy_buffer size确定的,如果把请求体的缓冲功能关闭即proxy_buffering off,nginx会直接利用请求头缓冲区来简单缓冲数据,即上面理解成的缓冲区域非常小,其实就是在请求头缓冲区开辟出的小缓冲区【除了简单请求,一般请求头缓冲区的大小都是不够的,也不会向磁盘缓冲区缓存数据,此时不会断开与上游服务器的连接,会一直等待读取上游服务器数据】

      • 如果内存缓冲区的容量用光了【内存缓冲区够用不会使用磁盘缓冲区】,此时会继续接收上游服务器的数据,将数据写入磁盘【磁盘缓冲区】,磁盘的临时文件总大小【所有的临时文件】由参数proxy_max_temp_file_size 1024m确定,默认情况下一个1G大小【再次听解释好像还是说这个1G和一次性从上游服务器读取的大小有关,并不一定是每个文件只能写8k,只是每次只能写8k,这里的1G大小到底是指磁盘划分出临时文件最大总大小还是单次从上游服务器读取文件的大小上限,以及单次写8k是一个文件只能写8k,文件数据写成多个文件的形式还是文件数据每次只能写8k,但是文件数据都写在一个文件中需要明确】;临时文件的位置由参数proxy_temp_path /spool/nginx/proxy_temp 1 21 2表示在/spool/nginx/proxy_temp目录下在创建一个2级目录【建立两级目录的目的是当并发量太大的情况下如果不分级存储那么一个目录下的文件数量可能会非常高,此时的检索效率就会降低】,实际的临时文件路径可能是/spool/nginx/proxy_temp/7/45/00000123457;临时文件每次写入数据时写入数据量的大小由参数proxy_temp_file_write_size 8k确定,这里设置的是8k,不能设置的太大,解释的原因是写的太多占资源,没听懂啥意思;【小了和不缓存有啥区别,内存缓冲区2M,磁盘缓冲区8k,那直接把内存缓冲区加8k影响也不大啊?难道后续数据是分多个文件存入磁盘缓冲区,每个文件最大8k吗】

        上下游服务器速率问题:nginx向客户端【手机、浏览器】响应数据要通过公网,公网的网络不稳定,数据传输速率极有可能跑不满网络带宽;上游服务器和nginx间的网络一般是局域网,局域网中的速度是非常快的,非常容易就把网络带宽跑满,数据瞬间就能传递到nginx;导致数据没法不积压在nginx就直接传递到客户端

        外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    • 请求流程涉及参数总览

      这个是配置在location中吗?没有看到有配置演示,但是图上写的是location,使用的时候查询一下配置地方,配置图片就不截取了,配置参数都是以下列出的这些,配置位置图上显示location,自己再研究一下

      • set_header

        设置header

      • proxy_connect_timeout

        与上游服务器连接超时时间、快速失败

      • proxy_send_timeout

        定义nginx向后端服务发送请求的间隔时间(不是耗时)。默认60秒,超过这个时间会关闭连接

      • proxy_read_timeout

        后端服务给nginx响应的时间,规定时间内后端服务没有给nginx响应,连接会被关闭,nginx返回504 Gateway Time-out。默认60秒

        【缓冲区】

      • proxy_requset_buffering

        是否完全读到请求体之后再向上游服务器发送请求

      • proxy_buffering

        是否缓冲上游服务器数据

      • proxy_buffers 32 64k;

        缓冲区大小 32个 64k大小内存缓冲块

      • proxy_buffer_size

        header缓冲区大小

        proxy_requset_buffering on;
        proxy_buffering on;
        
        proxy_buffer_size 64k;
        
        proxy_buffers 32 128k;
        proxy_busy_buffers_size 8k;
        proxy_max_temp_file_size 1024m;
        
      • proxy_temp_file_write_size 8k

        当启用从代理服务器到临时文件的响应的缓冲时,一次限制写入临时文件的数据的大小。 默认情况下,大小由proxy_buffer_size和proxy_buffers指令设置的两个缓冲区限制。 临时文件的最大大小由proxy_max_temp_file_size指令设置。

      • proxy_max_temp_file_size 1024m;

        临时文件最大值

      • proxy_temp_path

        proxy_temp_path /spool/nginx/proxy_temp 1 2;
        

        a temporary file might look like this:

        /spool/nginx/proxy_temp/7/45/00000123457
        
  2. nginx对接收客户端请求的配置

    nginx对客户端配置可以配置在http模块、也可以配置在server模块,还可以配置在location模块;三者的关系有点像maven的依赖管理,http中配置了会被子模块server和location继承,server中配置的会覆盖http中的配置并同时被location继承,location是最小配置;对客户端整体的配置可以配置在http模块下,对服务器与客户端之间可以在server和location中进行更个性化的配置

    • 常用的对客户端配置参数

      nginx服务器在对客户请求进行代理前,需要对客户端请求进行一些校验工作和预处理

      一般来说这些配置都不用动,在特殊需求下需要进行调整

      • client_body_buffer_size

        对客户端请求体设置内存缓冲区大小, 默认32位操作系统分配8K。 64位操作系统默认是16K,如果请求体大于配置缓冲区大小,则将整个请求体或仅将其部分写入临时文件。

        这个可以适当调整,如果请求都是get请求,请求体都没啥,这个默认配置完全就够用了;如果有提交数据,需要分辨是否表单提交【表单提交16k也是完全够用的,一般1k就够用了】,如果是上传文件的需求,这个缓冲区大小需要考虑文件大于缓冲区会写入临时文件的问题,此外这个配置可以针对不同的server和location进行更改,对于专门处理文件上传的location站点可以将内存缓冲区配置的更大一些

      • client_header_buffer_size

        设置读取客户端请求头的内存缓冲区大小 ,如果一个请求行或者一个请求头字段不能放入这个缓冲区【请求头数据非常大,一般都是url特别长的情况下,其次是特殊需求下header的配置属性比较多】,那么就会使用名为large_client_header_buffers的另外一块缓冲区,正常情况下都是够用的

      • client_max_body_size 1000M;

        对请求体的文件大小进行限制,而且是在文件接收之前就对文件大小进行检测以决定是否对文件进行接收,不会等待接收文件的中途才会去检验文件的大小;检测原理是请求的请求头中有一个content-length的属性,该属性标记了请求体数据的大小【但是前面不是说请求报文生成以后转成二进制吗?难道请求头和请求体的数据是分开传递的吗】;这个需要根据实际情况进行调整,因为默认是1MB大小,连图片都不一定能上传上来,如果一个请求的大小超过配置的值,会返回413 (request Entity Too Large)错误给客户端,将size设置为0将禁用对客户端请求正文大小的检查。

      • client_body_temp_path path [level1 [level2 [level3`]]]

        当内存缓冲区的空间满了就会将数据存入磁盘缓冲区,该参数设置缓存路径,最多可以设置3层目录,避免并发请求情况下的单层目录文件过多

      • client_body_timeout

        指定客户端与服务端建立连接后发送 request body 【nginx读取请求体等了设置的时间没有读到请求体就报错并返回408,是指一直没有读取到请求体数据,请求体数据读取过程中是不计入这个时间的,如果数据读取到一半读不到数据了,这个时间从0算起,到了设置时间还没再次接收到数据就提示超时,简单的说请求体获取期间不活跃时间超过设置时间】的超时时间。如果客户端在指定时间内没有发送任何内容,Nginx 返回 HTTP 408(Request Timed Out),避免请求过程中网络中断浪费服务器网络带宽和连接资源;从这里可以看出请求体和请求头的读取很可能是分开的

      • client_header_timeout

        客户端向服务端发送一个完整的 request header 的超时时间。如果客户端在指定时间内没有发送一个完整的 request header,Nginx 返回 HTTP 408(Request Timed Out)

      • client_body_in_file_only on;

        一般不咋用这个,把body写入磁盘文件,请求结束也不会删除;一般用来做数据分析上的操作,比如想看看用户请求上传的东西是什么或者需要对这些数据做额外的处理需要设置该参数,设置以后nginx会完整的把请求体的数据写入磁盘缓冲区,即使请求没有请求体,也会记录一个0字节大小的文件,这个在线上服务器千万不要配,很容易就把服务器给撑炸了

      • client_body_in_single_buffer

        常用于二次开发,不做二次开发一般不太配置该参数;意思是尽量缓冲body的时候在内存中使用连续单一缓冲区,在二次开发时使用$request_body读取数据时性能会有所提高【request_body是nginx中的一个系统变量,通过该变量就可以读取到用户请求体中的具体内容】,如果请求体数据分布在多块区域,读取请求体的速度会慢一些,如果开辟连续的缓存区来缓冲请求体,读取时的速度会快一些。

      • large_client_header_buffers

        默认是8K大小

上游服务器获取用户IP

  1. 使用java获取用户IP地址的代码

    正常java代码从HttpServletRequest对象中可以获取用户的主机,端口和IP地址,还有获取其他信息的,记不住没关系,打个断点看看request中有啥属性对应获取

    因为java应用一般在上游,可能涉及将用户IP加入黑白名单的情况,此时需要获取用户的ip地址

    Enumeration<String> headerNames=request.getheaderNames();//获取用户的请求头信息
    String remoteHost=request.getRemoteHost();
    int remotePort=request.getRemotePort();
    String remoteAddr=request.getRemoteAddr();
    String ipAddress=requrest.getHeader("x-forwarded-for");
    

    【没有经过代理的java直接获取用户IP信息】

    127.0.0.1是IPV4的IP地址,浏览器访问IPV4的地址,后端接受到的才会是IPV4的IP;如果是访问localhost,IP则会显示0:0:0:0:0:0:0:1

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  2. 上游服务器tomcat部署java程序

    将写了获取用户请求IP地址的java程序打成jar包,直接传到tomcat的root目录,直接用java -jar命令启动,这貌似不是用linux上的tomcat运行的,有点像用SpringBoot内置的tomcat运行的,因为我windows上的jar包不需要再配置在tomcat中就能运行;

    【访问效果】

    这是在linux上直接访问该服务器对应接口的效果

    IP地址变成192.168.44.1了,浏览器发送请求会随机的选择一个端口号,不需要管端口号

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  3. 使用反向代理服务器代理用户请求原先的代码无法获取到用户的IP地址

    【windows上的IP地址】

    无线网的地址是192.168.31.180【这个是和外网相连的IP地址】,虚拟的网络适配器【VMWare新加的IP地址】是192.168.176.1和192.168.44.1【44.1不是网关,是为了能和虚拟机通信设置的第二个网卡,必须和虚拟机在同一网段。配置虚拟机的时候,vmware默认的网关的ip是44.2。在新版linux有详细介绍】

    外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

    【宿主主机访问虚拟机的原理】

    宿主主机的无线网络IP是180,宿主主机【windows】需要额外的一个网卡才能访问到内部的虚拟机,宿主主机通过虚拟网卡【虚拟网卡就是44.1】才能访问到虚拟机的网卡【这个就是跑java程序的虚拟机】,虚拟机能够读到的是虚拟网卡的IP

    【添加一层反向代理服务器】

    如果在虚拟网卡和java程序所在服务器之间再加一台IP101的反向代理服务器,虚拟机只能获取反向代理服务器的地址,无法获取到真正客户端的IP地址

    【反向代理服务器访问效果】

    并没有获取到真正的ip地址44.1【虚拟机访问才是44.1,公网访问是180】,实际上上游服务器获取的IP地址是反向代理服务器的

    这个ipAddress:null实际上是还没有在反向代理服务器中配置X-Forwarded-For属性,所以读取不到对应的属性值

  4. 使用x-forwarded-for获取用户真实IP

    X-Forwarded-For是一个扩展的Header,并不在HTTP1.1的协议中,可以在反向代理服务器中通过以下配置即客户端发起请求的请求头中的remote_addr属性将客户端IP写到该Header额外的X-Forwarded-For属性中【反向代理服务器中完成】,此时不在java程序中通过默认的请求头remote_addr找到相应的客户端IP地址,因为remote_addr是记录真实物理TCP连接的下游服务器IP地址,是无法篡改和伪造的,通过remote_addr是拿不到客户端的IP地址的,此时需要获取请求头中nginx封装的X-Forwarded-For属性【后端程序获取该属性值并展示在ipAddress:中】来获取nginx反向代理服务器像请求头中封装的客户端IP地址【?但是网关服务器多层中转过程中应该也会涉及到这个问题,但是网关服务器应该有对用户IP独特的一套解决方案和处理,可以理解成网关已经解决了该问题,传递到目标服务器上的地址就是客户端IP】

    proxy_set_header是向请求的请求头中添加属性和属性值【k-v键值对】,$remote_addr是引用接收到的请求的真实TCP物理连接的IP地址,此前对反向代理服务器和上游服务器使用keepalive也用到了向请求头添加或者修改参数的信息proxy_set_header Connection "";,该属性配置在location中

    由此也引发出一个问题,在经过中转代理服务器或者使用postman发送请求能够向请求头编辑X-Forwarded-For属性信息来达到伪造客户端IP地址的效果;实际上是无法实现的,因为经过nginx代理服务器,所有的请求头信息会被nginx重置,nginx会根据真实的 r e m o t e a d d r 来重新为 X − F o r w a r d e d − F o r 属性赋值【?还是有疑问, remote_addr来重新为X-Forwarded-For属性赋值【?还是有疑问, remoteaddr来重新为XForwardedFor属性赋值【?还是有疑问,remote_addr在多层网关服务器中是怎么处理的】,还可能存在系统越来越大,一个nginx反向代理服务器后还可能存在其他的反向代理服务器【小系统一般是不会有两层nginx服务器的,但是可能存在nginx前面有一层lvs的情况,此时只有lvs能获取到物理连接上的 r e m o t e a d d r 完成赋值,但是到了第二层反向代理服务器上, remote_addr完成赋值,但是到了第二层反向代理服务器上, remoteaddr完成赋值,但是到了第二层反向代理服务器上,remote_addr是lvs和反向代理服务器的TCP连接的lvs的IP地址,此时第二层反向代理服务器无法完成对额外属性X-Forwarded-For进行客户端的IP地址赋值】,这些nginx服务器可能还会承担一些逻辑上的工作:鉴权、URL的重写等业务工作

    proxy_set_header X-Forwarded-For $remote_addr;
    

    【配置示例】

    server {
        listen       80;
        server_name  localhost;
    
        location / {
        	
        	#配置示例
        	proxy_http_version 1.1;
        	proxy_set_header Connection "";
        	proxy_set_header X-Forwarded-For $remote_addr;
        	
            proxy_pass http://stickytest;
            
        }
    
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    

    【配置效果】

    此时ipAddress已经变成了客户端的ip地址了,如果java程序还是读取remoteHost或者remoteAddr,仍然会读到反向代理服务器的地址

  5. 两层反向代理服务器获取客户端IP的方式

    简单系统lvs+nginx构成两层;复杂系统可能本身就存在多层nginx的情况;这种情况最大的问题是第二层反向代理服务器无法通过$remote_addr获取到客户端的IP地址来给X-Forwarded-For赋值,对于这种情况一般有两种解决方式【这是两种最简单的方案,还有很多第三方的工具和module也提供相关解决方案】

    以下两种方式都建立在在nginx代理服务器中能够随意操作被接收请求的请求头信息,并没有对配置进行演示,盲猜可以通过配置proxy_set_header x-forwarded-for $x-forwarded-forproxy_set_header x-forwarded-for $x-forwarded-for,$remote_addr对两种情况分别进行配置

    • 第一种方式

      • 在第二层和后续反向代理服务器不再使用$remote_addr来为X-Forwarded-For赋值,而是直接传递上级代理服务器传递过来的X-Forwarded-For属性直到传递到上游服务器
    • 第二种方式

      • 在第二层和后续反向代理服务器中追加前一个反向代理服务器的IP地址形成X-Forwarded-For:客户端ip1,第一层反向代理服务器ip2

      【两层代理服务器结构图】

  6. Http协议中比较有用的请求头信息

    浏览器最原始的请求头报文中的第一行实际上是请求行的内容

    显示出来的头信息中没有设置端口ip等信息,这些信息存在哪儿的,还是一些默认的请求头配置,只是这里没讲

    • x-forwarded-for:是nginx反向代理服务器中手动设置的用以表示用户IP地址的属性
    • host:是请求的目标地址,nginx反向代理服务器会将upstream的名称自动覆盖掉最初的请求头Host信息
    • Pragma:是和缓存相关的配置,表示可以利用浏览器的缓存存储一些请求的结果,主要是一些不会经常变化的静态资源,能极大地提高目标服务器的并发量
    • cache-control:也是和缓存相关的配置
    • upgrade-insecure-requests:Chrome浏览器会默认添加该信息,表示当前请求使用的是Http协议,浏览器也会显示不安全;这个头信息可以看做chrome浏览器告诉服务器端可以升级成https协议
    • User-Agent:能够表明当前系统使用的浏览器的操作内核,系统版本等客户端信息;有这个就可以判断请求来自于PC、手机还是笔记本;是windows系统还是mac;根据这个信息就可以自动响应给用户对应平台的资源如安装包【这个还可以深入研究一下细节,比如如何进行平台判断等等】
    • accept:accept是浏览器能够接受的一些响应数据类型【text/html是MIME的类型的一种,是客户端能够正常展示和处理的数据类型】
    • accept-encoding:gzip意思是服务器端数据在传输的过程中可以将数据进行压缩,打成一个压缩包响应给浏览器,浏览器再将压缩包解压开,用户感知不到这个过程,但是实际数据传输过程很有可能使用了gzip对数据进行压缩,能够提升网络传输数据包的效率【传输文本的时候压缩效率比较高,但是传输图片时效率并不高】
    • accept-language:浏览器能够接受的语言,比如此处是期望语言使用中文

Gzip动态压缩

Gzip动态压缩:所有的请求到服务端返回数据都会进行一次Gzip压缩,就是以下所有配置的方式就是动态压缩,这种压缩方式有一个致命的缺点,所有的文件无法再使用nginx的sendfile高级特性,sendfile针对本地磁盘文件,开启sendfile数据零拷贝,直接发送信号给系统内核,内核直接从磁盘上找到对应的文件,不加载到nginx内存中直接通过网卡驱动将数据传递给用户,sendfile开了因为少了一次内存拷贝,数据响应速度是非常快的,一旦开了Gzip动态压缩功能,sendfile就自动无效了;但是现在大多网站都在用gzip,也在用sendfile,是因为还有一种gzip静态压缩方式

  1. Gzip压缩原理

    Gzip是针对网络数据传输过程进行数据压缩,该功能需要客户端和服务器端都支持gzip,主要的原理是客户端和服务器端在协议请求头上增加一个参数accept-encoding:gzip表明客户端如浏览器支持gzip,服务器端看见请求头中有客户端支持gzip的信息,就会把数据包压缩成压缩包【还是.zip的格式】发给客户端,可以在服务器端设置压缩的等级,压缩等级越高,压缩比就越高,压缩出来的文件也越小,传输效率越高,同时压缩和解压时对CPU资源的消耗也越高,解压的时候速度就会显得慢一些;对于zip解压缩操作来说,电脑性能一般都是过剩的,对客户端没什么压力,但是对于服务器来说,在频繁高并发情况下这种压缩还是比较消耗CPU性能的,一般构建网站都会配置Gzip来稍微压缩一下响应数据

  2. 京东的Gzip压缩示例

    第一个请求jd.com是用户输入域名的请求过程,general中显示301内部重定向到www.jd.com,此时www.jd.com的响应码是200,表示响应成功;在响应头中有Content-Encoding:gzip就表示数据包使用了gzip,Content-length是文档的大小,Server显示的是nginx【京东就使用的nginx】,除了主页用了gzip,在引用的请求中,jquery.js也用的gzip进行压缩;gzip在国内网站是非常流行的一种方式了

    【谷歌的数据压缩格式】

    chrome也是谷歌开发的,内置了对br算法的支持,如果浏览器不支持br算法,浏览器是打不开google返回的数据的,就好比rar无法解压缩zip文件,因为压缩算法和协议就不一样,br算法现在主流浏览器都是支持的;此外还有很多基于Chrome内核在外面套了一层构建的第三方浏览器,实际上使用的还是谷歌浏览器,使用的还是谷歌浏览器的加密算法、压缩算法等,所以br压缩算法在国内基本也是通用的

    谷歌用的是br,br是另外一种压缩算法,这是谷歌自己开发出来的

  3. Gzip相关配置参数

    Gzip的配置也可以写在http,server,location模块下,

    • gzip on;

      开启Gzip压缩功能,默认是关闭的

    • gzip_buffers 16 8k;

      nginx对响应数据压缩的过程中使用多少的内存缓冲,这里配置的是16个大小为8K的内存缓冲块,这个主要根据操作系统和CPU是32位还是64位进行配置,32位操作系统和CPU建议配置成32个4k的内存缓冲区,64位建议配置成16个8k的内存缓冲区

    • gzip_comp_level 6;

      选择gzip压缩算法的压缩等级,数字1-9都可以配置,1基本上就是不压缩,压缩等级越低,压缩解压速度就越快,压缩比也越小;同一个压缩等级对不同的文件类型压缩出来的压缩比也是不一样的,压缩比高的情况1M可能压成200KB,但是对CPU的消耗比较高,一般都配置成1-6,别搞得太高,CPU受不了

    • gzip_http_version 1.1;

      配置gzip压缩能够支持的最低的HTTP协议版本号,现在主流浏览器上都默认支持1.1版本以上的Http协议,也支持2.0版本,少部分的实验版本浏览器支持3.0的HTTP协议,可配可不配,因为基本上浏览器默认就是1.1以上的协议版本

    • gzip_min_length 256;

      设置是否对文件进行压缩的文件大小阈值,小于256K的文件不进行压缩,直接传输;大于256K的文件进行压缩;一般配置成1-5K都可以,不能配置的特别小

    • gzip_proxied any;

      这个配置官方文档和网上的文章都解释的不清楚

      这个是nginx作为反向代理服务器的时候可以根据上游服务器响应的头信息配置一些额外的压缩条件【只有nginx作为反向代理服务器该配置才生效,制作静态文件存储时这个选项是没有用的,配置了也没有效果】,off和any两种配置比较常见,off相当于不做任何配置,相当于该选项被关闭;any表示不根据上游服务器的头信息进行判断,所有请求满足其他配置条件的全部被压缩【没有说,说的是无条件启用压缩,这里我是猜的】;any和off的效果是一样的【不一样,从中文官方文档上显示的是off是关闭所有的代理结果数据的压缩,但是官网的默认值就是off,那岂不是只对nginx上的静态资源进行压缩,这里需要留意一下到底是什么意思,暂时就用any】;

      其他的配置参数针对的是上游服务器响应的头信息中带有缓存相关的header【一般来说被cache的数据都是准备缓存在浏览器上的,一般来说需要启动cache的数据都已经被压缩过了,不需要再进行压缩了】或者是权限相关的Header来决定是否对上游服务器响应的数据启用压缩

      完整配置参数:

      • off - 为不做限制【有两条弹幕说官方文档上该选项是禁止使用压缩,经查证,官方中文文档上确实说的是关闭所有的代理结果数据的压缩,但是官网的默认值就是off,那岂不是只对nginx上的静态资源进行压缩,这里需要留意一下到底是什么意思】
      • 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_vary on;

      一般来说不需要配置,一般是配置给比较老的浏览器使用的,配置上无所谓,只是多出来一个头信息Vary:Accept-Encoding而已

    • gzip_types text/plain application/x-javascript text/css application/xml;

      两个gzip_types任意选一个,针对项目情况选择使用,后面的参数值用空格分隔开,意思是针对哪些mime类型的文档做gzip压缩;text/plain是一般的文本文档、application/x-javascript是针对javascript相关的、text/css针对CSS相关的、application/xml针对xml相关的

    • gzip_types
      text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
      text/javascript application/javascript application/x-javascript
      text/x-json application/json application/x-web-app-manifest+json
      text/css text/plain text/x-component
      font/opentype application/x-font-ttf application/vnd.ms-fontobject
      image/x-icon;

      这是比较全的配置了,第一行是和文本文档和xml相关的,第二行是和JavaScript相关的,第三行是和json相关的,第四行时css以及文本文档相关的,第五行是对字体相关的【服务器可能会向浏览器下发一些字体来展示响应页面的效果】,第六行是对图标做压缩【视频特指网站页面左侧的小图标】

      建议是针对自己的系统冗余配置,少配置会导致有些文件不进行压缩了

    • gzip_disable “MSIE [1-6].(?!.*SV1)”;

      这个参数表示针对哪些浏览器关闭gzip这个功能,这个也是默认配置,针对IE6版本以下的浏览器就关闭Gzip功能,建议这个参数不要配置,因为每个请求差不多都会检验gzip,而这个参数值是正则表达式,都去匹配该正则表达式对服务器的性能消耗是指数级别上升的,正则写的越复杂,性能消耗就越大【包括location中的其他配置,净量不要出现正则表达式,直接最小匹配直接匹配上,千万尽量少用正则,很影响性能】

  4. 配置Gzip压缩

    【未开启Gzip情况下的效果】

    gzip能在请求报文的ResponseHeader信息中体现出来,未开启gzip情况下的请求头信息

    Content-Length表示当前页面一共有多大,这里是7832个字节

    【nginx中配置gzip】

    server {
        listen       80;
        server_name  localhost;
    
        location / {
        	gzip on;
            gzip_buffers 16 8k;
            gzip_comp_level 6;
            gzip_http_version 1.1;
            gzip_min_length 256;
            gzip_proxied any;
            gzip_vary on;
            gzip_types text/plain application/x-javascript text/css application/xml;
            gzip_types
                text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
                text/javascript application/javascript application/x-javascript
                text/x-json application/json application/x-web-app-manifest+json
                text/css text/plain text/x-component
                font/opentype application/x-font-ttf application/vnd.ms-fontobject
                image/x-icon;
            gzip_disable "MSIE [1-6]\.(?!.*SV1)";
        	
        	#配置示例
        	proxy_http_version 1.1;
        	proxy_set_header Connection "";
        	proxy_set_header X-Forwarded-For $remote_addr;
        	
            proxy_pass http://stickytest;
            
        }
    
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root   html;
        }
    }
    

    【配置gzip后的访问效果】

    多出来三个响应头信息,

    Content-Encoding:gzip:当前页面内容压缩的格式是gzip

    Transfer-Encoding:chunked:传输的压缩格式,chunked表示将数据包以一个一个的包的形式发送给客户端,每个包发送过来单独的解压缩,直到最后一个大小为0的包表示数据传输完成;这个参数就是替代之前数据大小的作用,没有数据大小信息的情况下需要判断数据传输完成的标志,该参数由nginx生成响应头时写入,但是nginx生成响应头的同时响应数据还没有准备好【异步响应式操作】,此时还不知道压缩数据到底有多大,先把头信息准备好以后再去请求上游服务器读数据,最后将头信息和响应体合并一起发给客户端【有些文件比较大压缩比较慢,先把头信息发送给nginx先进行处理】

    Vary:Accept-Encoding:该参数由配置gzip_vary on;添加的头信息,一般来说不需要配置,一般是配置给比较老的浏览器使用的,配置上无所谓,只是多出来一个头信息而已

    少了原来的内容大小信息

    【chrome浏览器的一些参数】

    Accepted Content:可接受的内容压缩格式,取消浏览器默认设置可以选择不支持下列三种内容压缩算法【deflate压缩算法最早出现在apache httpd上,现在使用nginx基本上看不到这种压缩算法,默认是三种都支持】,不支持某种压缩算法,但是服务器上没有原件,服务器会直接返回404

    Network throtting:该选项卡可以模拟不同的网络情况,可以选择3G网络【访问请求响应到页面上的速度会明显变慢】,还可以直接选择offline让浏览器离线

Gzip静态压缩

Gzip静态压缩可以看做动态压缩的一个扩展功能,Gzip静态压缩就完美解决了动态压缩无法使用sendfile功能的问题,原理是预先将静态文件打包成一个压缩包,动态压缩指去磁盘上找一个文件,文件找到后用Gzip压缩,压缩完以后发送给客户端;静态压缩指磁盘上有一个文件,该文件还配置了一个对应的压缩包【如index.html,此外还有一个index.html.gz,这是提前用gzip方式压缩好的压缩包】,Gzip静态压缩开启后会优先去读取gz文件,读取到直接就用sendfile的方式发送给客户端,而不再进行压缩的操作【理解成不压缩就可以使用sendfile】,压缩文件就是linux上的哪个压缩命令,还可以使用命令gzip -9 example.txt设置对应的压缩等级为9

因为sendfile针对的是nginx上的资源,一般nginx都作为静态资源前置服务器,上面一般都是静态资源,静态压缩功能是在动态压缩功能基础上开启的【一般这种权限都掌控在架构师和技术经理手上,有天全权接手项目就能考虑这个事情了】

官方文档上Gzip相关的模块:ngx_http_gzip_module:动态Gzip压缩模块;ngx_http_gzip_static_module:Gzip静态压缩模块;静态压缩模块默认情况下没在预编译的包中,需要手动添加--with-http_gzip_static_module参数到./configure命令下

  1. 为反向代理服务器131安装静态Gzip压缩模块

    • 使用命令./configure --prefix=/usr/local/nginx/ --add-module=/opt/nginx/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d --with-http_gzip_static_module在nginx的解压目录下进行预编译配置

      经过测试没问题,内置模块用with,外置模块用add

    • 没有报错使用make命令进行安装

      不要用make install,make install执行原来的nginx会被重置

    • 停止nginx的运行,使用命令mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old2 ,使用命令cp nginx /usr/local/nginx/sbin/objs/nginx.sh复制到nginx的sbin目录

  2. Gzip静态压缩的参数配置

    • gzip_static on|off|always,作用域还是httpserverlocation

      配置成on会去检查客户端是否支持gzip,如果客户端不支持gzip,就不会去给客户端发送gzip这个压缩包了,用户支持gzip就发送gzip文件,不支持就去找原文件,如果找不到原件就会直接报404;off相当于直接把gzip这个模块给直接关闭了;always是不管客户端是否支持gzip,都将gzip压缩包发送给客户端【此时如果客户端不支持gzip,压缩包就解不开,此时还需要配合另外一个模块ngx_http_gunzip_moudle来进行使用,该模块也不在预编译的包下,需要手动添加--with-http_gunzip_moudle参数到./configure命令下【./configure --prefix=/usr/local/nginx/ --add-module=/opt/nginx/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d --with-http_gzip_static_module --with-http_gunzip_module】,该模块的作用是发送数据给客户端前将压缩包给解压开,有这个就可以把磁盘上的所有原文件删除,只保留压缩文件,任何时候都不需要专门去访问原文件】

      Gzip静态压缩模块和gunzip模块配合可以实现当用户可以使用gzip的方式就直接访问压缩包,如果用户不支持gzip就在数据响应给用户前在nginx中奖压缩包解压缩为原文件再响应给客户端;大多数情况下不需要这个配置,基本上浏览器都支持gzip,gunzip模块大多数情况下都是用来节省磁盘空间的,注意gzip_static on在使用gunzip的时候一般是没有原文件的,对于不支持gzip的客户端,使用gzip_static on会导致nginx去找原文件,找不到直接报404;不会再去用gunzip模块解压得到原文件并响应给用户,需要将该参数配置成gzip_static always,找不找的到原件都必须给客户端发送压缩包,发送压缩包发现用户不支持解gzip解压缩就在服务器使用gunzip解压缩,而且此时响应头中也不会有gzip的信息,显示内容是text/html类型,但是传输编码还是显示的chunked,没有显示响应内容的大小【原因还是磁盘上本身没有对应的原文件,需要解压缩,没有对应的原文件大小信息】

    • 总体的配置演示

      开启动态压缩和静态压缩的配置

      worker_processes  1;
      
      events {
          worker_connections  1024;
      }
      
      
      http {
          include       mime.types;
          default_type  application/octet-stream;
          sendfile        on;
          keepalive_timeout 65 65;
          keepalive_time 1h;
          keepalive_requests 1000;
          send_timeout 60;
          upstream stickytest{
              keepalive 100;
              keepalive_requests 1000;
              keepalive_timeout 65;
              server 192.168.200.135:8080;
          }
      	
      	server {
              listen       80;
              server_name  localhost;
      
              location / {
              	gunzip on;
              	gzip_static on;
                  gzip on;
                  gzip_buffers 16 8k;
                  gzip_comp_level 6;
                  gzip_http_version 1.1;
                  gzip_min_length 256;
                  gzip_proxied any;
                  gzip_vary on;
                  gzip_types text/plain application/x-javascript text/css application/xml;
                  gzip_types
                      text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
                      text/javascript application/javascript application/x-javascript
                      text/x-json application/json application/x-web-app-manifest+json
                      text/css text/plain text/x-component
                      font/opentype application/x-font-ttf application/vnd.ms-fontobject
                      image/x-icon;
                  gzip_disable "MSIE [1-6]\.(?!.*SV1)";
      
                  #配置示例
                  proxy_http_version 1.1;
                  proxy_set_header Connection "";
                  proxy_set_header X-Forwarded-For $remote_addr;
      
                  proxy_pass http://stickytest;
      
              }
      
              error_page   500 502 503 504  /50x.html;
              location = /50x.html {
                  root   html;
              }
      	}   
      }
      
  3. gunzip模块的参数配置

作用域同样httpserverlocation

  • gunzip on|off:开启或者关闭gunzip模块的功能
  • gunzip_buffers number size:内存缓冲区大小,number是内存缓冲块个数,size是每个内存缓冲块大小;默认值32位操作系统和CPU默认配置成32个4k的内存缓冲区,64位默认配置成16个8k的内存缓冲区
  1. 静态压缩的使用场景

图片和音视频不适合使用gzip进行压缩,现在看到的视频在传输过程已经经过一层压缩了,现在使用的最多的格式是h264的格式,服务器对视频的处理会先压缩一遍,再次进行压缩处理效果极低,除非提高很高的压缩等级,但是文件又大,压缩和解压过程中CPU的消耗都非常高

图片和音频资源也是经过提前压缩处理的,png、gif、jpg都是已经被压缩过的格式

总结图片、音视频资源本身的格式都是已经被压缩过的格式,再去压缩得到的压缩比不会太高,要把压缩比弄的很高代价特别高昂;比较适合实用gzip的就是访问频次很高的文本文件【html,css,js等】

响应头信息中有gzip也有内容大小信息【Content-length,但是之前有静态压缩还是chunked不知道这个内容长度是怎么实现的】说明访问站点使用了静态压缩的方式,资源文件都是经过预压缩的,传输快,节省资源

  • nginx服务器做为cdn服务器的上游服务器【数据存储服务器】,把数据文件全部压缩【使用linux上的压缩命令gzip -r my_directory可以递归压缩整个目录的文件】
  • 极为高频被访问到的页面或者一些CSS、JS静态资源文件可以提前压缩使用静态压缩的方式,

第三方Brotli压缩

Brotli是google搞出来的,其性能远高于gzip,也有大量站点使用这种压缩方式,但是使用这种方式必须在https协议下,默认情况下浏览器没有accept-encoding:br这个头信息,该压缩算法效率高是因为内置了大概一万多个字典文件,这些字典文件包含的都是常见的html、css、js文件文本,比如对标签进行归类,生成序号字典文件代替这些标签,就可以用序号代替标签进行压缩和解压缩,尤其对文本文档的压缩效率极高,Brotli的压缩效率比Gzip大概高20%左右

这个Brotli第三方压缩模块和Gzip可以在nginx中共存,浏览器支持Brotli的话会优先使用brotli,浏览器不支持就会降级使用gzip

Brotli官网:https://github.com/google/ngx_brotli

安装Brotli
安装步骤

安装ngx_brotli和brotli稍微麻烦一点,因为有子项目依赖

  1. 从下载地址https://github.com/google/ngx_brotli下载broltinginx上的插件ngx_brotli

    ngx_brotli下载地址有文档,配置使用官方推荐的动态模块的方式,动态加载模块是nginx在1.9版本以后才支持的,禁用模块无需重新进行编译,直接在配置文件中进行配置要使用的模块就可以直接使用了【就是安装是安装,需要使用要在配置文件进行配置,不配置对应的模块就会禁用】

    ngx_brotli下载点击release下载,只能下载到源码,需要自己在本地进行编译和安装;或者从git直接拉取到本地进行编译和安装;稳定版本现在只有1.0.0,linux系统下载tar.gz版本

  2. 从下载地址https://codeload.github.com/google/brotli/tar.gz/refs/tags/v1.0.9下载brotli单独的算法

    仓库地址:https://github.com/google/brotli,也可以直接从对应仓库的release下载tar.gz的源码压缩包

  3. 使用命令tar -zxvf ngx_brotli-1.0.0rc.tar.gz 解压ngx_brotli的压缩包

  4. 使用命令tar -zxvf brotli-1.0.9.tar.gz解压brolti的压缩包

  5. 进入brolti的解压目录,使用命令mv ./* /opt/nginx/ngx_brotli-1.0.0rc/deps/brotli将brotli算法的压缩包移动到ngx_brotli解压目录下的ngx_brotli-1.0.0rc/deps/brolti目录【注意不要把解压目录brotli-1.0.9也拷贝过去了,只拷贝该目录下的文件,目录层级不对预编译检查会报错】

  6. 在nginx的解压目录使用命令./configure --with-compat --add-dynamic-module=/opt/nginx/ngx_brotli-1.0.0rc --prefix=/usr/local/nginx/ --add-module=/opt/nginx/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d --with-http_gzip_static_module --with-http_gunzip_module 以动态化模块的方式【传统的方式模块无法禁用模块】对ngx_brolti进行编译,传统的编译可以在./configure中使用--add-dynamic-module=brotli目录

  7. 在nginx的解压目录使用命令make进行编译

    没报错就是成功的,上述步骤经测试没问题

  8. 使用命令mkdir /usr/local/nginx/modules在nginx安装目录下创建一个modules模块专门来放置第三方模块,默认是没有的

  9. nginx解压目录进入objs目录,使用命令cp ngx_http_brotli_filter_module.so /usr/local/nginx/modules/ 以及cp ngx_http_brotli_static_module.so /usr/local/nginx/modules/将模块ngx_http_brotli_filter_module.songx_http_brotli_static_module.so拷贝到nginx安装目录的modules目录下,以后就可以在nginx配置文件动态的加载brotli模块了

  10. 停止nginx的运行,使用命令mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old3 ,使用命令cp nginx /usr/local/nginx/sbin/objs/nginx.sh复制到nginx的sbin目录

    只要进行了编译就要将新的主程序nginx拷贝到nginx的安装目录,否则即使之前的步骤都没问题模块也是用不了的

    每次预编译都要带上之前添加的完整的模块,否则可能由于对某些模块有配置但是没有安装相应的模块,会导致服务启动不起来

    可以在拷贝完成后再重启nginx服务

  11. nginx.conf配置文件中的最外层【与worker_processes同一层】添加以下配置开启Brolti的相关模块

    load_module "/usr/local/nginx/modules/ngx_http_brotli_filter_module.so";
    load_module "/usr/local/nginx/modules/ngx_http_brotli_static_module.so";
    
配置Brolti

Brolti的配置和Gzip很像,

  1. 配置参数详解

    • brotli_static

      • 参数值: brotli_static on|off|always
      • 默认值: off
      • 作用域: http, server, location

      静态压缩功能配置

      属性值为on,会优先去查找以br为后缀的文件,如果找不到br为后缀的文件就去查找原文件;

      属性值为always,压根就不读取原文件,和gzip的静态压缩是一样的,不支持也会返回压缩包【那自动降级gzip和这个冲突吗】;

      属性值为off,表示不使用brotli进行静态压缩

    • brotli

      • 参数值: brotli on|off
      • 默认值: off
      • 作用域: http, server, location, if

      是否使用brolti对响应文件进行动态压缩

    • brotli_types

      • 参数值: brotli_types <mime_type> [..]
      • 默认值: text/html
      • 作用域: http, server, location

      指定需要brolti压缩的MIME类型,*表示匹配任意响应的MIME类型,文本类型压缩效果特别好,对图片、音视频的压缩效果没什么作用【压缩效果好是因为brolti有强大的内置字典】

    • brotli_buffers

      • 参数值: brotli_buffers <number> <size>
      • 默认值: 32 4k|16 8k
      • 作用域: http, server, location

      进行压缩的内存缓冲区,和gzip是一样的,默认32位系统是32个4k内存缓冲块,64位系统是16个8k内存缓冲块,但是官方文档显示已经过时可忽略Deprecated, ignored.

    • brotli_comp_level

      • 参数值: brotli_comp_level <level>
      • 默认值: 6
      • 作用域: http, server, location

      压缩等级,默认第6级,和gzip不一样的是一共有11级【1-11】

    • brotli_window

      • 参数值: brotli_window <size>
      • 默认值: 512k
      • 作用域: http, server, location

      窗口值是压缩软件中通用的一个概念,相当于用来摆放数据的桌子,桌子越大,相同的标志性数据查找效率越高,也不是设置的越大越好,因为比较占用内存,默认情况下是512k,可以以2的倍数进行设置

    • brotli_min_length

      • 参数值: brotli_min_length <length>
      • 默认值: 20
      • 作用域: http, server, location

      设置响应会被压缩的最小长度,响应体的长度取响应头中的Content-Length属性值

  2. Brotli和Gzip完整配置演示

    Gzip和Brolti是完全可以共存的,两者的配置完全没有冲突

    注意Chrome浏览器默认情况下没有开启对br压缩格式的支持的【http协议没有对br相应的头信息的支持,可以使用curl自定义头信息发送http请求来使用br】,只有在https下才支持br【需要https服务器客户端才能发送https的URL】

    课堂演示表明浏览器不支持br格式会自动使用gzip来响应请求

    #只有使用新编译完的nginx主程序才能支持加载这些模块,否则也是不认识的
    load_module "/usr/local/nginx/modules/ngx_http_brotli_filter_module.so";
    load_module "/usr/local/nginx/modules/ngx_http_brotli_static_module.so";
    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout 65 65;
        keepalive_time 1h;
        keepalive_requests 1000;
        send_timeout 60;
        upstream stickytest{
            keepalive 100;
            keepalive_requests 1000;
            keepalive_timeout 65;
            server 192.168.200.135:8080;
        }
    	
    	server {
            listen       80;
            server_name  localhost;
    
            location / {
            	#gzip和gunzip配置
            	gunzip on;
            	gzip_static on;
                gzip on;
                gzip_buffers 16 8k;
                gzip_comp_level 6;
                gzip_http_version 1.1;
                gzip_min_length 256;
                gzip_proxied any;
                gzip_vary on;
                gzip_types text/plain application/x-javascript text/css application/xml;
                gzip_types
                    text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
                    text/javascript application/javascript application/x-javascript
                    text/x-json application/json application/x-web-app-manifest+json
                    text/css text/plain text/x-component
                    font/opentype application/x-font-ttf application/vnd.ms-fontobject
                    image/x-icon;
                gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    
    			#Brolti配置
    			brotli on;
                brotli_static on;
                brotli_comp_level 6;
                brotli_buffers 16 8k;
                brotli_min_length 20;
                brotli_types text/plain text/css text/javascript application/javascript text/xml application/xml application/xml+rss application/json image/jpeg image/gif image/png;
    			
                #配置示例
                proxy_http_version 1.1;
                proxy_set_header Connection "";
                proxy_set_header X-Forwarded-For $remote_addr;
    
                proxy_pass http://stickytest;
    
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
    	}   
    }
    

    【效果演示】

    浏览器使用http协议不能添加支持br的头信息,必须https协议才能使用支持br的头信息,这里为了方便,直接使用命令curl -H 'accept-encoding:br' -I http://192.168.200.131自定义头信息为br来让服务器自动使用br的压缩方式来压缩传输数据【感觉课上说的启用静态压缩会自动停用动态压缩的知识是错误的,这里配置文件开启了静态压缩功能,客户端根本没有br为后缀的文件,返回的响应头中还是br,感觉理解成静态压缩和动态压缩同时开启,浏览器不支持br就自动使用gzip;不确定这儿,因为开启了gzip的动态压缩,浏览器也支持gzip,但是返回的是上游服务器的原件,可能是没到压缩阈值,测试了一下确实是没到阈值,爷屌不屌;这里没问题,就按之前的理解,遇到了再修正】

合并请求

请求一个主页,会因为引用发情N多个请求,请求类型分为文本类:js、css;多媒体类型:png;

可以使用Concat模块把客户端发起的多个CSS资源请求、js资源请求合并成一个请求,目的是为了减少并发请求,让服务器可以承载更高的并发量

原理是前端发起请求的连接不能像以前一样每个文本资源文件都写一个herf,而要采用类似于定义接口形式,将要请求的静态资源文本文件的对应标志以参数的形式拼接在uri的后面发送给服务器,服务器解析请求的参数,读取对应的资源,并将这些资源合并之后以一个文件的形式响应给客户端【这里目前演示的是同一种后缀的文件合并在一起,衍生出一个问题,为什么不把这些相同类型的文件如css全部写成一个文件一次请求一次响应呢?给出的解释是在研发阶段如果只写成一个css文件会特别地麻烦,情况变得错综复杂,因此选择牺牲一些性能,在响应的时候将这些css等文本文件使用合并操作成一个文件响应给客户端,弹幕补充:主要原因是你不拆分文件,那就无法实现按需引入,所以每次请求的css都会有冗余的,没有使用的信息,单个文件太大就会影响响应速度,页面渲染,所以就要拆分在这个过程中内存中会存在这些文件的内容,即内核态到用户态的复制,这种情况下sendfile也是无法使用的】,消耗计算性能减少并发请求数量;很多的大型网站都在使用这个技术,且Concat模块就是淘宝研发出来的

【请求合并示意图】

  1. 淘宝的请求合并实例

    这也不是减少高并发请求的唯一方式,只是一种,还可以利用浏览器缓存来减少并发请求的数量,以后会讲

    css和js文件的请求都能合并,凡是淘宝页面有两个问号的开头的css和js请求都走了concat模块【这是Concat的规范】

    做架构师最初就是从现有成功的项目中学习人家是怎么做的,因为别人有这么大的并发量,有试验场景;不知名项目可能根本就没有经过如此大高并发请求的访问【亿级流量也就几大家真正经历过】

安装Concat模块

官网地址:https://github.com/alibaba/nginx-http-concat

nginx官网对Concat的介绍:https://www.nginx.com/resources/wiki/modules/concat/

Tengine的官网也有介绍,该模块最早是tengine发布的,tengine已经被捐献给apache开源组织,tengine是基于nginx开源版基础上再源码层面做了很多的修改,不只是增加了模块和功能,大部分的tengine的模块直接拿到nginx中是用不了的,但是Concat模块是能直接拿过来使用的,Concat虽然很久没用了,但是比较简单,就一个C源文件,而且release中没有发行版,只能通过git clone进行安装或者点击code通过download zip将压缩包nginx-http-concat-master.zip下载下来

安装步骤
  1. 将压缩包nginx-http-concat-master.zip拷贝到/opt/nginx目录下

    这里有个坑,大家用git指令下载的文件是和直接下载的文件是不一样的,git下载的文件名没有-master

    [root@nginx1 nginx]# unzip nginx-http-concat-master.zip 
    Archive:  nginx-http-concat-master.zip
    b8d3e7ec511724a6900ba3915df6b504337891a9
       creating: nginx-http-concat-master/
      inflating: nginx-http-concat-master/README.md  
      inflating: nginx-http-concat-master/config  
      inflating: nginx-http-concat-master/ngx_http_concat_module.c  
    
  2. 在nginx的解压目录中使用命令./configure --with-compat --add-dynamic-module=/opt/nginx/ngx_brotli-1.0.0rc --prefix=/usr/local/nginx/ --add-module=/opt/nginx/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d --with-http_gzip_static_module --with-http_gunzip_module --add-module=/opt/nginx/nginx-http-concat-master进行预编译,然后使用make命令编译

    这里面为了nginx的课程连续性,添加了很多其他模块,自己根据需要选择,预编译检查的命令不是固定的,对nginx.sh使用命令./nginx -V能够查看当前nginx的版本,gcc版本和安装的模块

    [root@nginx1 objs]# ./nginx -V
    nginx version: nginx/1.20.2
    built by gcc 8.3.1 20190311 (Red Hat 8.3.1-3) (GCC) 
    configure arguments: --with-compat --add-dynamic-module=/opt/nginx/ngx_brotli-1.0.0rc --prefix=/usr/local/nginx/ --add-module=/opt/nginx/nginx-goodies-nginx-sticky-module-ng-c78b7dd79d0d --with-http_gzip_static_module --with-http_gunzip_module --add-module=/opt/nginx/nginx-http-concat-master
    
  3. 使用命令systemctl stop nginx停掉nginx,在/objs目录下使用命令cp nginx /usr/local/nginx/sbin/将编译好的nginx运行文件替换掉老的nginx.sh,然后重启nginx

    一般来说各个模块包括第三方模块之间不会有冲突性问题

Concat合并文本请求

Concat只能合并文本请求

图片请求也能合并,但是是前端的技术,作为开发需要对这些技术进行了解,比如当前页面有N多图片,图片比较小,以icon【以按钮为代表的简单图表实例】为后缀;还可以把几张图合并成一张大图,在前端展示时使用css样式中的position属性来控制图片展示的范围

【图表合并图】

用css样式中的position属性来切割图片,这种图片合并使用与logo和icon这种,处理高并发较为取巧也常用的方式

外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传

  1. Concat配置

    Concat只能合并同一台服务器上的css、js文件,如果文件分布在不同的服务器上,concat无法合并这些文件

    location / {
        concat on; #开启concat模块的使用
        concat_max_files 20; #配置一个请求最多能包含多少个文件,最好不要在一个请求中包含太多的文件
    }
    

    【完整配置】

    #只有使用新编译完的nginx主程序才能支持加载这些模块,否则也是不认识的
    load_module "/usr/local/nginx/modules/ngx_http_brotli_filter_module.so";
    load_module "/usr/local/nginx/modules/ngx_http_brotli_static_module.so";
    worker_processes  1;
    
    events {
        worker_connections  1024;
    }
    
    
    http {
        include       mime.types;
        default_type  application/octet-stream;
        sendfile        on;
        keepalive_timeout 65 65;
        keepalive_time 1h;
        keepalive_requests 1000;
        send_timeout 60;
        upstream stickytest{
            keepalive 100;
            keepalive_requests 1000;
            keepalive_timeout 65;
            server 192.168.200.135:8080;
        }
    	
    	server {
            listen       80;
            server_name  localhost;
    
            location / {
            	#concat的配置
            	concat on; #开启concat模块的使用
         		concat_max_files 20; #配置一个请求最多能包含多少个文件,最好不要在一个请求中包含太多的文件
            	#gzip和gunzip配置
            	gunzip on;
            	gzip_static on;
                gzip on;
                gzip_buffers 16 8k;
                gzip_comp_level 6;
                gzip_http_version 1.1;
                gzip_min_length 256;
                gzip_proxied any;
                gzip_vary on;
                gzip_types text/plain application/x-javascript text/css application/xml;
                gzip_types
                    text/xml application/xml application/atom+xml application/rss+xml application/xhtml+xml image/svg+xml
                    text/javascript application/javascript application/x-javascript
                    text/x-json application/json application/x-web-app-manifest+json
                    text/css text/plain text/x-component
                    font/opentype application/x-font-ttf application/vnd.ms-fontobject
                    image/x-icon;
                gzip_disable "MSIE [1-6]\.(?!.*SV1)";
    
    			#Brolti配置
    			brotli on;
                brotli_static on;
                brotli_comp_level 6;
                brotli_buffers 16 8k;
                brotli_min_length 20;
                brotli_types text/plain text/css text/javascript application/javascript text/xml application/xml application/xml+rss application/json image/jpeg image/gif image/png;
    			
                #配置示例
                proxy_http_version 1.1;
                proxy_set_header Connection "";
                proxy_set_header X-Forwarded-For $remote_addr;
    
                proxy_pass http://stickytest;
    
            }
    
            error_page   500 502 503 504  /50x.html;
            location = /50x.html {
                root   html;
            }
    	}   
    }
    
  2. 配置上游服务器tomcat的欢迎页

    【给页面添加样式】

    h最多到h6生效 ,把css文件的背景和字体颜色拆分两个css文件,在主文件中对两个css文件进行引用

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title>tomcat</title>
    	</head>
    	<body>
            <h1>welcome to tomcat!I'm 192.168.200.135</h1>
    		<h2>welcome to tomcat!I'm 192.168.200.135</h2>
    		<h3>welcome to tomcat!I'm 192.168.200.135</h3>
    		<h4>welcome to tomcat!I'm 192.168.200.135</h4>
    		<h5>welcome to tomcat!I'm 192.168.200.135</h5>
    		<h6>welcome to tomcat!I'm 192.168.200.135</h6>
    		<h7>welcome to tomcat!I'm 192.168.200.135</h7>
    		<h8>welcome to tomcat!I'm 192.168.200.135</h8>
    		<h9>welcome to tomcat!I'm 192.168.200.135</h9>
    		<h10>welcome to tomcat!I'm 192.168.200.135</h10>
    		<h11>welcome to tomcat!I'm 192.168.200.135</h11>
    		<h12>welcome to tomcat!I'm 192.168.200.135</h12>
    		<h13>welcome to tomcat!I'm 192.168.200.135</h13>
    		<h14>welcome to tomcat!I'm 192.168.200.135</h14>
    		<h15>welcome to tomcat!I'm 192.168.200.135</h15>
    		<h16>welcome to tomcat!I'm 192.168.200.135</h16>
    	</body>
    </html>
    <style>
    	body{
    		background-color:#000;
    		color:#fff;
    	}	
    </style>
    

    【样式效果】

  3. 对css样式分别拆成两个文件并在欢迎页中进行引用

    【欢迎页】

    css文件的根目录就是欢迎页所在的root目录

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title>tomcat</title>
            <!-- 这个link不一定必须要加在标签内,直接找个标签外的地方写都行,但是外部css样式文件必须要通过link引入 -->
            <link rel="stylesheet" type="text/css" href="font.css"/>
            <link rel="stylesheet" type="text/css" href="page.css"/>
    	</head>
    	<body>
            <h1>welcome to tomcat!I'm 192.168.200.135</h1>
    		<h2>welcome to tomcat!I'm 192.168.200.135</h2>
    		<h3>welcome to tomcat!I'm 192.168.200.135</h3>
    		<h4>welcome to tomcat!I'm 192.168.200.135</h4>
    		<h5>welcome to tomcat!I'm 192.168.200.135</h5>
    		<h6>welcome to tomcat!I'm 192.168.200.135</h6>
    		<h7>welcome to tomcat!I'm 192.168.200.135</h7>
    		<h8>welcome to tomcat!I'm 192.168.200.135</h8>
    		<h9>welcome to tomcat!I'm 192.168.200.135</h9>
    		<h10>welcome to tomcat!I'm 192.168.200.135</h10>
    		<h11>welcome to tomcat!I'm 192.168.200.135</h11>
    		<h12>welcome to tomcat!I'm 192.168.200.135</h12>
    		<h13>welcome to tomcat!I'm 192.168.200.135</h13>
    		<h14>welcome to tomcat!I'm 192.168.200.135</h14>
    		<h15>welcome to tomcat!I'm 192.168.200.135</h15>
    		<h16>welcome to tomcat!I'm 192.168.200.135</h16>
    	</body>
    </html>
    

    【font.css】

    tomcat的webapps下的root目录

    body{
        color:#fff;
    }	
    

    【page.css】

    body{
        background-color:#000;
    }	
    

    【请求效果】

    只有清空缓存进行请求的时候才会出现content-encoding:gzip,浏览器有缓存不会显示content-encoding属性

  4. 使用comcat模块,在主页修改href让一个请求获取多个css文件

    【主页】

    此时服务器端的css文件没有任何变动

    <!DOCTYPE html>
    <html>
    	<head>
    		<meta charset="utf-8">
    		<title>tomcat</title>
            <!-- 这个link不一定必须要加在标签内,直接找个标签外的地方写都行,但是外部css样式文件必须要通过link引入,不需要type属性都行,但是不能没有rel属性 -->
            <!-- 直接在href写对应的请求文件格式就能获取到对应的文件,不知道合并的具体过程,但是返回的应该是同一个文件,只是在服务端多个文件被合并了,浏览器也能解析返回的这个文件 -->
            <link rel="stylesheet" type="text/css" href="??font.css,page.css"/>
    	</head>
    	<body>
            <h1>welcome to tomcat!I'm 192.168.200.135</h1>
    		<h2>welcome to tomcat!I'm 192.168.200.135</h2>
    		<h3>welcome to tomcat!I'm 192.168.200.135</h3>
    		<h4>welcome to tomcat!I'm 192.168.200.135</h4>
    		<h5>welcome to tomcat!I'm 192.168.200.135</h5>
    		<h6>welcome to tomcat!I'm 192.168.200.135</h6>
    		<h7>welcome to tomcat!I'm 192.168.200.135</h7>
    		<h8>welcome to tomcat!I'm 192.168.200.135</h8>
    		<h9>welcome to tomcat!I'm 192.168.200.135</h9>
    		<h10>welcome to tomcat!I'm 192.168.200.135</h10>
    		<h11>welcome to tomcat!I'm 192.168.200.135</h11>
    		<h12>welcome to tomcat!I'm 192.168.200.135</h12>
    		<h13>welcome to tomcat!I'm 192.168.200.135</h13>
    		<h14>welcome to tomcat!I'm 192.168.200.135</h14>
    		<h15>welcome to tomcat!I'm 192.168.200.135</h15>
    		<h16>welcome to tomcat!I'm 192.168.200.135</h16>
    	</body>
    </html>
    

    【访问效果】

    注意:如果合并请求的css文件不在nginx服务器上,结果响应回来是错误的,并不会实现css的样式效果

    请求合并的css文件必须在装了concat模块的nginx服务器上

    【合并文件效果】

    实际上css文件在tomcat上并没有实现样式效果

  5. 将项目迁移到nginx服务器上的试验效果

    此时整个项目都在131nginx服务器上,css样式效果出现了

    【只要nginx服务器上有合并请求的css文件,即使内容在tomcat上也能正常响应】

    错了走的静态文件的根目录都不一样,即使有css文件也找不到,经过测试,下面这张图是因为走了浏览器缓存的原因,清除缓存再次发送请求就变成没有样式的效果了,所以还是原来的规则,合并请求的css文件、js文件必须在装了concat模块的nginx代理服务器上

资源静态化

资源静态化是处理高并发请求的利器,处理高并发的手段无非就是做缓存、资源静态化、把服务器的节点拉入云用户

资源静态化相当于把原来需要后端处理的响应数据缓存下来,通过缓存的方式极大地减少后端服务器的计算工作

从合并文件输出和集群文件同步讲资源静态化,静态页面资源生成和管理的方案自己查找资料学习

资源静态化的解决方案还有很多,自己学习

  1. 资源静态化原理

    • 现在的项目nginx一般都作为反向代理服务器,将用户请求转发到上游服务器做一些计算,比如查看账户余额、登录功能,订外卖,资源静态化在这种系统中非常频繁的出现,尤其是高并发的电商系统,资源静态化能够将原本需要上游服务器计算的内容以文件的形式缓存在nginx代理服务器中,比如外卖的商家列表,这些商家信息并不是每次都去数据库中进行查询的, 这些信息都可以缓存在nginx中,像get请求获取的结果很多都可以进行资源静态化处理【电商系统并发量最高的是商品的详情页,其次是列表页,然后是首页和广告页面】;只有当涉及到列表信息变动时才会涉及到数据一致性的问题;

    • 传统的架构是用户发起请求,经过nginx转发到上游服务器,上游服务器从数据库查询到详情页数据响应给nginx服务器再被响应给客户端;

    • 资源静态化将动态获取的数据转化成静态页面【这种方式特别适合页面中数据只会少量变化的情况】,动态资源转换成静态页面可以通过java中的Themeleaf、JSP、Enjoy、Freemarker、Velocity,模板引擎的工作原理是模仿html标签,将动态查询出的数据以标签的形式写入特殊后缀的文件,这些文件在服务器端会被渲染解析成html页面并以流的形式输出静态页面【out.write()】给客户端,一般的模板引擎都有拦截输出流的作用,可以选择将渲染出来的静态页面输出给客户端还是输出到文件中,通过这种方式就可以直接把一个商品的详情页查出来动态的生成静态页面文件,此时可以直接将生成的这个静态页面文件直接前置到nginx反向代理服务器中,这个过程就叫资源静态化;

    • 下次相同请求直接从nginx取该静态文件即可;并且结合OpenResty可以实现客户端发起请求去请求静态资源如item100.html,此时就可以使用OpenResty对nginx做二次开发,设置如果nginx自身找不到对应的静态资源,OpenResty可以直接连接数据库通过内置的模板引擎跨过【不经过】tomcat服务器直接从数据库把数据读取到OpenResty经过模板引擎渲染后直接响应给客户端并且同时将生成的静态文件直接缓存在nginx服务器本地的磁盘中,下次还有相同请求打到nginx直接一个sendfile就把数据响应过去了,速度极快【小型系统直接使用最早的架构技术也不会有问题,高并发系统下这是非常大的一个优化点,能极大地提高系统的效率,也有很多现成的组件和开源项目都可以使用】

      高并发的系统架构还会引入更多的技术,现在讲的只是冰山一角

    • 同时,nginx代理服务器一般还是高可用的配置,会配置多台nginx,此时还会涉及到多台nginx反向代理服务器上静态资源同步的问题【因为一个nginx服务器中生成了新的静态资源文件但是别的nginx服务器上没有,客户端请求也会出现问题,再请求缓存一次消耗也不是特别大,会不会是其他的原因呢】,同步操作可以通过额外一些手段实现【比如在linux上有一款非常好用的工具叫rsync,这个工具就能将一台nginx上的文件同步到另外一台nginx服务器上,此时就可以专门在一台nginx服务器上生成静态资源文件然后同步给其他nginx服务器,为了系统的稳定性和高效,一般把这个用于生成静态资源的nginx服务器独立出来,不再进行代理请求和响应资源的工作,而是专门负责生成静态资源然后同步给其他nginx服务器,让客户端去请求其他的nginx服务器】

      【资源静态化架构图】

      这个图比较随意,只讲了个大概

  2. 一个静态页面包含的几个部分

    • 页面内容【商品详情介绍】

      像价格这种容易变化的内容一般都不生成在静态页面中,或者时常变化的数据都不放在静态页面中,以异步请求的方式【页面渲染前进行请求,或者js来发起异步请求】将这些资源加载进来参与最终页面的生成,比如我的购物车也经常变化,也不能直接生成静态资源,像京东这种网站的首页【首页对每个人的推荐肯定不一样】,可以只生成一个骨架,再配合CDN、异步加载来展现整个效果;原则上就是不要把动态变化的数据和资源直接生成在静态页面中

      容易变化的数据和静态资源之间的区别也是一种一致性问题,多个nginx服务器之间的静态资源同步也是一种一致性问题

      如果动态的数据必须写入静态资源页面,此时和一致性问题无法避开,可以使用两步提交的方式来解决这个问题,【这里举例举的不好,意会一下,就是用户展示的还是静态页面的价格,但是一旦涉及到使用该数据进行操作比如结算时,一定要再次请求数据库对数据进行验证和更新,即展示数据用老数据,但是涉及到使用数据进行更改操作时需要查询验证更新数据,还有其他方案,以后再学习】,我有个问题,课上没讲,这种展示老数据的行为不会影响到业务吗?客户对这种行为肯定有意见啊

    • 关联的内容【比如广告或者商品推荐,没法做成静态的,这部分需要进行动态的请求,还有点击操作发送的异步请求】

    • 固定页面【固定共用的页面头和页面尾,没必要每个页面都生成一份,这样资源消耗大而且一旦发生修改波及面特别广,比如@CopyRight2023更新成@CopyRight2024,如果每个页面一份,那波及面就特别广了,此时一般都单独抽取出来作为独立的静态页面】

      把固定共用的部分和其他两个页面内容组合合并的方法一般有两个:

      前端合并:比如用js的innerHtml把另外一个资源给包含进来,使用iframe或者html的方式来合并【谷粒学院的default.vue】,这种方式节约服务器端的计算资源,但是消耗请求数【请求数举例说的是前端有5个iframe,就会额外再向服务器发起五次请求,但是之前的谷粒学院不是这样的,谷粒学院是使用一个独立的头和脚页面拼接的】

      后端合并:通过nginx,将固定共用的资源和其他两个内容直接在服务器合并好【有点像concat,该技术叫SSI,可以合并静态页面,SSI是nginx的官方模块,和Concat模块没有关系】

合并文件输出

nginx自带的合并静态资源的模块为Module ngx_http_ssi_module

  1. SSI模块配置参数

    • ssi

      • 参数值: ssi on|off
      • 默认值: off
      • 作用域: http, server, locationif in location

      开启nginx的ssi模块功能,if in location指的是可以在location作用域的if逻辑判断中使用

    • ssi_last_modified

      • 参数值: ssi_last_modified on|off
      • 默认值: off
      • 作用域: http, server, location

      将该属性设置为on会让合并的新文件的最后修改时间为主文件【非使用标签参与合并的文件】的最后修改时间,即不使用文件合并的时间而使用主文件在磁盘上存储的最后时间,这样是为了在浏览器使用缓存时会去根据文件的最后修改时间为依据去检验服务器上的文件是否发生修改,一旦最后修改的时间不一致,浏览器就会认为缓存已经过时;因此这个last_modified对浏览器缓存至关重要,默认是采用合并文件的当前时间作为响应文件的最后修改时间

    • ssi_min_file_chunk

      • 参数值: ssi_min_file_chunk size
      • 默认值: 1k
      • 作用域: http, server, location

      该属性的作用是在合并文件时当文件大小大于设置值1k时可以将合并出来的文件存储在磁盘上,如果不满足大于1k的情况文件就不会存储在磁盘上,文件不存储在磁盘上就不能使用sendfile功能

    • ssi_silent_errors

      • 参数值: ssi_silent_errors on | off
      • 默认值: off
      • 作用域: http, server, location

      该属性配置成on的作用是配置模板引擎SSI在指令出现错误时对指令错误进行隐藏,文件错误不会进行隐藏

      错误有两种:一种是模板引擎的语法命令出错,第二种是要合并的文件没有

    • ssi_types

      • 参数值: ssi_types mime-type
      • 默认值: text/html
      • 作用域: http, server, location

      该属性的作用是指定ssi能合并的mime类型,默认是html文件,如果需要合并一些xhtml,xml需要在参数后面声明相应的类型,没有配置示例。自己搜一下;此外*表示匹配任意MIME类型【0.8.29版本以后支持】

    • ssi_value_length

      • 参数值: ssi_value_length length
      • 默认值: 256
      • 作用域: http, server, location

      该属性的作用是使用<!--# include file="top.html"-->这类模板引擎命令【SSI命令】时可以传递参数,ssi_value_length属性限制这些参数的长度大小,不是限制合并文件的大小

  2. SSI模块的语法

    这个模块的功能就很像模板引擎了

    这些命令在主文件的任意位置都可以使用,不建议将SSI命令设置的过于复杂,过于复杂就变成应用级别的服务器了,做模板的内容填充和渲染会消耗更多的服务器内存和计算资源,更多的时候nginx都去前端抗住流量,将计算分发到上游服务器做;当然也有用nginx做应用服务器的情况,但是SSI模块的业务场景实际生产中是比较单一的,基本上就是做文件引用和合并,更复杂的模版引擎还有其他的解决方案,比如java和PHP都有很多模板引擎,即便是nginx也有更高效强大的模板引擎OpenResty,动态渲染用OpenResty会更多一些

    命令一般格式:<!--# command parameter1=value1 parameter2=value2 ... -->

    以下的都是命令

    • <!--# include file="top.html"-->

      file属性使用的是相对路径,发现# include file就会去本地磁盘找top.html,使用的是相对路径,会从当前目录开始找,还可以写子目录,也可以写绝对路径;即便静态文件都位于tomcat上也没问题,nginx也能自动从上游服务器获取文件并合并在一起,而且合并的方式是读取top.html的完整内容并在标签处对标签进行替换

      此外,模板引擎对该标签一般不敏感,可以直接把这个标签写在模板引擎中

    • block

      命令语法如下

      <!--# block name="one" -->
      替代输出的内容
      <!--# endblock -->
      

      block命令是定义一个块,这个块中可以写其他的SSI命令并作为include命令的一部分【stub可以作为include命令中的一部分】,应用实例没说,遇到再补充

      讲include命令的时候遇到了,stub一般配合block一起使用,通过stub的属性值找name属性为对应属性值的block标签,block标签中夹住的内容作为include命令执行出问题响应的数据为空或者请求处理过程中错误发生后的替代输出,也可以在block中写其他的SSI命令

      【应用举例】

      虚拟远程请求出问题,请求结果响应不回来,就用name为one的block替代输出

      <!--# block name="one" -->&nbsp;<!--# endblock -->
      <!--# include virtual="/remote/body.php?argument=value" stub="one" -->
      
    • config

      可以设置SSI命令使用过程中用到的参数,应用实例没说,遇到再补充

      • message

        默认值是[an error occurred while processing the directive],即该参数的作用是设置SSI命令发生错误时的提示信息,参数类型是字符串

      • timefmt

        配置当使用日期相关的函数strftime()【这个是nginx内部的函数】去输出日期和时间显示的格式,默认值是"%A, %d-%b-%Y %H:%M:%S %Z"%s是将时间以秒的格式进行输出

    • echo

      可以通过该命令去输出一些变量值,输出变量时有以下三个参数可供选择

      • var

        指定变量的名称,nginx有很多内置的变量,比如请求中的一些值和nginx内部系统的变量,可以拿出来用

      • encoding

        编码方式。可能的值包括 noneurlentity。默认值为 entity。这个解释不清楚,遇到再补充【w3schools上提到是html的编码方式,默认的html编码方式是entity,相当于直接将文本给输出出来】,w3schools的nginx文档貌似对官网做了补充,mark一下Nginx 伪动态SSI服务器 (w3schools.cn)

      • default

        命令实例:<!--# echo var="name" default="no" -->,该命令相当于命令<!--# if expr="$name" --><!--# echo var="name" --><!--#else -->no<!--# endif -->

        当要去输出一个名为name的参数的参数值,但是发现该参数没有值,此时就使用default中定义的默认值进行输出,默认值的默认值为no,no表示啥都不输出

    • if

      if的功能就有点像模版引擎了,但SSI远远没有模板引擎功能强大,通过 if 命令进行条件控制,if能做的逻辑判断也算比较完整,且 if 命令支持正则判断

      判断的参数expr一般是布尔值,一般是通过$取的参数值,实际上引号中写的是正则表达式,高性能服务器中最好少使用正则,判断也少做;正则带指数级别的运算复杂度增加,每次请求都要来一遍,如果正则写不好会特别的消耗性能

      【格式总览】

      一共有四个标签,三个点表示可以配合其他的SSI命令一起使用,演示中只看到配合echo的情况

      <!--# if expr="..." -->
      ...
      <!--# elif expr="..." -->
      ...
      <!--# else -->
      ...
      <!--# endif -->
      
      • <!--# if expr="..." -->

      • <!--# elif expr="..." -->

      • <!--# else -->

      • <!--# endif -->

        【比较简单的格式举例】

        <!--这个是如果name属性有值就为true-->
        <!--# if expr="$name" -->
        
        <!--# if expr="$name = text" -->
        <!--# if expr="$name != text" -->
        <!--# if expr="$name = /text/" -->
        <!--# if expr="$name != /text/" -->
        

        【复杂格式举例】

        正则表达式和配合echo做输出

        <!--# if expr="$name = /(.+)@(?P<domain>.+)/" -->
            <!--# echo var="1" -->
            <!--# echo var="domain" -->
        <!--# endif -->
        
    • include

      注意发起include virtual请求上游服务器文件的时候可以使用subrequest_output_buffer_size来设置响应内容的最大长度【1.13.10版本以后,这里老师说的是内存缓冲区的大小,看文档的时候再说吧】

      • <!--# include file="footer.html" -->

        include file是直接读取磁盘文件并将其合并到使用SSI命令的文件,找的是真实的文件

      • <!--# include virtual="/remote/body.php?argument=value" -->

        include file可以取加载虚拟的路径,没讲清楚啥意思,补充了一下可以写上游服务器的地址,或许是根据地址去请求文件吧,remote可以认为是一个location即站点,可以对应一个根目录,请求的uri为body.php?argument=value,这个过程涉及到模板引擎渲染和异步操作的问题,如果像上游服务器请求数据,数据还没有响应回来,但是模板引擎已经渲染完毕而且已经将数据发送出去了,这是很有可能的;这种情况下会出现问题,此时常常配合wait参数把异步操作变成同步操作,让渲染过程阻塞一下,等请求数据拉回来完以后再进行渲染,避免渲染完结果数据还没返回的情况

      • stub

        stub一般配合block一起使用,通过stub的属性值找name属性为对应属性值的block标签,block标签中夹住的内容作为include命令执行出问题响应的数据为空或者请求处理过程中错误发生后的替代输出

        【应用举例】

        <!--# block name="one" -->&nbsp;<!--# endblock -->
        <!--# include virtual="/remote/body.php?argument=value" stub="one" -->
        
      • wait

        <!--# include virtual="/remote/body.php?argument=value" wait="yes" -->当请求上游服务器文件的时候,阻塞渲染过程,让渲染过程变成同步操作,等待请求数据返回

      • set

        <!--# include virtual="/remote/body.php?argument=value" set="one" -->将set指定的变量写入请求上游服务器的请求参数的参数值value中,来向上游服务器发起动态请求

    • set

      设置变量的值,有以下两个属性

      • var

        变量名

      • value

        给对应变量名的变量赋值的变量值

  3. 测试使用ssi模块合并公共页面【头尾文件】与内容页面

    在135的tomcat的webapps的root目录下创建以下文件并配合index文件合并成一个文件

    • top.html

      页面头

      top...
      <p>
      
    • bottom.html

      页面底

      <p>
      bottom...
      
    • index.html

      在主页中使用SSI模块的命令对top和bottom页面进行引入

      file属性使用的是相对路径,发现# include file就会去本地磁盘找top.html,使用的是相对路径,会从当前目录开始找,还可以写子目录,也可以写绝对路径

      <!DOCTYPE html>
      <html>
      	<head>
      		<meta charset="utf-8">
      		<title>tomcat</title>
              <!---->
              <!--# include file="top.html"-->
              <!-- 这个link不一定必须要加在标签内,直接找个标签外的地方写都行,但是外部css样式文件必须要通过link引入,不需要type属性都行,但是不能没有rel属性 -->
              <!-- 直接在href写对应的请求文件格式就能获取到对应的文件,不知道合并的具体过程,但是返回的应该是同一个文件,只是在服务端多个文件被合并了,浏览器也能解析返回的这个文件 -->
              <link rel="stylesheet" type="text/css" href="??font.css,page.css"/>
      	</head>
      	<body>
              <h1>welcome to tomcat!I'm 192.168.200.135</h1>
      		<h2>welcome to tomcat!I'm 192.168.200.135</h2>
      		<h3>welcome to tomcat!I'm 192.168.200.135</h3>
      		<h4>welcome to tomcat!I'm 192.168.200.135</h4>
      		<h5>welcome to tomcat!I'm 192.168.200.135</h5>
      		<h6>welcome to tomcat!I'm 192.168.200.135</h6>
              <!--# include file="bottom.html"-->
      	</body>
      </html>
      
    • 测试效果

      合并文件即使所有的文件都不在nginx服务器上,都需要从上游服务器获取,文件的合并也没有任何问题

      【响应回来的内容】

      <!DOCTYPE html>
      <html>
      	<head>
      		<meta charset="utf-8">
      		<title>tomcat</title>
              <!--html标签在头标签中也能渲染,这里合并文件的标签自动被nginx替换了,所有的文件都来自于上游服务器-->
              top...
      <p>
              <!-- 这个link不一定必须要加在标签内,直接找个标签外的地方写都行,但是外部css样式文件必须要通过link引入,不需要type属性都行,但是不能没有rel属性 -->
              <!-- 直接在href写对应的请求文件格式就能获取到对应的文件,不知道合并的具体过程,但是返回的应该是同一个文件,只是在服务端多个文件被合并了,浏览器也能解析返回的这个文件 -->
              <link rel="stylesheet" type="text/css" href="??font.css,page.css"/>
      	</head>
      	<body>
              <h1>welcome to tomcat!I'm 192.168.200.135</h1>
      		<h2>welcome to tomcat!I'm 192.168.200.135</h2>
      		<h3>welcome to tomcat!I'm 192.168.200.135</h3>
      		<h4>welcome to tomcat!I'm 192.168.200.135</h4>
      		<h5>welcome to tomcat!I'm 192.168.200.135</h5>
      		<h6>welcome to tomcat!I'm 192.168.200.135</h6>
              <p>
      bottom...
      	</body>
      </html>
      
  4. 开启ssi_silent_errors测试合并语法出错和文件找不着的错误提示信息

    • 主文件代码

      top.html合并是合并语法错误;bottom.html是文件找不着错误

      <!DOCTYPE html>
      <html>
      	<head>
      		<meta charset="utf-8">
      		<title>tomcat</title>
              <!--语法错误include1-->
              <!--# include1 file="top.html"-->
              <!-- 这个link不一定必须要加在标签内,直接找个标签外的地方写都行,但是外部css样式文件必须要通过link引入,不需要type属性都行,但是不能没有rel属性 -->
              <!-- 直接在href写对应的请求文件格式就能获取到对应的文件,不知道合并的具体过程,但是返回的应该是同一个文件,只是在服务端多个文件被合并了,浏览器也能解析返回的这个文件 -->
              <link rel="stylesheet" type="text/css" href="??font.css,page.css"/>
      	</head>
      	<body>
              <h1>welcome to tomcat!I'm 192.168.200.135</h1>
      		<h2>welcome to tomcat!I'm 192.168.200.135</h2>
      		<h3>welcome to tomcat!I'm 192.168.200.135</h3>
      		<h4>welcome to tomcat!I'm 192.168.200.135</h4>
      		<h5>welcome to tomcat!I'm 192.168.200.135</h5>
      		<h6>welcome to tomcat!I'm 192.168.200.135</h6>
              <!--没有bottom1.html这个文件-->
              <!--# include file="bottom1.html"-->
      	</body>
      </html>
      
    • 未开启ssi_silent_errors的提示信息

    • 开启ssi_silent_errors的提示信息的提示信息

      模板引擎合并时的语法错误导致的错误提示信息直接被隐藏了,但是文件找不到的错误提示还在

      上来直接welcome,但是文件找不着404的错误没有被处理,这种错误需要去nginx中进行额外的配置【nginx错误页面的配置,后边讲】

静态文件同步

静态文件同步有很多种方式和工具,rsync是其中一个比较稳定成熟和成型的工具,在生产运维中选用的工具首要就是安全稳定,而不是追求新版本,同步操作的工具也没有什么额外的功能,只是希望将文件分发同步到不同的服务器上,使用rsync工具能够避免再去开发文件同步的过程,因为这里面涉及到比较多的细节,开发起来比较麻烦

  1. 文件同步原理

    原服务器,生成静态文件并分发给目标服务器;目标服务器获取文件并响应客户请求

    • 原服务器生成静态文件如index.html,此时希望将该文件推送到目标服务器集群中,推送的操作相对容易实现,除了同步外原服务器中的文件删除也需要通知目标服务器删除,但是目标服务器中可能存在原服务器中没有的文件,此时用原服务器来管理目标服务器可能会出现问题;此外还有文件修改以后原服务器还需要根据文件大小和最后修改时间去判断各个服务器中的数据是否更新,如果目标服务器将文件锁死,原服务器即便检测到文件需要更新,数据也写不进去;细节问题很多,开发起来比较麻烦,借助第三方工具可以节省这部分工作

    • 生成文件依靠一套系统java或者OpenResty,生成的文件同步依靠另一套系统;

    • rsync文件同步需要原服务器和目标服务器都安装rsync,rsync很像spc命令,只是rsync没有在操作系统中自带,scp命令可以找到目标地址,经过授权认证,将文件直接推送到目标地址;但rsync的功能更加强大,可以监控原服务器某个目录下所有文件的变动,如果原服务器只有一个文件发生了变化,通过远程比对就可以实现只推送一个文件到目标服务器,且传输过程中会自动压缩文件,rsync在系统中的作用主要是复制、推送以及比对

    • 在原服务器上一般还有另外一个工具inotify,这个工具是用来监控服务器指定目录下的文件是否发生变化,监控目录中的文件是否存在增删改操作,一旦有变动,由inotify调用rsync来来推送文件到目标地址,整个文件同步过程由inotify和rsync配合完成的

Rsync

rsync【remote synchronize】的官网:https://www.samba.org/ftp/rsync/rsync.html

remote synchronize是一个远程数据同步工具,可通过 LAN/WAN 快速同步多台主机之间的文件。也可以使用 rsync 同步本地硬盘中的不同目录。rsync 是用于替代 rcp 的一个工具,rsync 使用所谓的 rsync算法 进行数据同步,这种算法只传送两个文件的不同部分,而不是每次都整份传送,因此速度相当快。

rsync 基于inotify 开发

安装Rsync
  1. 使用命令yum install -y rsync安装rsync
安装成功测试
  1. 配置了源服务器上的rsyncd.conf,并且两台服务器的rsync都启动后,在目标服务器上使用命令rsync --list-only 192.168.200.131::ftp/查看源服务器上的文件目录是否能显示

    也可以直接使用ps -ef|grep rsync查看对应的进程是否存在

Rsync配置
  1. 配置Rsync的目标和源

    让132服务器实时监控131源服务器的文件变化,一旦文件有变化随时拉取同步

    • rsync的配置文件的完整文件目录/etc/rsyncd.conf,初始状态下配置文件中啥也没有,一般简单使用基本配置都不需要改

      【默认配置文件】

      # /etc/rsyncd: configuration file for rsync daemon mode
      
      # See rsyncd.conf man page for more options.
      
      # configuration example:
      
      # uid = nobody
      # gid = nobody
      # use chroot = yes
      # max connections = 4
      # pid file = /var/run/rsyncd.pid
      # exclude = lost+found/
      # transfer logging = yes
      # timeout = 900
      # ignore nonreadable = yes
      # dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
      
      # ftp指一组资源,path指rsync需要同步的目录,表示一个需要同步的项目
      # [ftp]
      #        path = /home/ftp
      #        comment = ftp export area
      

      【源服务器上的rsync配置】

      需要被同步的服务器

      # /etc/rsyncd: configuration file for rsync daemon mode
      
      # See rsyncd.conf man page for more options.
      
      # configuration example:
      
      # uid = nobody
      # gid = nobody
      # use chroot = yes
      # max connections = 4
      # pid file = /var/run/rsyncd.pid
      # exclude = lost+found/
      # transfer logging = yes
      # timeout = 900
      # ignore nonreadable = yes
      # dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
      
      # ftp指一组资源,path指rsync需要同步的目录,表示一个需要同步的项目
      [ftp]
      	path = /usr/local/nginx/html
      #        comment = ftp export area
      
    • 使用命令rsync --daemon启动源服务器的rsync,使用命令ps -ef | grep rsync可以查看rsync的运行状态

      rsync的运行需要源和目标服务器的rsync都进行启动

    • 使用命令rsync --list-only 192.168.200.131::ftp/在目标服务器上查看源服务器上同步目录的文件

      🔎ftp就是源服务器上的rsync配置文件中的默认配置组ftp

      [root@nginx2 ~]# rsync --list-only 192.168.200.131::ftp/
      drwxr-xr-x            107 2023/12/28 21:18:27 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             14 2023/12/28 21:18:27 bottom.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
    • 使用命令ll /usr/local/nginx/html/查看目标服务器的本地文件状况

      注意此时目标服务器的rsync还没有启动

      [root@nginx2 ~]# ll /usr/local/nginx/html/
      总用量 8
      -rw-r--r--. 1 root root 494 1116 08:39 50x.html
      -rw-r--r--. 1 root root 491 1220 19:29 index.html
      
    • 在目标服务器上使用命令rsync -avz 192.168.200.131::ftp/ /usr/local/nginx/html/对源服务器的同步目录进行同步,将文件同步到指定目录/usr/local/nginx/html/,只使用命令rsync -avz 192.168.200.131::ftp/只会显示正在接收文件,但是找不到对应的文件

      同步以后指定目录的文件和远程服务器的对应目录基本一样,连最后修改时间都一样

      [root@nginx2 ~]# rsync -avz 192.168.200.131::ftp/
      receiving incremental file list
      drwxr-xr-x            107 2023/12/28 21:18:27 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             14 2023/12/28 21:18:27 bottom.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
      sent 20 bytes  received 182 bytes  404.00 bytes/sec
      total size is 1,698  speedup is 8.41
      [root@nginx2 ~]# ll /usr/local/nginx/html/
      总用量 8
      -rw-r--r--. 1 root root 494 1116 08:39 50x.html
      -rw-r--r--. 1 root root 491 1220 19:29 index.html
      
      [root@nginx2 ~]# rsync -avz 192.168.200.131::ftp/ /usr/local/nginx/html/
      receiving incremental file list
      ./
      bottom.html
      font.css
      index.html
      page.css
      top.html
      
      sent 128 bytes  received 1,064 bytes  2,384.00 bytes/sec
      total size is 1,698  speedup is 1.42
      [root@nginx2 ~]# ll /usr/local/nginx/html
      总用量 24
      -rw-r--r--. 1 root root  494 1116 08:39 50x.html
      -rw-r--r--. 1 root root   14 1228 21:18 bottom.html
      -rw-r--r--. 1 root root   25 1228 21:18 font.css
      -rw-r--r--. 1 root root 1117 1228 21:18 index.html
      -rw-r--r--. 1 root root   37 1228 21:18 page.css
      -rw-r--r--. 1 root root   11 1228 21:18 top.html
      
同步安全认证
  1. 在源服务器上增加安全认证,让服务器更加安全

    这个操作让目标服务器在同步的同时需要输入用户名和存储在源服务器上的对应密码

    • 使用命令echo "earl:123456" >> /etc/rsyncd.pwd

      将文本"earl:123456"输出到文件/etc/rsyncd.pwd中,这是创建一个安全认证文件,earl为账号、123456为密码

    • 在源服务器上rsync的配置文件/etc/rsyncd.conf添加安全认证

      🔎每次重新加载rsync需要重启rsync,rsync是没有重启命令的,rsync的重启需要杀掉rsync的进程,然后再使用命令rsync --deamon重启

      # /etc/rsyncd: configuration file for rsync daemon mode
      
      # See rsyncd.conf man page for more options.
      
      # configuration example:
      
      # uid = nobody
      # gid = nobody
      # use chroot = yes
      # max connections = 4
      # pid file = /var/run/rsyncd.pid
      # exclude = lost+found/
      # transfer logging = yes
      # timeout = 900
      # ignore nonreadable = yes
      # dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
      
      #配置rsync的安全认证,意思是每次目标服务器发起同步请求都需要输入密码,设置用户名为earl,在安全认证文件中其对应的密码是123456
      auth users = earl
      secrets file = /etc/rsyncd.pwd
      
      # ftp指一组资源,path指rsync需要同步的目录,表示一个需要同步的项目
      [ftp]
      	path = /usr/local/nginx/html
      #        comment = ftp export area
      
    • 在目标服务器上使用命令rsync -avz 192.168.200.131::ftp/ /usr/local/nginx/html/向源服务器发起同步请求,观察此时的运行状态

      🔎此时发现发起同步请求需要输入密码,即便输入了密码还是提示错误,这是因为源服务器使用了安全认证文件的同时需要将安全认证文件的权限修改的低一点

      [root@nginx2 ~]# rsync -avz 192.168.200.131::ftp/ /usr/local/nginx/html/
      Password: 
      @ERROR: auth failed on module ftp
      rsync error: error starting client-server protocol (code 5) at main.c(1656) [Receiver=3.1.2]
      
    • 在源服务器上使用命令chmod 600 /etc/rsyncd.pwd给安全认证文件降低权限,降低权限后重启源服务器rsync

      💡其实这个权限太高源服务器在使用命令rsync --daemon后台启动的时候是爆了错的,但是后台运行不会把报错信息显示出来,这个不像nginx配置文件有一点问题服务根本起不来,这个配置文件有问题报错也能起来,但是同步的时候会报错

      【初始权限】

      [root@nginx1 etc]# ll | grep rsync
      -rw-r--r--.  1 root root        516 1230 16:35 rsyncd.conf
      -rw-r--r--.  1 root root         12 1230 16:36 rsyncd.pwd
      

      【修改后的权限】

      [root@nginx1 etc]# ll |grep rsyncd
      -rw-r--r--.  1 root root        516 1230 16:35 rsyncd.conf
      -rw-------.  1 root root         12 1230 16:36 rsyncd.pwd
      
    • 在目标服务器上使用命令rsync -avz 192.168.200.131::ftp/ /usr/local/nginx/html/再次向源服务器发起同步请求

      🔎仍然报错,原因是当源服务器开启安全认证后需要在发起同步请求时输入对应密码的用户名

      [root@nginx2 ~]# rsync -avz 192.168.200.131::ftp/ /usr/local/nginx/html/
      Password: 
      @ERROR: auth failed on module ftp
      rsync error: error starting client-server protocol (code 5) at main.c(1656) [Receiver=3.1.2]
      
    • 在目标服务器上使用命令rsync -avz earl@192.168.200.131::ftp/ /usr/local/nginx/html/再次向源服务器发起同步请求

      🔎可以看见命令rsync -avz earl@192.168.200.131::ftp/ /usr/local/nginx/html/只会显示本次同步发生了修改操作的文件,命令rsync -avz earl@192.168.200.131::ftp/会显示源服务器中对应目录下的所有文件但不会进行同步操作

      [root@nginx2 ~]# rsync -avz earl@192.168.200.131::ftp/ /usr/local/nginx/html/
      Password: 
      receiving incremental file list
      
      sent 20 bytes  received 182 bytes  44.89 bytes/sec
      total size is 1,698  speedup is 8.41
      
      [root@nginx2 ~]# rsync -avz earl@192.168.200.131::ftp/
      Password: 
      receiving incremental file list
      drwxr-xr-x            107 2023/12/28 21:18:27 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             14 2023/12/28 21:18:27 bottom.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
      sent 20 bytes  received 182 bytes  57.71 bytes/sec
      total size is 1,698  speedup is 8.41
      
  2. 免密登录

    📜使用SSH做SCP的时候可以做免密钥,rsync也可以实现免密钥的操作,在目标服务器上同样可以把密码记录在本机,当目标服务器发起同步请求的时候可以通过命令参数自动读取携带文件中的密钥

    🔎同样密钥文件的权限也需要降低至600

    • 使用命令echo "123456" >> /etc/rsyncd.pwd.client将密钥写入目标服务器的文件中

      只需要写密钥,用户名在同步命令中指明

    • 使用命令rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/发起同步请求

      密码由参数--password-file=/etc/rsyncd.pwd.client指明
      此时仍然显示错误的原因是密钥文件的权限太高

      [root@nginx2 ~]# echo "123456" >> /etc/rsyncd.pwd.client
      [root@nginx2 ~]# rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/
      ERROR: password file must not be other-accessible
      rsync error: syntax or usage error (code 1) at authenticate.c(196) [Receiver=3.1.2]
      
    • 使用命令chmod 600 /etc/rsyncd.pwd.client重新设置密钥文件的权限并再次发起显示同步目录的文件列表请求

      [root@nginx2 ~]# chmod 600 /etc/rsyncd.pwd.client
      [root@nginx2 ~]# rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/
      drwxr-xr-x            107 2023/12/28 21:18:27 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             14 2023/12/28 21:18:27 bottom.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
Rsync的命令参数
  1. 命令参数列表

    -a打头的参数主要就是arv,其他的大部分是保持原文件的属性信息

    -delete是做完全同步的,添加了该参数目标服务器和源服务器上的文件会完全同步,多出来不同的文件或者源服务器上删除的文件目标服务器上也会对应删除

    -exclude是将文件名中包含指定字符串的文件过滤掉不进行同步,比如在静态文件合并的过程中可能目录中存在一些临时文件,此时就可以使用-exclude参数排除这些文件被同步,还可以避免同步其他一切文件创建过程中产生的临时文件【一般创建文件的过程中会生成一个临时文件和一个锁文件】

    选项含义
    -aa表示跟随其他命令,可跟随-rtplgoD命令
    -rr表示要同步整个目录,类似cp时的-r选项
    -vv为用户显示同步过程中的一些信息
    -l保留软连接
    -L加上该选项后,同步软链接时会把源文件给同步
    -p保持文件的权限属性
    -o保持文件的属主
    -g保持文件的属组
    -D保持设备文件信息
    -t保持文件的时间属性
    –delete删除目标服务器中对应源服务器中同步目录没有的文件
    –exclude过滤指定文件,如–exclude “logs”会把文件名包含logs的文件或者目录过滤掉,不同步
    -P显示同步过程,比如速率,比-v更加详细
    -u加上该选项后,如果目标服务器中的文件比源服务器上的文件新,则不同步
    -z传输时压缩
近时同步方案

让同步的操作稍微复杂一点点,此前的同步操作实际上都是手动进行同步,此时在源服务器删掉一个文件,不在目标服务器手动同步,目标服务器是不会发生任何变化的

💡注意此前的同步都是增量同步,增加文件能同步过来,在以下试验中测试删文件同步【完全同步】

以下的实时同步用到的脚本、权限和rsync配置文件都是测试粗暴使用的,不能直接用在生产环境,生产环境让运维写专业的

  1. 减量同步

    🔎此前的同步命令rsync -avz --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/ /usr/local/nginx/html/只能做增量同步,当源服务器同步目录文件删减目标服务器使用该命令是无法相应的删除文件的,此时需要在同步命令中添加--delete参数删除目标服务器中对应源服务器中同步目录没有的文件

    • 删除源服务器上的bottom.html文件,注意此前源服务器和目标服务器因为同步过的原因,两个服务器上的文件是完全相同的

    • 在目标服务器上使用命令rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/查看源服务器同步目录文件列表

      可以看见源服务器同步目录中的bottom.html没了

      [root@nginx2 ~]# rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/
      drwxr-xr-x             88 2023/12/30 17:54:20 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
    • 在目标服务器上使用命令rsync -avz --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/ /usr/local/nginx/html/进行一次同步,同步后观察目标服务器同步目录的情况

      发现目标服务器上的bottom.html文件还在,减量同步没有实现

      [root@nginx2 ~]# rsync -avz --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/ /usr/local/nginx/html/
      receiving incremental file list
      ./
      
      sent 27 bytes  received 163 bytes  380.00 bytes/sec
      total size is 1,684  speedup is 8.86
      [root@nginx2 ~]# ll /usr/local/nginx/html/
      总用量 24
      -rw-r--r--. 1 root root  494 1116 08:39 50x.html
      -rw-r--r--. 1 root root   14 1228 21:18 bottom.html
      -rw-r--r--. 1 root root   25 1228 21:18 font.css
      -rw-r--r--. 1 root root 1117 1228 21:18 index.html
      -rw-r--r--. 1 root root   37 1228 21:18 page.css
      -rw-r--r--. 1 root root   11 1228 21:18 top.html
      
    • 在目标服务器上使用命令rsync -avz --delete --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/ /usr/local/nginx/html/进行一次减量同步,观察同步情况

      🔎千万不要在delete和exclude参数前只加一个横线,会报错的

      [root@nginx2 ~]# rsync -avz -delete --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/ /usr/local/nginx/html/
      rsync: Failed to exec lete: No such file or directory (2)
      rsync error: error in IPC code (code 14) at pipe.c(85) [Receiver=3.1.2]
      rsync: did not see server greeting
      rsync error: error starting client-server protocol (code 5) at main.c(1656) [Receiver=3.1.2]
      
      [root@nginx2 ~]# rsync -avz --delete --password-file=/etc/rsyncd.pwd.client earl@192.168.200.131::ftp/ /usr/local/nginx/html/
      receiving incremental file list
      deleting bottom.html
      
      sent 20 bytes  received 156 bytes  352.00 bytes/sec
      total size is 1,684  speedup is 9.57
      
  2. 近时同步

    📜使用脚本完成目标服务器与源服务器的目录同步,有两种方案,这里只介绍第二种实时推送方案,第一种方案就是使用shell命令写个脚本的事情

    💡第一种方案的原理是在目标服务器上写一个shell脚本定时去执行一次手动同步的命令,比如每隔两秒钟就去检测一次源服务器的目录情况【这种方案的弊端是所有机器都要配置rsync客户端并编写运行定时同步脚本,最早的聊天室就是这样的,要获取到用户最新的聊天信息,就要写一个定时任务不停地去检查用户的信息是否发送出来,这种方式存在一定资源的浪费,这是比较low的方案,因为用户如果很久都没有发出消息,就会浪费很多网络资源】

    💡第二种方案是源服务器同步目录发生变化时,主动将同步文件推给目标服务器,即实时推送,实时推送也叫近时推送,实时推送需要借助另外一个工具inotify

    🔎源服务器向目标服务器进行同步推送需要目标服务器以后台运行的方式启动,即目标服务器上需要使用命令rsync --daemon启动,此前目标服务器手动拉取是不需要以后台运行的方式启动rsync的

    📻留意一下弹幕中提到的使用nfs共享做文件同步

    • 同样推送也涉及到安全认证的问题,此时需要在目标服务器上创建一个安全认证文件,来供推送方进行身份验证,使用命令echo "earl:123456" >> /etc/rsync.conf在目标服务器上创建安全认证文件

      这里的操作本质其实是用源服务器去连接目标服务器的rsync,额外操作是解决安全免密认证的

    • 在目标服务器上修改rsync的配置文件/etc/rsyncd.conf,并使用命令chmod 600 /etc/rsyncd.pwd修改安全认证文件的权限

      和源服务器的配置文件是一样的

      # /etc/rsyncd: configuration file for rsync daemon mode
      
      # See rsyncd.conf man page for more options.
      
      # configuration example:
      
      # uid = nobody
      # gid = nobody
      # use chroot = yes
      # max connections = 4
      # pid file = /var/run/rsyncd.pid
      # exclude = lost+found/
      # transfer logging = yes
      # timeout = 900
      # ignore nonreadable = yes
      # dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
      
      #配置rsync的安全认证,意思是每次源服务器发起同步推送都需要输入密码,设置用户名为earl,在安全认证文件中其对应的密码是123456
      auth users = earl
      secrets file = /etc/rsyncd.pwd
      
      # ftp指一组资源,path指rsync需要同步的目录,表示一个需要同步的项目
      [ftp]
      	path = /usr/local/nginx/html
      #        comment = ftp export area
      
    • 在目标服务器上使用命令rsync --daemon以后台运行的方式启动rsync

    • 在源服务器上使用命令echo "123456" >> /etc/rsyncd.pwd.client创建密钥文件,并使用命令chmod 600 /etc/rsyncd.pwd.client修改密钥文件的权限

    • 在源服务器上使用命令rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.132::ftp/免密访问目标服务器上的同步目录列表

      [root@nginx1 ~]# rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.132::ftp/
      drwxr-xr-x             88 2023/12/30 17:54:20 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
    • 在源服务器使用命令rsync -avz --password-file=/etc/rsyncd.pwd.client /usr/local/nginx/html/ earl@192.168.200.132::ftp/进行推送观察文件同步情况,课堂演示命令rsync -avz --password-file=/etc/rsyncd.pwd.client /usr/local/nginx/html/ rsync://earl@192.168.200.132::ftp/和本处使用命令效果是一样的,只是课堂的命令多了一个协议,不加也默认使用该协议

      🔎推送和拉取的两个区别是,拉取在目标服务器拉,拉取命令原服务器地址在同步目录前面,这个同步目录应该指的是源服务器上的目录,因为ftp那个是目标服务器上rsyncd.conf中的配置本地同步目录

      这里我在源服务器上创建了foot.html以观察推送情况

      📌推送发现报错网络连接失败,且文件并没有被成功推送到目标服务器;但是此前查看目标服务器的同步目录又是成功的

      🔑这是因为目标服务器少了一个配置,因为目标服务器认为远程提交文件到本地是有一定风险的,默认情况下是不接收用户远程提交文件的,需要在/etc/rsyncd.conf文件下添加配置

      [root@nginx1 ~]# rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.132::ftp/
      drwxr-xr-x             88 2023/12/30 17:54:20 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
      [root@nginx1 ~]# rsync -avz --password-file=/etc/rsyncd.pwd.client /usr/local/nginx/html/ earl@192.168.200.132::ftp/
      sending incremental file list
      rsync: read error: Connection reset by peer (104)
      rsync error: error in socket IO (code 10) at io.c(792) [sender=3.1.2]
      
      [root@nginx1 ~]# rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.132::ftp/
      drwxr-xr-x             88 2023/12/30 17:54:20 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
    • 在目标服务器的rsync配置文件/etc/rsyncd.conf中添加配置read only=no配置并重启目标服务器上的rsync

      该配置默认是yes,表示只读,别的机器可以来本机上拉东西,但是不能写;改成no表示可以读也可以写入

      # /etc/rsyncd: configuration file for rsync daemon mode
      
      # See rsyncd.conf man page for more options.
      
      # configuration example:
      
      # uid = nobody
      # gid = nobody
      # use chroot = yes
      # max connections = 4
      # pid file = /var/run/rsyncd.pid
      # exclude = lost+found/
      # transfer logging = yes
      # timeout = 900
      # ignore nonreadable = yes
      # dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
      
      #配置rsync的安全认证,意思是每次源服务器发起同步推送都需要输入密码,设置用户名为earl,在安全认证文件中其对应的密码是123456
      auth users = earl
      secrets file = /etc/rsyncd.pwd
      #将默认支持只读改成no
      read only = no
      # ftp指一组资源,path指rsync需要同步的目录,表示一个需要同步的项目
      [ftp]
      	path = /usr/local/nginx/html
      #        comment = ftp export area
      
    • 再次在原服务器使用命令rsync -avz --password-file=/etc/rsyncd.pwd.client /usr/local/nginx/html/ earl@192.168.200.132::ftp/发起推送

      课堂因为没有真的添加文件所以没报错,我这儿添加了foot.html后报错了,原因查了一下好像是目标服务器的uid和gid的问题,这里目前还没有找到办法解决,[问题参考连接](rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted - BigBao的博客 - 博客园 (cnblogs.com))【后面发生了该错误,进行了解决;这个报错不影响文件同步,这里应该是没有设置源服务器和目标服务的同步目录/usr/local/nginx/html/权限为777导致的文件无法同步】

      老师解释该错误是因为rsync的配置文件配置的过于简单,甚至连当前去操作同步的用户都没有配置【博客中也提到是这个原因】,修改配置文件避免报错看自动化同步的最后配置

      [root@nginx1 ~]# rsync -avz --password-file=/etc/rsyncd.pwd.client /usr/local/nginx/html/ earl@192.168.200.132::ftp/
      sending incremental file list
      rsync: failed to set times on "/." (in ftp): Operation not permitted (1)
      ./
      foot.html
      rsync: mkstemp "/.foot.html.uDyqR5" (in ftp) failed: Permission denied (13)
      
      sent 223 bytes  received 195 bytes  836.00 bytes/sec
      total size is 1,684  speedup is 4.03
      rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1179) [sender=3.1.2]
      [root@nginx1 ~]# rsync --list-only --password-file=/etc/rsyncd.pwd.client earl@192.168.200.132::ftp/
      drwxr-xr-x             88 2023/12/30 17:54:20 .
      -rw-r--r--            494 2023/11/16 08:39:46 50x.html
      -rw-r--r--             25 2023/12/28 21:18:25 font.css
      -rw-r--r--          1,117 2023/12/28 21:18:53 index.html
      -rw-r--r--             37 2023/12/28 21:18:25 page.css
      -rw-r--r--             11 2023/12/28 21:18:26 top.html
      
  3. 配置inotify+rsync做静态资源的自动化同步

    目前虽然是源服务器进行推送,但是消息推送是手动的,或者需要定时手动,这样可能消耗网络资源,使用inotify监控同步目录变化自动进行文件同步【inotify的作用就是监控磁盘文件下的某个目录是否发生变化,发生变化可以触发事件让rsync发起命令去同步目录】

    🔎这种方式需要原服务器和目标服务器的rsync同时都以后台运行的方式启动

    • 在源服务器安装inotify

      以前yum源中有inotify的安装包,现在没了,现在用wget安装【课堂链接失效了,找了另一个下载地址:https://src.fedoraproject.org/repo/pkgs/inotify-tools/inotify-tools-3.14.tar.gz/b43d95a0fa8c45f8bab3aec9672cf30c/】

      • 将安装包拷贝到/opt/inotify目录下,在当前目录下使用命令tar -zxvf inotify-tools-3.14.tar.gz 解压安装包
      • 进入解压目录使用命令./configure --prefix=/usr/local/inotify预编译文件到/usr/local/inotify目录
      • 使用命令make进行编译
      • 使用命令make install 进行安装
    • 安装成功测试

      • 如果inotify的安装目录有以下文件说明成功安装

        [root@nginx1 inotify-tools-3.14]# cd /usr/local/inotify
        [root@nginx1 inotify]# ll
        总用量 0
        drwxr-xr-x. 2 root root  45 1230 23:20 bin
        drwxr-xr-x. 3 root root  26 1230 23:20 include
        drwxr-xr-x. 2 root root 143 1230 23:20 lib
        drwxr-xr-x. 4 root root  28 1230 23:20 share
        
    • 在源服务器的inotify的bin目录中使用命令./inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w%f %e' -e close_write,modify,delete,create,attrib,move //usr/local/nginx/html/开启对同步目录的监控

      参数--timefmt会显示增删改查的时间日期

      🔎这个命令是前台启动,此时要操作改服务器需要再启动一个终端

    • 再起一台终端,并在同步目录中创建文件,观察inotify的运行状况

      【对同步目录进行操作】

      [root@nginx1 ~]# cd /usr/local/nginx/html
      [root@nginx1 html]# echo "xxx" >> 1
      [root@nginx1 html]# ls
      1  50x.html  font.css  foot.html  index.html  page.css  top.html
      

      【inotify的变化】

      🔎显示了监控目录的创建时间、修改时间等信息,同时利用其启动事件的机制运行脚本实现监控目录目标服务器同步

      [root@nginx1 bin]# ./inotifywait -mrq --timefmt '%Y-%m-%d %H:%M:%S' --format '%T %w%f %e' -e close_write,modify,delete,create,attrib,move //usr/local/nginx/html/
      2023-12-30 23:31:44 //usr/local/nginx/html/1 CREATE
      2023-12-30 23:31:44 //usr/local/nginx/html/1 MODIFY
      2023-12-30 23:31:44 //usr/local/nginx/html/1 CLOSE_WRITE,CLOSE
      
    • 在inotify的bin目录下创建rsync自动化同步文件脚本auto.sh,并使用命令chmod 777 auto.sh给脚本文件执行的权限【试验用的授权很粗暴,生产要降权谨慎思考】,同时在源服务器和目标服务器都使用命令chmod 777 /usr/local/nginx/html/将同步目录的权限给满

      这个脚本很简单,真正在线上跑的脚本比这个复杂;因为生产环境有很多容错上的操作,一般这活都是运维干的,这个脚本不要直接在线上使用,肯定会出问题;让运维大师好好写一个

      脚本可能出现错误也不报错,需要提前将每条命令都跑一下,能跑通再写入脚本

      delete参数表示目标和源服务器的文件完全统一【以本机为主】

      #!/bin/bash
      
      /usr/local/inotify/bin/inotifywait -mrq --timefmt '%d/%m/%y %H:%M' --format '%T %w%f %e' -e close_write,modify,delete,create,attrib,move //usr/local/nginx/html/ | while read file
      do
             
              rsync -az --delete --password-file=/etc/rsyncd.pwd.client /usr/local/nginx/html/ earl@192.168.200.132::ftp/
      done
      
    • 在源服务器的inotify的bin目录下使用命令./auto.sh运行脚本

    • 在源服务器新建文件观察目标服务器是否同步

      虽然有报错,但是文件可以同步,之前不能同步应该不是报错的原因,这里报错原因和之前是一样的,之前已经是没有设置双方同步目录777权限导致的

      rsync: failed to set times on "/." (in ftp): Operation not permitted (1)
      rsync: chgrp "/.1.OfKy1Q" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.2.xGFqgV" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.3.Sx2ivZ" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.4.L3XbK3" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.5.wZT4Y7" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.6.CWsYdc" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.7.GIsSsg" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.New File.sxGMHk" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.foot.html.6SjHWo" (in ftp) failed: Operation not permitted (1)
      rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1179) [sender=3.1.2]
      rsync: chgrp "/.1.1XJCO4" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.2.9BZrw9" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.3.UgDhee" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.4.RRT7Vi" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.5.KFcYDn" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.6.7D2Ols" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.7.FelG3w" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.New File.JB7xLB" (in ftp) failed: Operation not permitted (1)
      rsync: chgrp "/.foot.html.QC6ptG" (in ftp) failed: Operation not permitted (1)
      rsync error: some files/attrs were not transferred (see previous errors) (code 23) at main.c(1179) [sender=3.1.2]
      

      【文件同步效果】

    • 在源服务器的rsync的配置文件添加用户来解决文件同步报错问题

      🔑老师解释该错误是因为rsync的配置文件配置的过于简单,甚至连当前去操作同步的用户都没有配置【博客中也提到是这个原因】,修改配置文件避免报错

      真正生产的时候需要创建一个用户去启动文件同步进程,现在为了方便直接把用户uid和gid全部改成root【gid是用户组】,实际要和去执行文件同步的用户的用户名保持一致

      🔑这里只添加源服务器的uid和gid后仍然报错,同时修改了源和目标服务器的uid和gid就不报错了,表现为脚本执行没有一点消息,但是文件正常同步了

      # /etc/rsyncd: configuration file for rsync daemon mode
      
      # See rsyncd.conf man page for more options.
      
      # configuration example:
      
      uid = root
      gid = root
      # use chroot = yes
      # max connections = 4
      # pid file = /var/run/rsyncd.pid
      # exclude = lost+found/
      # transfer logging = yes
      # timeout = 900
      # ignore nonreadable = yes
      # dont compress   = *.gz *.tgz *.zip *.z *.Z *.rpm *.deb *.bz2
      
      #配置rsync的安全认证,意思是每次源服务器发起同步推送都需要输入密码,设置用户名为earl,在安全认证文件中其对应的密码是123456
      auth users = earl
      secrets file = /etc/rsyncd.pwd
      #将默认支持只读改成no
      read only = no
      # ftp指一组资源,path指rsync需要同步的目录,表示一个需要同步的项目
      [ftp]
      	path = /usr/local/nginx/html
      #        comment = ftp export area
      
    • 删除掉此前1-10的测试文件,向源服务器上传一个大一点的文件,观察目标服务器的同步效果

inotify的参数解析
  1. 参数列表

    参数说明含义
    -r–recursive递归监视目录和其子目录
    -q–quiet仅仅打印监控事件信息,不要什么信息都打印
    -m–monitor始终保持目录监听状态
    –excludei排除文件或目录时,不区分大小写
    –timefmt#指定事件输出格式
    –format#打印使用指定的输出类似格式字符串
    -e–event[ -e|–event … ]accessmodifyattribcloseopenmove_tomove createdeleteumount#通过此参数可以指定要监控的事件 #文件或目录被读取#文件或目录的内容被修改#文件或目录属性被改变#文件或目录封闭,无论读/写模式#文件或目录被打开#文件或目录被移动至另外一个目录#文件或目录被移动另一个目录或从另一个目录移动至当前目录#文件或目录被创建在当前目录#文件或目录被删除#文件系统被卸载

多级缓存

处理高并发最好的手段就是不让请求产生高并发,缓存能很好的配合这个主题

资源静态化相当于将服务器中的动态资源生成了静态资源,相当于上游服务器对动态数据做了一层缓存操作,将本应该动态查询的操作前置化了,做缓存时有个原则是尽可能将数据和缓存内容尽可能向用户靠拢;缓存除了资源静态化前置到nginx反向代理服务器中,还有很多其他方式:如浏览器缓存【浏览器发起请求时并不是真的每次都完整的向服务器发起请求,而是将本地一些之前访问过的数据直接响应给用户,通过nginx可以控制浏览器对哪些文件进行缓存】

  1. 业务场景示例

    • 很多时候用户的请求压根就到不了服务器,比如双十一秒杀抢购,可以在客户端预埋一些数据,在大量并发请求到来以前就能预测到大概会有多少并发量,比如商品秒杀,商品页面的购买按钮会由即将开始变成立即抢购;在页面还没有变化以前,就能大致根据一些预埋数据统计出页面大概有多少人关注,此时后端服务器其实能知道大概有哪些商品是热门商品了;此时后端服务器会下发一份热门商品列表给客户端,对于太过热门的商品比如10000个人抢一件商品,此时甚至会直接跳转您来晚了,或者点了也没用,让客户端根本没发起请求;即便让客户端发起请求了也可以直接在nginx把客户端请求拦截住,不再去调用加入购物车和检查库存的操作,直接根据热点数据判断,即使用户点击了立即抢购也会直接显示您来晚了,通过这种方式可以屏蔽掉很多的请求打到服务器上来处理高并发;这是很多电商平台都在使用的方法,预埋数据一般是在app上实现的,因为浏览器很多时候出于安全和功能上的考虑导致其不够灵活
  2. 常用的多级缓存

    大部分重复无意义的请求通过nginx服务器的资源静态化、动静分离、浏览器缓存和CDN缓存都会被过滤掉,

    很多缓存方式其实是混合在一起的,基本分类有:静态资源缓存、浏览器缓存、CDN缓存、正向代理缓存、反向代理缓存、Nginx内存缓存、外置内存缓存、上游服务器应用缓存

    缓存学好了就可以做一些大型网站的开发了,只要你的手段够多,就可以层层削减到达上游服务器的请求应对高并发场景,从容地处理高并发的冲击,同时nginx强大的扩容能力还能很好很快的应对系统瓶颈

    • nginx服务器

      💡对动态数据生成的静态页面进

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值