Nginx反向代理、负载均衡、动静分离、压缩、缓存、跨域、高可用、性能优化

一、介绍

        早期业务都是基于单体节点部署,前期访问流量不大,单体结构也可满足需求,但随着业务增长,流量也越来越大,最终单台服务器受到的访问压力也会逐步增高。时间一长,单台服务器性能无法跟上业务增长,就会造成线上频繁宕机的现象发生,最终导致系统瘫痪无法继续处理用户的请求。

        那么就需要部署集群,引入负载均衡技术。

引入负载均衡技术的优点:

  • 高可用: 当某个节点宕机后可以迅速将流量转移至其他节点。

  • 高性能: 多台服务器共同对外提供服务,为整个系统提供了更高规模的吞吐。

  • 拓展性: 当业务再次出现增长或萎靡时,可再加入/减少节点,灵活伸缩。

1.Nginx简介

   Nginx是目前负载均衡技术中的主流方案,几乎绝大部分项目都会使用它,Nginx是一个轻量级的高性能HTTP反向代理服务器,同时它也是一个通用类型的代理服务器,支持绝大部分协议,如TCP、UDP、SMTP、HTTPS

        Nginx与Redis相同,都是基于多路复用模型构建出的产物,因此它与Redis同样具备 「资源占用少、并发支持高」的特点,在理论上单节点的Nginx同时支持5W并发连接,而实际生产环境中,硬件基础到位再结合简单调优后确实能达到该数值。

Nginx引入前后,客户端请求处理流程的对比:

Nginx反向代理、负载均衡、动静分离、压缩、缓存、跨域、高可用、性能优化_第1张图片

        原本客户端是直接请求目标服务器,由目标服务器直接完成请求处理工作;

        加入Nginx后,所有的请求会先经过Nginx,再由其进行分发到具体的服务器处理,处理完成后再返回Nginx,最后由Nginx将最终的响应结果返回给客户端;

2.Nginx安装

首先创建Nginx的目录并进入:

mkdir /soft && mkdir /soft/nginx/  
cd /soft/nginx/  

下载Nginx的安装包,可以通过FTP工具上传离线环境包,也可通过wget命令在线获取安装包:

wget https://nginx.org/download/nginx-1.21.6.tar.gz  
或
yum -y install wget  

解压Nginx的压缩包:

tar -xvzf nginx-1.21.6.tar.gz  

下载并安装Nginx所需的依赖库和包:

yum install --downloadonly --downloaddir=/soft/nginx/ gcc-c++  
yum install --downloadonly --downloaddir=/soft/nginx/ pcre pcre-devel4  
yum install --downloadonly --downloaddir=/soft/nginx/ zlib zlib-devel  
yum install --downloadonly --downloaddir=/soft/nginx/ openssl openssl-devel
或
yum -y install gcc zlib zlib-devel pcre-devel openssl openssl-devel  

通过rpm命令依次将依赖包一个个构建,或者通过如下指令一键安装所有依赖包:

rpm -ivh --nodeps *.rpm  

进入解压后的nginx目录,然后执行Nginx的配置脚本,为后续的安装提前配置好环境,默认位于/usr/local/nginx/目录下(可自定义目录):

 cd nginx-1.21.6  
./configure --prefix=/soft/nginx/  

编译并安装Nginx

make && make install  

最后回到前面的/soft/nginx/目录,输入ls即可看见安装nginx完成后生成的文件。

修改安装后生成的conf目录下的nginx.conf配置文件:

vi conf/nginx.conf  
修改端口号:listen    80;  
修改IP地址:server_name  你当前机器的本地IP(线上配置域名);  

制定配置文件并启动Nginx

sbin/nginx -c conf/nginx.conf  
ps aux | grep nginx  

Nginx其他操作命令:

sbin/nginx -t -c conf/nginx.conf # 检测配置文件是否正常  
sbin/nginx -s reload -c conf/nginx.conf # 修改配置后平滑重启  
sbin/nginx -s quit # 优雅关闭Nginx,会在执行完当前的任务后再退出  
sbin/nginx -s stop # 强制终止Nginx,不管当前是否有任务在执行  

开放80端口,并更新防火墙:

firewall-cmd --zone=public --add-port=80/tcp --permanent  
firewall-cmd --reload  
firewall-cmd --zone=public --list-ports  

Windows/Mac的浏览器中,直接输入刚刚配置的IP地址访问Nginx

最终看到Nginx欢迎界面,代表Nginx安装完成。

二、Nginx反向代理-负载均衡

首先通过SpringBoot+Freemarker快速搭建一个WEB项目:springboot-web-nginx,然后在该项目中,创建一个IndexNginxController.java文件,逻辑如下:

@Controller  
public class IndexNginxController {  
    @Value("${server.port}")  
    private String port;  
  
    @RequestMapping("/")  
    public ModelAndView index(){  
        ModelAndView model = new ModelAndView();  
        model.addObject("port", port);  
        model.setViewName("index");  
        return model;  
    }  
}  

在该Controller类中,存在一个成员变量:port,它的值即是从application.properties配置文件中获取server.port值。当出现访问/资源的请求时,跳转前端index页面,并将该值携带返回。

前端的index.ftl文件代码如下:

  
      
        Nginx演示页面  
          
      
      
        

欢迎来到熊猫高级会所,我是竹子${port}号!

前提工作准备就绪后,再简单修改一下nginx.conf的配置即可:

upstream nginx_boot{  
   # 30s内检查心跳发送两次包,未回复就代表该机器宕机,请求分发权重比为1:2  
   server 192.168.0.000:8080 weight=100 max_fails=2 fail_timeout=30s;   
   server 192.168.0.000:8090 weight=200 max_fails=2 fail_timeout=30s;  
   # 这里的IP请配置成你WEB服务所在的机器IP  
}  
  
server {  
    location / {  
        root   html;  
        # 配置一下index的地址,最后加上index.ftl。  
        index  index.html index.htm index.jsp index.ftl;  
        proxy_set_header Host $host;  
        proxy_set_header X-Real-IP $remote_addr;  
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
        # 请求交给名为nginx_boot的upstream上  
        proxy_pass http://nginx_boot;  
    }  
} 

因为配置了请求分发的权重,8080、8090的权重比为2:1,因此请求会根据权重比均摊到每台机器,也就是8080一次、8090两次、8080一次......

Nginx请求原理

客户端发出的请求192.168.12.129最终会转变为:http://192.168.12.129:80/,然后再向目标IP发起请求,流程如下:

  • 由于Nginx监听了192.168.12.12980端口,所以最终该请求会找到Nginx进程;

  • Nginx首先会根据配置的location规则进行匹配,根据客户端的请求路径/,会定位到location /{}规则;

  • 然后根据该location中配置的proxy_pass会再找到名为nginx_bootupstream

  • 最后根据upstream中的配置信息,将请求转发到运行WEB服务的机器处理,由于配置了多个WEB服务,且配置了权重值,因此Nginx会依次根据权重比分发请求。

三、动静分离

1.什么是动静分离?

        动静分离应该是听的次数较多的性能优化方案,那先思考一个问题:为什么需要做动静分离呢?它带来的好处是什么?

        例如淘宝,当浏览器输入www.taobao.com访问淘宝首页时,打开开发者调试工具可以很明显的看到,首页加载会出现100+的请求数,而正常项目开发时,静态资源一般会放入到resources/static/目录下:

        在项目上线部署时,这些静态资源会一起打成包,那此时思考一个问题:假设淘宝也是这样干的,那么首页加载时的请求最终会去到哪儿被处理?首页100+的所有请求都会来到部署WEB服务的机器处理,那则代表着一个客户端请求淘宝首页,就会对后端服务器造成100+的并发请求。这对于后端服务器的压力是尤为巨大的。

        大部分请求属于静态的,这些资源大概率情况下,长时间也不会出现变动,那为何还要让这些请求到后端再处理呢?能不能在此之前就提前处理掉?因此经过分析之后能够明确一点:做了动静分离之后,至少能够让后端服务减少一半以上的并发量。

2.实现动静分离

先在部署Nginx的机器,Nginx目录下创建一个目录static_resources

mkdir static_resources 

将项目中所有的静态资源全部拷贝到该目录下,而后将项目中的静态资源移除重新打包。

稍微修改一下nginx.conf的配置,增加一条location匹配规则:

location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css){  
    root   /soft/nginx/static_resources;  
    expires 7d;  
} 

然后照常启动nginx和移除了静态资源的WEB服务,你会发现原本的样式、js效果、图片等依旧有效

其中static目录下的nginx_style.css文件已被移除,但效果依旧存在(绿色字体+蓝色大边框)

解读规则:location ~ .*\.(html|htm|gif|jpg|jpeg|bmp|png|ico|txt|js|css)

  • ~代表匹配时区分大小写

  • .*代表任意字符都可以出现零次或多次,即资源名不限制

  • \.代表匹配后缀分隔符.

  • (html|...|css)代表匹配括号里所有静态资源类型

也可以将静态资源上传到文件服务器中,然后location中配置一个新的upstream指向。

四、Nginx资源压缩

        建立在动静分离的基础之上,如果一个静态资源的Size越小,那么自然传输速度会更快,同时也会更节省带宽,因此我们在部署项目时,也可以通过Nginx对于静态资源实现压缩传输,一方面可以节省带宽资源,第二方面也可以加快响应速度并提升系统整体吞吐。

        在Nginx也提供了三个支持资源压缩的模块ngx_http_gzip_module、ngx_http_gzip_static_module、ngx_http_gunzip_module,其中ngx_http_gzip_module属于内置模块,代表着可以直接使用该模块下的一些压缩指令,后续的资源压缩操作都基于该模块,先来看看压缩配置的一些参数/指令:

Nginx反向代理、负载均衡、动静分离、压缩、缓存、跨域、高可用、性能优化_第2张图片

了解了Nginx中的基本压缩配置后,接下来可以在Nginx中简单配置一下:

http{
    # 开启压缩机制
    gzip on;
    # 指定会被压缩的文件类型(也可自己配置其他类型)
    gzip_types text/plain application/javascript text/css application/xml text/javascript image/jpeg image/gif image/png;
    # 设置压缩级别,越高资源消耗越大,但压缩效果越好
    gzip_comp_level 5;
    # 在头部中添加Vary: Accept-Encoding(建议开启)
    gzip_vary on;
    # 处理压缩请求的缓冲区数量和大小
    gzip_buffers 16 8k;
    # 对于不支持压缩功能的客户端请求不开启压缩机制
    gzip_disable "MSIE [1-6]\."; # 低版本的IE浏览器不支持压缩
    # 设置压缩响应所支持的HTTP最低版本
    gzip_http_version 1.1;
    # 设置触发压缩的最小阈值
    gzip_min_length 2k;
    # 关闭对后端服务器的响应结果进行压缩
    gzip_proxied off;
}

        在上述的压缩配置中,最后一个gzip_proxied选项,可以根据系统的实际情况决定,总共存在多种选项:

  • off:关闭Nginx对后台服务器的响应结果进行压缩。

  • expired:如果响应头中包含Expires信息,则开启压缩。

  • no-cache:如果响应头中包含Cache-Control:no-cache信息,则开启压缩。

  • no-store:如果响应头中包含Cache-Control:no-store信息,则开启压缩。

  • private:如果响应头中包含Cache-Control:private信息,则开启压缩。

  • no_last_modified:如果响应头中不包含Last-Modified信息,则开启压缩。

  • no_etag:如果响应头中不包含ETag信息,则开启压缩。

  • auth:如果响应头中包含Authorization信息,则开启压缩。

  • any:无条件对后端的响应结果开启压缩机制。

修改好了Nginx的压缩配置后,可以在原本的index页面中引入一个jquery-3.6.0.js文件

  

五、Nginx缓冲区

        接入Nginx的项目一般请求流程为:“客户端→Nginx→服务端”,在这个过程中存在两个连接:“客户端→NginxNginx→服务端”,那么两个不同的连接速度不一致,就会影响用户的体验(比如浏览器的加载速度跟不上服务端的响应速度)。

        在Nginx也同样存在缓冲区的机制,主要目的就在于:用来解决两个连接之间速度不匹配造成的问题 ,有了缓冲后,Nginx代理可暂存后端的响应,然后按需供给数据给客户端。先来看看一些关于缓冲区的配置项:

  • proxy_buffering:是否启用缓冲机制,默认为on关闭状态。

  • client_body_buffer_size:设置缓冲客户端请求数据的内存大小。

  • proxy_buffers:为每个请求/连接设置缓冲区的数量和大小,默认4 4k/8k

  • proxy_buffer_size:设置用于存储响应头的缓冲区大小。

  • proxy_busy_buffers_size:在后端数据没有完全接收完成时,Nginx可以将busy状态的缓冲返回给客户端,该参数用来设置busy状态的buffer具体有多大,默认为proxy_buffer_size*2

  • proxy_temp_path:当内存缓冲区存满时,可以将数据临时存放到磁盘,该参数是设置存储缓冲数据的目录。

  • path是临时目录的路径。

    • 语法:proxy_temp_path path;  path是临时目录的路径

  • proxy_temp_file_write_size:设置每次写数据到临时文件的大小限制。

  • proxy_max_temp_file_size:设置临时的缓冲目录中允许存储的最大容量。

  • 非缓冲参数项:

    • proxy_connect_timeout:设置与后端服务器建立连接时的超时时间。

    • proxy_read_timeout:设置从后端服务器读取响应数据的超时时间。

    • proxy_send_timeout:设置向后端服务器传输请求数据的超时时间。

具体的nginx.conf配置如下:

http{  
    proxy_connect_timeout 10;  
    proxy_read_timeout 120;  
    proxy_send_timeout 10;  
    proxy_buffering on;  
    client_body_buffer_size 512k;  
    proxy_buffers 4 64k;  
    proxy_buffer_size 16k;  
    proxy_busy_buffers_size 128k;  
    proxy_temp_file_write_size 128k;  
    proxy_temp_path /soft/nginx/temp_buffer;  
}  

使用缓冲也可以减少即时传输带来的带宽消耗。

六、缓冲机制

对于整个系统而言,加入缓存带来的优势额外明显:

  • 减少了再次向后端或文件服务器请求资源的带宽消耗。

  • 降低了下游服务器的访问压力,提升系统整体吞吐。

  • 缩短了响应时间,提升了加载速度,打开页面的速度更快。

Nginx中,如何配置代理缓存,缓存相关的配置项:

proxy_cache_path:代理缓存的路径。

proxy_cache_path path [levels=levels] [use_temp_path=on|off] keys_zone=name:size [inactive=time] [max_size=size] [manager_files=number] [manager_sleep=time] [manager_threshold=time] [loader_files=number] [loader_sleep=time] [loader_threshold=time] [purger=on|off] [purger_files=number] [purger_sleep=time] [purger_threshold=time];

解释一下每个参数项的含义:

  • path:缓存的路径地址。

  • levels:缓存存储的层次结构,最多允许三层目录。

  • use_temp_path:是否使用临时目录。

  • keys_zone:指定一个共享内存空间来存储热点Key(1M可存储8000个Key)。

  • inactive:设置缓存多长时间未被访问后删除(默认是十分钟)。

  • max_size:允许缓存的最大存储空间,超出后会基于LRU算法移除缓存,Nginx会创建一个Cache manager的进程移除数据,也可以通过purge方式。

  • manager_files:manager进程每次移除缓存文件数量的上限。

  • manager_sleep:manager进程每次移除缓存文件的时间上限。

  • manager_threshold:manager进程每次移除缓存后的间隔时间。

  • loader_files:重启Nginx载入缓存时,每次加载的个数,默认100。

  • loader_sleep:每次载入时,允许的最大时间上限,默认200ms。

  • loader_threshold:一次载入后,停顿的时间间隔,默认50ms。

  • purger:是否开启purge方式移除数据。

  • purger_files:每次移除缓存文件时的数量。

  • purger_sleep:每次移除时,允许消耗的最大时间。

  • purger_threshold:每次移除完成后,停顿的间隔时间。

proxy_cache:开启或关闭代理缓存,开启时需要指定一个共享内存区域。

proxy_cache zone | off;

zone为内存区域的名称,即上面中keys_zone设置的名称。

proxy_cache_key:定义如何生成缓存的键。

proxy_cache_key string;

string为生成Key的规则,如$scheme$proxy_host$request_uri

proxy_cache_valid:缓存生效的状态码与过期时间。

proxy_cache_valid [code ...] time;

code为状态码,time为有效时间,可以根据状态码设置不同的缓存时间。

        例如:proxy_cache_valid 200 302 30m;

proxy_cache_min_uses:设置资源被请求多少次后被缓存。

proxy_cache_min_uses number;

number为次数,默认为1。

proxy_cache_use_stale:当后端出现异常时,是否允许Nginx返回缓存作为响应。

proxy_cache_use_stale error;

error为错误类型,可配置timeout|invalid_header|updating|http_500...

proxy_cache_lock:对于相同的请求,是否开启锁机制,只允许一个请求发往后端。

proxy_cache_lock on | off;

proxy_cache_lock_timeout:配置锁超时机制,超出规定时间后会释放请求。

proxy_cache_lock_timeout time;

proxy_cache_methods:设置对于那些HTTP方法开启缓存。

proxy_cache_methods method;

method为请求方法类型,如GET、HEAD等。

proxy_no_cache:定义不存储缓存的条件,符合时不会保存。

proxy_no_cache string...;

string为条件,例如$cookie_nocache $arg_nocache $arg_comment;

proxy_cache_bypass:定义不读取缓存的条件,符合时不会从缓存中读取。

proxy_cache_bypass string...;

和上面proxy_no_cache的配置方法类似。

$upstream_cache_status:记录了缓存是否命中的信息,存在多种情况:

  • MISS:请求未命中缓存。

  • HIT:请求命中缓存。

  • EXPIRED:请求命中缓存但缓存已过期。

  • STALE:请求命中了陈旧缓存。

  • REVALIDDATED:Nginx验证陈旧缓存依然有效。

  • UPDATING:命中的缓存内容陈旧,但正在更新缓存。

  • BYPASS:响应结果是从原始服务器获取的。

配置一下Nginx代理缓存:

http{  
    # 设置缓存的目录,并且内存中缓存区名为hot_cache,大小为128m,  
    # 三天未被访问过的缓存自动清楚,磁盘中缓存的最大容量为2GB。  
    proxy_cache_path /soft/nginx/cache levels=1:2 keys_zone=hot_cache:128m inactive=3d max_size=2g;  
      
    server{  
        location / {  
            # 使用名为nginx_cache的缓存空间  
            proxy_cache hot_cache;  
            # 对于200、206、304、301、302状态码的数据缓存1天  
            proxy_cache_valid 200 206 304 301 302 1d;  
            # 对于其他状态的数据缓存30分钟  
            proxy_cache_valid any 30m;  
            # 定义生成缓存键的规则(请求的url+参数作为key)  
            proxy_cache_key $host$uri$is_args$args;  
            # 资源至少被重复访问三次后再加入缓存  
            proxy_cache_min_uses 3;  
            # 出现重复请求时,只让一个去后端读数据,其他的从缓存中读取  
            proxy_cache_lock on;  
            # 上面的锁超时时间为3s,超过3s未获取数据,其他请求直接去后端  
            proxy_cache_lock_timeout 3s;  
            # 对于请求参数或cookie中声明了不缓存的数据,不再加入缓存  
            proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;  
            # 在响应头中添加一个缓存是否命中的状态(便于调试)  
            add_header Cache-status $upstream_cache_status;  
        }  
    }  
}  

你可能感兴趣的:(技术,nginx,负载均衡,服务器)