Nginx 以高并发、低消耗而闻名,这个特点使其很适合作为一个负载均衡器 (Load Balancer),有策略地分发请求给不同的后端服务器。避免单点故障之余,亦增强整个系统的可用性,简单说不容易宕机。负载均衡是反代的其中一个用途。本文介绍 Nginx 常用的几个负载均衡策略。
⚠️ 假设有一组名为 backend 的 upstream,那么调用方式是 proxy_pass http://backend。
在此例中,平时使用 www.example.com 提供服务,bad.example.com 则暂时下线,bak.example.com 作为发生故障时兜底的一台机器。又称故障转移 (failover)。
upstream backend { server www.example.com; server bad.example.com down; server bak.example.com backup; }
介个是经(mò)典(rèn)的负载均衡算法。理论上会跳过不能用的 server,但预设的超时太长,实务上可改成 proxy_connect_timeout 1、proxy_read_timeout 1 和 proxy_send_timeout 1。
按照时间先后来分发请求。在此例中,顾名思义第 1 个 request 走 srv1.example.com,第 2 个走 srv2.example.com,第 3 个走 srv3.example.com,第 4 个走 srv1.example.com……以此类推。
upstream backend { server srv1.example.com; server srv2.example.com; server srv3.example.com; }
因应忙闲不均、负载不均的问题而生。在此例中,Nginx 将试图减轻已经很忙的服务器的压力,把新请求分发至没那么忙的主机。
若对动态负载均衡的算法感兴趣,还有一插件 nginx-upstream-fair 可以实现最快响应法。
upstream backend { least_conn; server srv1.example.com; server srv2.example.com; server srv3.example.com; }
Session 用于跟踪用户操作,涉及身份认证时 (用户系统) 往往能派上用场,而通常情况下会话信息保存在单机上,这就使得有些需求得在同一台服务器上完成,不能换到其他的服务器 (姑且不谈分布式 Session)。
Sticky Sessions (粘滞会话) 将保证一个用户对应一台服务器,从而解决 Session 不一致的问题 (通常表现为无法登录)。出于容灾理由,不建议大量使用。
upstream backend { ip_hash; server srv1.example.com; server srv2.example.com; server srv3.example.com; }
集群中难免有算力不均的情况。不同服务器处理速度有快有慢,不问性能的齐头式平等,很可能触发木桶原理的副作用——让配置最差的那个服务器决定整个服务器集群的性能。可跑一次 bench.sh,再依据测试结果给服务器排个序。
在此例中,每 10 个新请求有 6 个被分配至 srv1.example.com,然后各有 2 个被分配至 srv2.example.com 和 srv3.example.com。此例演示的轮询法,事实上最小连接和源地址哈希法也可以分配权重。
upstream backend { server srv1.example.com weight=3; server srv2.example.com; server srv3.example.com; }
根据 Nginx 的文档,max_fails 缺省值是 1,fail_timeout 缺省值是 10s。
在此例中,www.example.com 的健康检查会被关闭,一直都标记为可用;www2.example.com 连不上时先重试 2 次,如果还不行就退出服务,下线 1 天,方便运维人员排障。
upstream backend { server www.example.com max_fails=0; server www2.example.com max_fails=2 fail_timeout=1d; }
根据 Nginx 的文档,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; ... }