Nginx 反向代理与负载均衡
2013-05-13 by dongnan
开始之前
什么时候需要使用反向代理?
我这里有个例子,早期的WEB服务器使用的是HTTP/1.0
协议,是不支持keep-alive
功能的,
为了解决这个问题当时主流的解决方案是使用nginx
做反向代理(支持HTTP/1.1
当时优势之一),为客户端浏览器链接HTTP/1.1
,
并为后端WEB服务器链接HTTP/1.0
,经过nginx
代理后就可以愉快的使用keep-alive
功能了。
为什么HTTP/1.1
协议中的keep-alive
功能这么重要(HTTP/2
笑而不语),这是因为HTTP/1.0
开销很大而速度又很慢,
HTTP/1.0
为每一个请求/响应都打开一个TCP
连接,打开一个TCP
连接需要多次往返消息传递因此速度较慢。
HTTP/1.1
引入了持久连接(俗称长连接)的概念: 底层的TCP
连接可以通过Connection
头部来被部分控制。
使得对于同一个服务器的请求可以继续在该连接上完成后续请求。
(一图胜千言)
环境描述
八卦完HTTP/1.0
协议,让我们回到nginx
反向代理上,由于时间太过久远找不到当时的nginx
代理配置文件,
下面的示例是以一个python
项目来讲解nginx
如何配置反向代理。两个案例功能类似都是反向代理后端服务器,所以能够说明问题。
说明下项目环境,这个python
项目是基于django
框架开发的,django
框架内置web服务可以直接运行项目,
不过django
官方明确内置的web服务适用于开发环境,不建议直接用于生产环境,
更不用说nginx
静态文件处理能力是"这些框架"内置web服务所望尘莫及的。所以这里增加了nginx
用于反向代理django
内置web服务,拓扑图如下。
配置文件
cat /etc/nginx/conf.d/default.conf
server{
#... 其它配置项省略
location ^~ /static {
alias /django-demo/static/;
expires 30d;
}
location / {
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header Host $host;
proxy_pass http://unix:/var/run/django.socket;
#proxy_pass http://upstream_server:8000;
}
#... 其它配置项省略
}
示例配置文件中,使用proxy_set_header
指令设置Http
头部,最后使用proxy_pass
指令转发请求到后端服务器。
X-Forwarded-For
,用来记录代理服务器的地址,每经过一个代理该字段会追加上一个记录。例如:6.6.6.6, 8.8.8.8
。X-Real-IP
,同样用来记录代理服务器的地址,但是和上面的不同它不把记录追加到尾部,而是直接替换掉。Host
,为后端服务器重新设定Host主机头,$host
变量的值在请求包含"Host"请求头时为"Host"字段的值,在请求未携带"Host"请求头时为虚拟主机的主域名。remote_addr
,表示客户端地址,注意如果存在代理服务器,则是最后的代理服务器地址,而非真正的客户端地址。proxy_pass
,经过调整头部信息后,将请求转发到后端的服务器。
Nginx 指令
proxy_set_header 指令
语法: proxy_set_header field value
默认值:
proxy_set_header Host $proxy_host;
proxy_set_header Connection close;
使用字段: http, server, location字段。
功能: 这个指令允许重新定义或者添加发往后端服务器的请求头。
value可以包含文本、变量或者它们的组合。
proxy_pass 指令
语法: proxy_pass URL
默认值: none
使用字段: location, location中的if字段。
功能: 这个指令设置被代理服务器的地址和被映射的URI。
地址可以使用主机名或IP加端口号的形式。
proxy_pass http://localhost:8000/uri/;
或者unix socket,路径在unix关键字的后面指定,位于两个冒号之间。
proxy_pass http://unix:/tmp/backend.socket:/uri/;
负载均衡模块
这个模块为后端的服务器提供简单的负载均衡: 轮询(round-robin
)和连接IP(client IP
)。
nginx部分配置文件:
upstream backend {
server backend1.example.com weight=5;
server backend2.example.com:8080;
server unix:/tmp/backend3;
}
server {
location / {
proxy_pass http://backend;
}
}
ip_hash 指令
语法: ip_hash
默认值: none
使用字段: upstream
这个指令将基于客户端连接的IP地址来分发请求。
哈希的关键字是客户端的C类网络地址,这个功能将保证这个客户端请求总是被转发到一台服务器上,
但是如果这台服务器不可用,那么请求将转发到另外的服务器上,这将保证某个客户端有很大概率总是连接到一台服务器。
无法将权重(weight)与ip_hash联合使用来分发连接。如果有某台服务器不可用,你必须标记其为“down”.
nginx部分配置文件:
upstream backend {
ip_hash;
server backend1.example.com;
server backend2.example.com;
server backend3.example.com down;
server backend4.example.com;
}
server 指令
语法: server name [parameters]
默认值: none
使用字段: upstream
指定后端服务器的名称和一些参数,可以使用域名,IP,端口,或者unix socket。如果指定为域名,则首先将其解析为IP。
weight = NUMBER - 设置服务器权重,默认为1。
max_fails = NUMBER - 在一定时间内(这个时间在fail_timeout参数中设置)检查这个服务器是否可用时产生的最多失败请求数,默认为1,将其设置为0可以关闭检查,这些错误在proxy_next_upstream或fastcgi_next_upstream(404错误不会使max_fails增加)中定义。
fail_timeout = TIME - 在这个时间内产生了max_fails所设置大小的失败尝试连接请求后这个服务器可能不可用,同样它指定了服务器不可用的时间(在下一次尝试连接请求发起之前),默认为10秒,fail_timeout与前端响应时间没有直接关系,不过可以使用proxy_connect_timeout和proxy_read_timeout来控制。
down - 标记服务器处于离线状态,通常和ip_hash一起使用。
backup - (0.6.7或更高)如果所有的非备份服务器都宕机或繁忙,则使用本服务器(无法和ip_hash指令搭配使用)。
nginx部分配置文件:
upstream backend {
server backend1.example.com weight=5;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
注意:如果你只使用一台上游服务器,nginx将设置一个内置变量为1
,即max_fails
和fail_timeout
参数不会被处理。建议使用多台上游服务器。
upstream 指令
语法: upstream name { … }
默认值: none
使用字段: http
这个字段设置一群服务器,可以将这个字段放在proxy_pass和fastcgi_pass指令中作为一个单独的实体,
它们可以是监听不同端口的服务器,并且也可以是同时监听TCP和Unix socket的服务器。
服务器可以指定不同的权重,默认为1。
nginx部分配置文件:
upstream backend {
server backend1.example.com weight=5;
server 127.0.0.1:8080 max_fails=3 fail_timeout=30s;
server unix:/tmp/backend3;
}
请求将按照轮询的方式分发到后端服务器,但同时也会考虑权重。
在上面的例子中如果每次发生7个请求,5个请求将被发送到backend1.example.com
,其他两台将分别得到一个请求。
如果有一台服务器不可用,那么请求将被转发到下一台服务器,直到所有的服务器检查都通过。 如果所有的服务器都无法通过检查,那么将返回给客户端最后一台工作的服务器产生的结果。
参考
- http://nginx.org/en/docs/http/load_balancing.html
- 如何中使用 Gunicorn 管理 Django 应用?