运维 --- Nginx高级功能介绍

Nginx高级功能

反向代理服务器

Nginx可用做反向代理服务器。

什么是代理服务器

代理服务器位于用户与目标服务器之间,但是对于用户而言,代理服务器就相当于目标服务器,即用户直接访问代理服务器就可以获得目标服务器的资源。

运维 --- Nginx高级功能介绍_第1张图片

正向代理

用户向代理服务器发送一个请求,并指明目标服务器,这时代理服务器访问目标服务器并将返回信息传递给用户。最广泛的应用就是防火墙,夹在用户和端口之间,需要通过防火墙才能对端口进行访问。

即用户知道目标服务器地址,并主动使用。

反向代理

不同于正向代理,用户向代理服务器发送请求,并不需要指明目标服务器,此时代理服务器在用户看来就是目标服务器。

即用户不知道目标服务器地址,请求由代理服务器分配到目标服务器。

反向代理服务器的用途

**负载平衡:**反向代理服务器可以充当驻留在我们后端服务器前面的交通警察,并以提高速度和容量利用率的方式在一组服务器之间分配客户端请求,同时确保没有任何服务器过载。如果服务器未启动,则负载平衡器会将流量重定向到其余的在线服务器。

Web 加速: Nginx 反向代理用于压缩出站和入站数据,以及缓存常见请求的内容,这两者都加快了客户端和服务器之间的流量流动。

**安全性和匿名性:**我们可以拦截前往我们后端服务器的客户端的请求,通过这样做,反向代理服务器可以保护他们的身份并作为对安全攻击的额外防御。

负载均衡

什么是负载均衡?

简单从语义理解就是将负载分摊到多台服务器上,使其协同完成工作。

负载均衡是反向代理的重要组成,Nginx因高并发和低消耗所以很适合来做负载均衡器。

Nginx中的负载均衡策略

Nginx中的负载均衡策略由upstream上下文实现,加入有一组test的upstream

热备(Hot Standby)
upstream test {
    server www.example.com;
    server bad.example.com down;
    server bak.example.com backup;
}

在此例中,平时使用 www.example.com 提供服务,bad.example.com 则暂时下线,bak.example.com 作为发生故障时兜底的一台机器。又称故障转移 (failover)。

轮询 (round-robin)

轮询是默认的负载均衡算法。

upstream test {
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

如上配置,请求1—>srv1.example.com、请求2—>srv2.example.com、请求3—>srv3.example.com、请求4—>srv1.example.com…以此类推。

最小连接 (least-connected)

因应忙闲不均、负载不均的问题而生。

upstream test {
    least_conn;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

在此例中,Nginx 将试图减轻已经很忙的服务器的压力,把新请求分发至没那么忙的主机。

源地址哈希 (ip-hash)

Session 用于跟踪用户操作,涉及身份认证时 (用户系统) 往往能派上用场,而通常情况下会话信息保存在单机上,这就使得有些需求得在同一台服务器上完成,不能换到其他的服务器 (姑且不谈分布式 Session),也就是说要求一个用户的请求打在特定服务器上。

upstream test {
    ip_hash;
    server srv1.example.com;
    server srv2.example.com;
    server srv3.example.com;
}

Sticky Sessions (粘滞会话) 将保证一个用户对应一台服务器,从而解决 Session 不一致的问题 (通常表现为无法登录)。出于容灾理由,不建议大量使用。

当有一台服务器宕机,那么对应于这台服务器的用户的请求也就无法处理,所以不建议使用。

加权负载均衡 (Weighted Load Balancing)

给每台服务器一个权重,可以配合负载均衡策略来完成某些功能,以轮询策略为例。

upstream test {
    server srv1.example.com weight=3;
    server srv2.example.com;
    server srv3.example.com;
}

不加权重的权重将默认为1,根据上面的例子如果有十个请求那么将会有6个请求在srv1.example.com上处理,srv2.example.com和srv3.example.com均分剩余两个请求。

运行状况检查 (Health Checks)
upstream test {
    server www.example.com max_fails=0;
    server www2.example.com max_fails=2 fail_timeout=1d;
}

其中max_fails参数含义为尝试最大连接次数,默认值为1,fail_timeout参数含义为失败后服务器退出服务时间默认值为10s。

在此例中,www.example.com 的健康检查会被关闭,一直都标记为可用;www2.example.com 连不上时先重试 2 次,如果还不行就退出服务,下线 1 天,方便运维人员排障。

proxy_next_upstream指令

proxy_next_upstream 含括下列数种情况:

error 建立连接 / 发送请求 / 接收响应时出错(缺省值之一);
timeout 建立连接 / 发送请求 / 接收响应时超时(缺省值之一);
invalid_header 上游返回空白或无效响应;
http_500 上游返回 500 Internal Server Error;
http_501 上游返回 501 Not Implemented;
http_502 上游返回 502 Bad Gateway;
http_503 上游返回 503 Service Unavailable;
http_504 上游返回 504 Gateway Timeout;
http_404 上游返回 404 Not Found;
http_429 上游返回 429 Too Many Requests;
non_idempotent 解除对非幂等请求 (POST, LOCK, PATCH) 的封印,小心造成重复提交;
off 不得转给下一台服务器。

一般来说,即使某一台后端服务器返回了 500,这台服务器也会参与负载均衡,毕竟能收到 HTTP 状态码,就表示它还活着。但这样的结果在用户眼里跟 Connection Refused 以及 Operation Timed Out 可没啥区别,所以在此例中,把 500 一并纳入“在下一台服务器重试”的机制里。

location / {
    ...
    proxy_pass http://backend;
    proxy_next_upstream error timeout http_500;
    ...
}

Nginx缓存配置

proxy_cache 运用局部性的原理,备存一些先前被访问过、估计还要被再度使用的资源,使用户得以由前端服务器直接取得,从而减少后端服务器的资源开销,并缓解整个系统的压力。

缓存也是反向代理的用途之一。

要启用缓存,请在顶级 http { } 上下文中添加 proxy_cache_path 指令。

proxy_cache_path 指令

http {
    ...
    proxy_cache_path /usr/local/nginx/cache levels=1:2 keys_zone=the_cache_zone:10m inactive=1h max_size=512m use_temp_path=off;
    ...
}

proxy_cache_path 定义一个缓存目录,路径为 /usr/local/nginx/cache;levels=1:2 采用二级的目录结构;keys_zone 建立一块用于存放缓存键 (cache keys) 和元数据 (metadata) 的共享内存区,名叫 ”the_cache_zone” 且分配 10MB 的内存;inactive 不活跃的缓存文件 1 小时后将被清除;max_size 缓存所占磁盘空间的上限是 512MB;use_temp_path=off 为不另设临时目录。

启用缓存

为了清楚缓存状态可以添加响应头。

缓存指令包含

下列指令请斟酌使用——
proxy_cache_bypass 用于指定忽略缓存的情况,当其值为空或为零时,使用缓存。
proxy_cache_key 用于生成缓存键,区分不同的资源。要特别留心 Query String。
proxy_cache_min_uses 则规定缓存门槛,请求过多少次才缓存,不缓存低频请求,避免浪费。

location / {
    ...
    proxy_pass http://www.example.com;

    proxy_cache the_cache_zone;
    #           proxy_cache_bypass $is_args;
    # (default) proxy_cache_key $scheme$proxy_host$request_uri;
    #           proxy_cache_min_uses 3;
    
    add_header X-Cache-Status $upstream_cache_status;
    ...
}

$is_args 反映请求的 URI 是否带参数(网址中问号后面那一长串),若没有即为空值。$request_uri 近似于 $uri$is_args$args。key 是决定缓存命中率的因素之一。

配置自己的缓存期限

Nginx 作反代时,相对来说也是客户端。如果希望在目标服务器抓取的信息总是新鲜的,需要忽略上游的缓存期限,即不遵循源站的 Cache-Control 和 Expires 等响应头,需要再配置自己的缓存期限。

服务端一侧,proxy_cache_valid 控制的是 expiration (有效期),针对不同的 HTTP 状态码可以设定不同的有效期。inactive 决定的是 retention (保留期限),时间一到管你新不新鲜都要丢掉 (refresh)。可以理解为 inactive 要优位于 proxy_cache_valid。

location / {
    ...
    proxy_ignore_headers X-Accel-Expires Cache-Control Expires;

    proxy_cache_valid 301 1h;
    proxy_cache_valid 200 30m;
    proxy_cache_valid any 1m;
    proxy_cache_revalidate on;
    ...
}

proxy_cache_revalidate 将对客户端传来之 Etag 或 Last-Modified 作出验证,若服务端资源没有变化,则使用“稍早前”缓存页面,无论其有效期为何。有助减少回源次数。

缓存雪崩问题

因为大量数据在同一时间失效,造成数据大批量的访问目标服务器的现象。

缓存一时爽,全家火葬场。这显然不是我们想要看到的。

如果网站不是特别大、并发要求不是特别高,可以采取加锁排队暂时返回陈旧数据的方式缓解问题。但根本的还是要各别设定缓存期限,错峰更新缓存,不要一窝蜂。

  • proxy_cache_lock 对同一资源,未命中一次只回源一次,阻塞后续请求直至当前请求完成。
  • proxy_cache_lock_age “不能者止”,如果当前请求未能如期完成,就放行后续请求。
  • proxy_cache_lock_timeout 发生超时,同样放行,但不作缓存。
  • proxy_cache_use_stale 则是指定“共体时艰”的情境,比如服务器正在更新 (updating) 缓存的时候,或者遭遇 503 服务不可用错误的时候,勉予使用 (inactive 还未清理的) 过期缓存,以保持可用性。
  • proxy_cache_background_update 返回陈旧数据时,也跟源站要一份新鲜的,下次用。
location / {
    ...
    proxy_cache_lock on;
    # (default) proxy_cache_lock_age 5s;
    # (default) proxy_cache_lock_timeout 5s;

    proxy_cache_use_stale error timeout updating http_503;
    proxy_cache_background_update on;
    ...
}

手动清理缓存

倘若有手动清除缓存的需求,又用不起 NGINX Plus,不妨考虑 ngx_cache_purge。

下载源码、nginx -V 检视参数、追加参数 --add-module=…/ngx_cache_purge-2.3 且重新编译 Nginx,以完成添加模块的动作。

proxy_cache_purge 得回头看前面的配置,使用的缓存路径与 proxy_cache 的对应、缓存键向 proxy_cache_key 的看齐。URI 前面加上 /purge 即为清除缓存接口(最好不要被外部访问)。

location / {
    ...
    proxy_cache the_cache_zone;
    proxy_cache_key $scheme$proxy_host$request_uri;
    ...
}

location ~ /purge(/.*) {
    ...
    allow 127.0.0.1;
    deny all;			# 只允许本地访问/purge
    proxy_cache_purge the_cache_zone $scheme$proxy_host$1$is_args$args;
    ...
}

Nginx压缩和解压

压缩过程减少了传输数据的大小。然而,由于压缩发生在运行时,它也可能包括相当大的处理开销,这会对性能产生负面影响。Nginx 在向客户端发送响应之前执行压缩,但不会对已经压缩的响应进行双重压缩。

运维 --- Nginx高级功能介绍_第2张图片

整个请求过程来看,开启gzip和不开启gip功能,其http的请求和返回过程是一致的,不同的是参数。

当开启HTTP的gzip功能时,客户端发出http请求时,会通过headers中的Accept-Encoding属性告诉服务器“我支持gzip解压,解压格式(算法)deflate,sdch为:”。Accept-Encoding:gzip,deflate,sdch

注意,不是request说自己支持解压,Nginx返回response数据的时候就一定会压缩。这还要看本次Nginx返回数据的格式是什么,如果返回数据的原始数据格式,和设置的gzip_types相符合,这时Nginx才会进行压缩。

Nginx返回response headers是,如果数据被压缩了,就会在Content-Encoding属性中标示gzip,表示接下来返回的response content是经过压缩的;并且在Content-Type属性中表示数据的原始格式。

最后返回经过压缩的response content给客户端,客户端再进行解压。这里注意一下,在客户端发送的headers里面,有一个deflate,sdch。这是两种压缩算法,如果读者感兴趣,可以查查相关的资料(我建议查查,了解哈弗曼压缩算法对扩展自己的架构思路很有帮助)

原文链接:https://blog.csdn.net/liupeifeng3514/article/details/79018334

阅读这位博主的博客也是受益良多。

启用压缩

要启用压缩,请添加带有on参数的 gzip 指令:

gzip on;  

默认情况下,Nginx 仅使用 text/html(MIME 类型)压缩响应。要使用其他 MIME 类型压缩响应,请添加 gzip_types 指令并列出其他类型。

gzip_types text/plain application/xml;  

我们可以定义要压缩的响应的最小长度,使用 gzip_min_length 指令。默认值为 20 字节。

gzip_min_length 1000;  

开启解压

某些客户端不使用 gzip 编码方法处理响应。同时,可能需要动态存储压缩数据或响应并将它们存储在缓存中。为了成功地为接受和不接受压缩数据的客户端提供服务,NGINX 可以在将数据发送到后一种类型的客户端时即时解压缩数据。

要启用运行时解压缩,请使用 gunzip 指令。

location /storage/ {  
    gunzip on;  
    ...  
} 

gunzip 指令可以在与 gzip 指令相同的上下文中定义:

server {  
    gzip on;  
    gzip_min_length 1000;  
    gunzip on;  
    ...  
}  
注意:该指令在一个单独的模块中定义,默认情况下可能不包含在 NGINX 构建中。

发送压缩文件

要将压缩文件而不是常规文件发送到客户端,请在适当的上下文中将 gzip_static 指令设置为 on。

location / {  
    gzip_static on;  
} 

在上面的例子中,为了服务一个对 /path/to/file 的请求,NGINX 试图找到并发送文件*/path/to/file.gz*。如果文件不存在,或者客户端不支持 gzip,NGINX 会发送未压缩的文件。

使用 br 压缩

Brotli 是 Google 研发的一种新兴压缩算法,可以加快网页的载入速度。相较于 Gzip 压缩率更高、性能也非常好,再基于 Chrome 的市场支配地位,Brotli 便得到迅速普及。不过要注意,它仅适用于 HTTPS。

运维 --- Nginx高级功能介绍_第3张图片

  1. 下载 Brotli 的源码;
yum install git && cd /usr/local/src

git clone https://github.com/google/ngx_brotli.git
pushd ngx_brotli
git submodule update --init
popd
  1. 执行命令 nginx -V,configure arguments 即为现有的参数;
nginx version: nginx/1.16.1
built by gcc 4.8.5 20150623 (Red Hat 4.8.5-36) (GCC) 
built with OpenSSL 1.1.1c  28 May 2019
TLS SNI support enabled
configure arguments: --with-http_ssl_module --with-http_v2_module --with-http_sub_module --with-openssl=../openssl-1.1.1c
  1. 追加参数 --add-module=…/ngx_brotli,重新编译 Nginx。本系列文中 Nginx 的源码目录是 /usr/local/src/nginx-1.16.1;
cd nginx-1.16.1
./configure \
--with-http_ssl_module \
--with-http_v2_module \
--with-http_sub_module \
--with-openssl=../openssl-1.1.1c \
--add-module=../ngx_brotli
make && make install

如需执行平滑升级 (热部署),make 之后请不要 make install;

mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
cp objs/nginx /usr/local/nginx/sbin/nginx
make upgrade
  1. 接着修改 nginx.conf (缺省值就够用);
http {
    ...
    gzip on;
    brotli on;
    
    # gzip_types text/css text/javascript application/rss+xml;
    # brotli_types text/css text/javascript application/rss+xml;
    ...
}
  1. 重载 Nginx,可借由 Brotli Test 等工具检验效果。
nginx -t && nginx -s reload

你可能感兴趣的:(运维,nginx,运维)