本文主要是介绍了 NGINX Plus 的相关功能, 横跨了 NGINX Plus R5/R6/R7/R9 等各个不同版本的更新
什么是 NGINX Plus?
顾名思义, 就是 Nginx 的加强版或者扩展版我们知道 Nginx 是开源的免费的, 但是 NGINX Plus 的很多功能就需要收费了 Nginx Plus 可以作为一个负载均衡器, 一个 web 服务器, 还可以作为一个内容缓存既然是 Nginx 的加强版, 那无疑功能会比 Nginx 更加强大 NGINX Plus 在开源 Nginx 已有的功能基础上, 提供了许多适合生产环境的专有功能, 包括 session 一致性实时更新 API 配置有效的健康检查等
NGINX Plus 的版本更新
NGINX Plus R5 和更新的版本可以支持基于 TCP 应用的负载均衡 (比如 MySQL) 这就不仅仅限制于 Http 的负载均衡, 而是大大扩充了 Nginx 作为负载均衡器的作用范围 R6 中 TCP 负载均衡功能得到很大的扩充, 加入了健康检查动态更新配置 SSL 终端等等到了 R7,TCP 负载均衡功能就基本和 Http 负载均衡差不多了 z 再到了 R9, 就可以支持 UDP 了通过这些更新, NGINX Plus 远远超过了 web 应用的层面, 成为了一款意义更为广泛的负载均衡器毕竟协议是基础层面的东西, 支持的协议越多, 应用面也越广从最初的 Http/SMTP 到 TCP 再到 UDP,NGINX Plus 一步步的变得越来越强大
开源 Nginx 和 NGINX Plus 都支持 HTTP, TCP, 和 UDP 应用的负载均衡但 NGINX Plus 提供了一些企业级别的功能, 这些功能是收费的, 包括 session 一致性, 健康检查, 动态更新配置等
HTTP 负载均衡
NGINX Plus 对 Http 负载均衡做了很多功能优化, 诸如 HTTP 升级长连接优化内容压缩和响应缓存等在 NGINX Plus 中 Http 负载均衡的实现也非常简单:
- http {
- upstream my_upstream {
- server server1.example.com;
- server server2.example.com;
- }
- server {
- listen 80;
- location / {
- proxy_set_header Host $host;
- proxy_pass http://my_upstream;
- }
- }
- }
可以通过 proxy_set_header 指令来设置 Host, 而 proxy_pass 将请求转发到上游的 my_upstream 中
Http 长连接(HTTP Keepalives)
HTTP 协议是用的底层 TCP 协议来传输请求, 接收响应的 HTTP1.1 支持 TCP 的长连接或者重利用, 以免反复的创建和销毁 TCP 连接所带来的开销
我们看看 Http 的长连接:
NGINX 是一个完全意义的反向代理, 在长连接上也毫不含糊它管理所以来从客户端到 Nginx 的长连接, 同样也会管理从 Nginx 到上游服务器的长连接, 二者是完全独立的
Nginx 管理的长连接:
NGINX 将连接上游服务器的空闲连接做了缓存, 并不直接关掉它们如果有请求过来, NGINX 先从缓存的活跃连接中去拿一个使用, 而不是立马创建一个新的, 如果缓存为空那么 NGINX 再去新建一个连接这种操作这降低了 Nginx 和上游服务器之间的延迟并减少的临时端口的利用率, 所以 NGINX 能处理大的并发这种技术加上别的负载均衡技术, 有时候可以被称为连接池, 或者连接复用
为了配置闲置长连接缓存, 你需要指定几个指令:
- proxy_http_version,proxy_set_header,keepalive
- server {
- listen 80;
- location / {
- proxy_pass http://backend;
- proxy_http_version 1.1; # 只有 Http1.1/2.0 才能支持长连接
- proxy_set_header Connection "";
- }
- }
- upstream backend {
- server webserver1;
- server webserver2;
- # maintain a maximum of 20 idle connections to each upstream server
- keepalive 20; # 闲置长连接缓存时间为 20
- }
TCP 和 UDP 的负载均衡
作为对 Http 协议的扩展, NGINX Plus 可以直接支持基于 TCP 和 UDP 协议的应用基于 TCP 的如 MySQL, 支持 UDP 的如 DNS 和 RADIUS 应用对于 TCP 请求来说, NGINX Plus 接收了客户端的 TCP 请求, 然后再创建一个 TCP 请求对上游服务器发起访问
- stream {
- upstream my_upstream {
- server server1.example.com:1234;
- server server2.example.com:2345;
- }
- server {
- listen 1123 [udp];
- proxy_pass my_upstream; #注意这里没有 http:// 了
- }
- }
对 TCP 请求的支持出现在 NGINX Plus R5,R6 和 R7 版本主要是在优化这个功能, 到 R7 时 TCP 请求的负载均衡已经强大到足够媲美 Http 负载均衡了, 到了 R9, 则可以支持 UDP 了这里先有个印象, 后面会更加详细介绍 TCP 负载均衡功能
连接数限制(Connection Limiting)
你还可以为负载均衡做连接数量限制这里说的连接是指 NGINX Plus 发给上游服务器的 Http/TCP/UDP 请求连接 (对于 UDP 则是会话) 有了连接数限制的功能, 当上游服务器的 Http/TCP 连接数量, 或者 UDP 的会话数量超过一定的值时, NGINX Plus 就不再创建新的连接或者会话客户端多出的请求连接可以被放进队列等候, 也可以不被处理可以通过 max_conns,queue 指令来实现这一点:
- upstream backend {
- zone backends 64k;
- queue 750 timeout=30s;
- server webserver1 max_conns=250;
- server webserver2 max_conns=150;
- }
server 指令表示 webserver1 最多承载 250 个连接而 webserver2 最多 150 个, 多出来的可以放在队列 queue 当中等候在队列 queue 中等候的连接数量和等候时间也是有限制的当 webserver1 和 webserver2 连接数降低到各自最大连接数以下时, 等候在队列 queue 中的连接随时就补上去
queue 750 timeout=30s
表示总共可以有 750 个连接排队等候, 每个连接等候 30s
Limiting connections 是十分有用的, 可以为客户端提供可持续可预见的服务不必因为某台 server 负载过大导致挂掉一般来说一台 server 大概能承载多少负荷是可以通过某些手段测试出来的, 因此把这个可承受的上线作为 max_conns 指令的值便可以保证 server 的相对安全
Least Time 负载均衡算法
在 NGINX Plus R6 中增加了一种新的均衡算法 Least Time, 将相应时间也考虑进去, 算得上对 Least Connections 的扩展
这种算法同时考虑当前连接数和连接池里各个节点的平均响应时间目的是使得当前请求选择当下响应更快连接更少的服务器, 而不是选择响应更慢连接更多的
当连接池的各个服务器节点有着明显不同的响应延时时, 这种算法就要优于其他的几种 (round-robin/ip-hash/lease connections) 一个典型的应用场景是, 如果有两个分布在不同的地域的数据中心, 那么本地的数据中心就要比异地的数据中心延时要少得多, 这个时候就不能仅仅考虑当下连接数了, 这个响应的延时也要被计入考量 Least Time 算法就更倾向于选择本地的, 当然这只是更倾向于的问题, 并不能代替 Nginx 最基本的错误转移功能, 哪怕本地的数据中心响应再快, 如果它挂掉了 Nginx Plus 也能马上切换到远端数据中心
最少时间可以有两种计算方式, 一种是从请求发出到上流服务器接返回响应头部算的时间, 另一种是从请求发出到接收到全部请求体的时间, 分别以 header_time 和 response_time 来表示
Session 一致性(Session Persistence)
Session 一致性问题除了可以通过指定 ip-hash 的均衡算法来实现, 还有更为通用的实现方式, 这是在 NGINX Plus 中实现的
NGINX Plus 可以识别用户 Session, 从而能够鉴别不同的客户端, 并且可以将来自同一个客户端的请求发往同一个上游服务器这在当应用保存了用户状态的情况下非常有用, 可以避免负载均衡器按照某个算法将请求发到别的服务器上去另外, 在共享用户信息的集群服务器这种方式也非常有用
session 一致性的要求同一个客户端每次的请求都选择同一个服务器, 而负载均衡要求我们利用一种算法去服务器连接池里面去选择下一个, 那么这两种矛盾的方式可以共存么? 可以的, NGINX Plus 按照如下的步骤决策到底选用哪一种:
如果 request 匹配某个 Session 一致性的规则, 那么根据这个规则选取上游服务器;
如果没有匹配上或者匹配的服务器无法使用, 那么使用负载均衡算法选择上游服务器;
为了能保证 session 一致性, Nginx Plus 提供了 sticky cookie,sticky learn 和 sticky route 几种规则
sticky cookie 规则
对于 sticky cookie 规则, 当客户端的第一个请求选择了某个上游服务器, 并从这个上游服务器返回响应时, NGINX Plus 为这个响应添加一个 session cookie, 用来鉴别这个上游服务器当后面的请求再过来时, NGINX Plus 取出这个 cookie, 分析是哪一台服务器, 再把请求发往这台相同的服务器
使用指令 sticky cookie, 配置如下:
- upstream backend {
- server webserver1;
- server webserver2;
- sticky cookie srv_id expires=1h domain=.example.com path=/;
- }
cookie 的名字就叫 srv_id, 用来记住是哪一个 server; 过期时间 1h,domain 为. example.com;path 为 /
NGINX Plus 在第一次响应中, 插入一个名称为 srv_id 的 cookie, 用来记住这第一次请求是发个哪个上游的, 后面的请求带上这个 cookie, 同样再被 NGINX Plus 甄别一下, 再发往同一个的服务器这样就能保证 session 的一致了
sticky route 规则
和 sticky cookie 规则类似, 只不过记住上游服务器的方式不同而已
在客户端发起第一次请求时, 接收它的服务器为其分配一个 route, 此后这个客户端发起的所有请求都要带上这个 route 信息, 或者在 cookie 中或者在 uri 中然后和 server 指令中的 route 参数做对比, 决定选取哪个 server 如果指定的服务器无法处理, 那交给负载均衡算法去选择下一个服务器
- map $cookie_jsessionid $route_cookie {
- ~.+\.(?P<route>\w+)$ $route;
- }
- map $request_uri $route_uri {
- ~jsessionid=.+\.(?P<route>\w+)$ $route;
- }
- upstream backend {
- server backend1.example.com route=a;
- server backend2.example.com route=b;
- # select first non-empty variable; it should contain either 'a' or 'b'
- sticky route $route_cookie $route_uri;
- }
在这里, route 在 JSESSIONID 的 cookie 中选择, 如其包含 a 那么选择服务器 backend1; 如其包含 b 则选择 backend2, 如果都不包含那么在 $request_uri 中再做类似的选择, 以此类推
不管是选哪种方式保持 session 一致, 如果选择出的 server 无法使用, 那么将会按照负载均衡算法 (如 round-robin) 在服务器列表中的选择下一台 server 继续处理
实时健康检查(Active Health Checks)
前面提到过, Nginx 有两大功能: 一个是扩展, 增加更多的 server 以满足更大的并发; 二是检测失效 server, 以便及时排除那么, 如何定义一个失效 server(failed server)就变得非常重要这一节就是来讨论这个问题这是 NGINX Plus 才有的功能, 并且是收费的
开源版本 NGINX 可以提供简单的健康检查, 并且可以自动做故障转移但是如何定义一个上游 server 失效开源 NGINX 却做的很简单 NGINX Plus 为此提供了一个可以自定义的综合式的评判标准, 除此之外 NGINX Plus 还可以平缓的添加新的服务器节点到集群当中这个功能使得 NGINX Plus 可能甄别更为多元化的服务器错误, 十分有效的增加了 HTTP/TCP/UDP 应用的可靠性
这里要用到的指令有: health_check,match 等指令:
- upstream my_upstream {
- zone my_upstream 64k;
- server server1.example.com slow_start=30s;
- }
- server {
- # ...
- location /health {
- internal;
- health_check interval=5s uri=/test.php match=statusok;
- proxy_set_header HOST www.example.com;
- proxy_pass http://my_upstream
- }
- }
- match statusok {
- # 在 / test.php 做健康检查
- status 200;
- header Content-Type = text/html;
- body ~ "Server[0-9]+ is alive";
- }
health_check 中 interval=5s 表示每隔 5s 检测一次; uri=/test.php 表示在 / test.php 里进行健康检查, NGINX Plus 自动发起 uri 的请求, uri 可以自定义, 你在里面具体执行检查的逻辑, 比如 mysql/redis 这些是否正常, 然后作出一定的响应; 然后在 match 指令中, 就通过一些规则来匹配 / test.php 的响应 / test.php 的响应可以包括 status,header,body 这些, 供后面这些指令做匹配全部检查通过, 就算健康, server 被标记为活跃; 如果一项匹配未通过, 比如
Content-Type = text/json
或者 status = 201 那都算检测失败, server 不健康, 被标记为不活跃
使用 DNS 发现新的服务
Nginx Plus 一启动就会进行 DNS 解析并且自动永久缓存解析出的域名和 IP, 但是某些情形下需要重新解析下, 这时候可以使用下面的指令来实现:
- resolver 127.0.0.11 valid=10s;
- upstream service1 {
- zone service1 64k;
- server www.example.com service=http resolve;
- }
127.0.0.11 是默认的 DNS 服务器的地址, 此例中 NGINX Plus 每 10s 中 DNS 服务器发起一次重新解析的请求
访问控制(Access Controls)
NGINX Plus Release 7 主要给增加了 TCP 负载均衡的安全性比如 Access Controls 和 DDoS 保护
你现在可以允许或者拒绝对做反向代理的或者做负载均衡的 TCP 服务器的访问, 仅仅通过配置简单的 IP 或者一个 IP 范文就能实现:
- server {
- # ...
- proxy_set_header Host www.example.cn;
- proxy_pass http://test;
- deny 72.46.166.10;
- deny 73.46.156.0/24;
- allow all;
- }
第一个 deny 指令拒绝一个 IP 的访问, 第二个拒绝一个 IP 范围, 除去这两个剩下的都是被允许访问的被拒绝访问的 IP, 会被返回 403 错误
连接数限制(Connection Limiting)
使用 NGINX Plus R7 你可以限制客户端发往由 NGINX Plus 代理的 TCP 应用的请求数量, 防止对 TCP 的请求数量过多在你的应用中, 可能一部分的比另一部分要慢一些比如说, 请求你的应用的某块, 将会产生大量的 MySQL 请求, 或者 fork 出一大堆的 work 进程那么攻击者将会利用这点产生成千上万个请求, 致使你的服务器负载过重而瘫痪
但是有了连接数限制功能, 你可以通过配置
limit_conn my_limit_conn
指令限制同一个客户端 (IP) 所能发起的最大请求数, 以此将上述的攻击风险降到最低
- stream {
- limit_conn_zone $binary_remote_addr zone=my_limit_conn:10m;
- # ...
- server {
- limit_conn my_limit_conn 1;
- # ...
- }
- }
这条指令限定了每个 IP 同时只能有一个连接
带宽限制(Bandwidth Limiting)
R7 还新增了一项功能限制每个连接的上传和下载的最大带宽
- server {
- # ...
- proxy_download_rate 100k;
- proxy_upload_rate 50k;
- }
有了这个配置, 客户端最多只能以 100kbytes/s 的速度下载, 以 50kbytes/s 的速度上传因为客户端可以开多个连接, 因此如果要限制总的上传 / 下载速度, 同时还得限制下单个客户端的连接数
支持无缓冲的上传
这是在 R6 中增加的功能你可以在 R6 和以后的版本中使用无缓冲的上传, 意味 Nginx Plus 可以通过更大的 Http 请求比如上传无缓冲的上传可以在这些请求一过来便进行上传, 而不是像之前那样先是缓冲所有的上传内容, 再将其转发给你上游服务器
默认情况下, Nginx 在上传时, 接收到数据时会先放进缓冲区进行缓冲, 以避免将资源和基于 worker 进程的后端脚本绑定, 但是针对事件驱动的后端语言如 Node.js, 缓冲是几乎没有必要的这个修改改进了服务器对上传大文件的响应性, 因为应用可以一接收到数据就马上对做出响应, 使得上传进度条变成实时的和准确的同样, 这个改进也减少了磁盘 I/O
SSL/TLS 优化
在 R6 中, 可以在和上游的 HTTPS 或者 uwSGI 服务器打交道时为客户端提供一个证书这大大提高了安全性, 尤其是在和不受保护网络上的安全服务进行通信的时候 R6 支持 IMAP, POP3, 和 SMTP 的 SSL/TLS 客户端认证
缓存优化
proxy_cache 指令可以支持变量了, 这个简单的改进以为着你可以定义几个基于磁盘的缓存, 并且根据请求数据做自由的选择当你打算创建巨大的内容缓存, 并且将其保存到不同的磁盘时是非常有用的
API 功能
upstreem 模块的一些指令, 不光可以通过手动去修改, 还可以通过 restful api 的方式去修改, 并且马上自动更新有了这个功能, NGINX Plus 的一些功能, 你都可以通过 API 的方式去改变应用性得到很大提升当然这也是收费的:
- upstream backend {
- zone backends 64k;
- server 10.10.10.2:220 max_conns=250;
- server 10.10.10.4:220 max_conns=150;
- }
- server {
- listen 80;
- server_name www.example.org;
- location /api {
- api write=on;
- }
- }
有了 API, 你就可以使用 curl 工具来动态修改配置了, 比如用 POST 命令来增加一个集群的节点:
$ curl -iX POST -d '{"server":"192.168.78.66:80","weight":"200","max_conns":"150"}' http://localhost:80/api/1/http/upstreams/backend/servers/
相当于添加了一个这样的配置:
- upstream backend {
- zone backends 64k;
- server 10.10.10.2:220 max_conns=250;
- server 10.10.10.4:220 max_conns=150;
- #此处是通过 api 添加的
- server 192.168.78.66:80 weight=200 max_conns=150;
- }
如果需要修改一个节点配置, 你可以用服务器节点在连接池中的自然顺序 (从 0 开始) 作为它们各自唯一的 ID, 然后使用 PATCH/DELETE 方法去操作它们:
$ curl -iX PATCH -d '{"server":"192.168.78.55:80","weight":"500","max_conns":"350"}' http://localhost:80/api/1/http/upstreams/backend/servers/2
这条命令是修改以上连接池中的第三个
server 192.168.78.66:80 max_conns=200;
为:
server 192.168.78.55:80 weight=500 max_conns=350;
如果要返回所有的节点信息, 可以使用:
$ curl -s http://localhost:80/api/1/http/upstreams/backend/servers/
返回的是一个 JSON 字符串
- {
- "backup": false,
- "down": false,
- "fail_timeout": "10s",
- "id": 0,
- "max_conns": 250,
- "max_fails": 1,
- "route": "","server":"10.10.10.2:220","slow_start":"0s","weight": 1
- },
- {
- "backup": false,
- "down": false,
- "fail_timeout": "10s",
- "id": 1,
- "max_conns": 150,
- "max_fails": 1,
- "route": "","server":"10.10.10.4:220","slow_start":"0s","weight": 1
- },
- {
- "backup": false,
- "down": false,
- "fail_timeout": "10s",
- "id": 2,
- "max_conns": 200,
- "max_fails": 1,
- "route": "","server":"192.168.78.66:80","slow_start":"0s","weight": 200
- }
- }
配置的最佳实践
为不同个应用配置创建各自的目录和文件, 并用 include 指令再合并到一起是个非常好的习惯标准的 NGINX Plus 配置是将各个应用的配置文件放到各自的 conf.d directory 目录下:
- http {
- include /etc/nginx/conf.d/*.conf;
- }
- stream {
- include /etc/nginx/stream.d/*.conf;
- }
http 和 stream 模块的各自分属不同的目录, 而在 http 下的都是 http 请求的配置, stream 都是 TCP/UDP 请求的配置没有统一的标准, 主要是看开发者自己能便于识别和修改
来源: https://www.cnblogs.com/minirice/p/8570157.html