正向代理与反向代理
正向代理
反向代理
Nginx 的正向代理
Nginx 正向代理配置
Nginx 正向代理使用场景并不多见。
需求场景 1:
如果在机房中,只有一台机器可以联网,其他机器只有内网,内网的机器想用使用 yum 安装软件包,在能联网的机器上配置一个正向代理即可。
Nginx 正向代理配置文件
server {
listen 80 default_server;
resolver 119.29.29.29;
location / {
proxy_pass http://$host$request_uri;
}
}
Nginx 正向代理配置执行说明
- resolver
语法:resolver address;
address 为 DNS 服务器的地址,国内通用的 DNS 119.29.29.29 为 dnspod 公司提供。 国际通用 DNS 8.8.8.8 或者 8.8.4.4 为 google 提供。
其他可以参考 http://dns.lisect.com/
示例:resolver 119.29.29.29;
- default_server
之所以要设置为默认虚拟主机,是因为这样就不用设置 server_name 了,任何域名解析过来都可以正常访问。
- proxy_pass
该指令用来设置要代理的目标 url,正向代理服务器设置就保持该固定值即可。关于该指令的详细解释在反向代理配置中。
Nginx 的反向代理
Nginx 反向代理配置
Nginx 反向代理在生产环境中使用很多的。
场景 1:
域名没有备案,可以把域名解析到香港一台云主机上,在香港云主机做个代理,而网站数据是在大陆的服务器上。
示例 1:
server
{
listen 80;
server_name a.com;
location /
{
proxy_pass http://123.23.13.11/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
配置说明
- proxy_pass
在正向代理中,已经使用过该指令。
格式很简单: proxy_pass URL;
其中 URL 包含:传输协议(http://, https://等)、主机名(域名或者 IP:PORT)、uri。
示例如下:
proxy_pass http://www.a.com/;
proxy_pass http://192.168.200.101:8080/uri;
proxy_pass unix:/tmp/www.sock;
对于 proxy_pass 的配置有几种情况需要注意。
示例 2:
location /a/
{
proxy_pass http://192.168.1.10;
...
}
示例 3:
location /a/
{
proxy_pass http://192.168.1.10/;
...
}
示例 4:
location /a/
{
proxy_pass http://192.168.1.10/linux/;
...
}
示例 5:
location /a/
{
proxy_pass http://192.168.1.10/linux;
...
}
假设 server_name 为 www.a.com
当请求 http://www.a.com/a/a.html 的时候,以上示例 2-5 分别访问的结果是
示例2:http://192.168.1.10/a/a.html
示例3:http://192.168.1.10/a.html
示例4:http://192.168.1.10/linux/a.html
示例5:http://192.168.1.10/linuxa.html
- proxy_set_header
proxy_set_header 用来设定被代理服务器接收到的 header 信息。
语法:proxy_set_header field value;
field 为要更改的项目,也可以理解为变量的名字,比如 host
value 为变量的值
如果不设置 proxy_set_header,则默认 host 的值为 proxy_pass 后面跟的那个域名或者 IP(一般写 IP),
比如示例 4,请求到后端的服务器上时,完整请求 uri 为:http://192.168.1.10/linux/a.html
如果设置 proxy_set_header,如 proxy_set_header host $host;
比如示例 4,请求到后端的服务器完整 uri 为:http://www.a.com/linux/a.html
proxy_set_header X-Real-IP $remote_addr; 和 proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
用来设置被代理端接收到的远程客户端 IP,如果不设置,则 header 信息中并不会透传远程真实客户端的 IP 地址。
可以用如下示例来测试:
示例 6(被代理端)
server{
listen 8080;
server_name www.a.com;
root /tmp/123.com_8080;
index index.html;
location /linux/ {
echo "$host";
echo $remote_addr;
echo $proxy_add_x_forwarded_for;
}
}
示例 7(代理服务器上)
server {
listen 80;
server_name www.a.com;
location /a/
{
proxy_pass http://192.168.1.10:8080/linux/;
proxy_set_header host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
- proxy_redirect
该指令用来修改被代理服务器返回的响应头中的 Location 头域和 refresh 头域。
语法结构为:
proxy_redirect redirect replacement;
proxy_redirect default;
proxy_redirect off;
示例 8:
server {
listen 80;
server_name www.a.com;
index index.html;
location /
{
proxy_pass http://127.0.0.1:8080;
proxy_set_header host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
当请求的链接为 http://www.a.com/a
结果会返回 301,定向到了 http://www.a.com:8080/a/
注意:返回 301 有几个先决条件
1. location 后面必须是 /;
2. proxy_pass 后面的 URL 不能加 uri,只能是 IP 或者 IP:port 结尾,并不能以 / 结尾;
3. 访问的 uri 必须是一个真实存在的目录,如,这里的 a 必须是存在的
4. 访问的时候,不能以 / 结尾,只能是 www.a.com/a
虽然,这 4 个条件挺苛刻,但确实会遇到类似的请求。解决方法是,加一行 proxy_redirect http://$host:8080/ /;
示例 9:
server {
listen 80;
server_name www.a.com;
index index.html;
location /
{
proxy_pass http://127.0.0.1:8080;
proxy_set_header host $host;
proxy_redirect http://$host:8080/ /;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
Nginx 反向代理中的 proxy_buffering 和 proxy_cache
- 两个都是 nginx 代理中内存设置相关的参数。
proxy_buffering 设置
proxy_buffering 主要是实现被代理服务器的数据和客户端的请求异步。
为了方便理解,我们定义三个角色,A 为客户端,B 为代理服务器,C 为被代理服务器。
当 proxy_buffering 开启,A 发起请求到 B,B 再到 C,C 反馈的数据先到 B 的 buffer 上,
然后 B 会根据 proxy_busy_buffer_size 来决定什么时候开始把数据传输给 A。在此过程中,如果所有的 buffer 被写满,
数据将会写入到 temp_file 中。
相反,如果 proxy_buffering 关闭,C 反馈的数据实时地通过 B 传输给 A。
以下配置,都是针对每一个 http 请求的。
1. proxy_buffering on;
该参数设置是否开启 proxy 的 buffer 功能,参数的值为 on 或者 off。
如果这个设置为 off,那么 proxy_buffers 和 proxy_busy_buffers_size 这两个指令将会失效。
但是无论 proxy_buffering 是否开启,proxy_buffer_size 都是生效的
2. proxy_buffer_size 4k;
该参数用来设置一个特殊的 buffer 大小的。
从被代理服务器(C)上获取到的第一部分响应数据内容到代理服务器(B)上,通常是 header,就存到了这个 buffer 中。
如果该参数设置太小,会出现 502 错误码,这是因为这部分 buffer 不够存储 header 信息。建议设置为 4k。
3. proxy_buffers 8 4k;
这个参数设置存储被代理服务器上的数据所占用的 buffer 的个数和每个 buffer 的大小。
所有 buffer 的大小为这两个数字的乘积。
4. proxy_busy_buffer_size 16k;
在所有的 buffer 里,我们需要规定一部分 buffer 把自己存的数据传给 A,这部分 buffer 就叫做 busy_buffer。
proxy_busy_buffer_size 参数用来设置处于 busy 状态的 buffer 有多大。
对于 B 上 buffer 里的数据何时传输给 A,我个人的理解是这样的:
1)如果完整数据大小小于 busy_buffer 大小,当数据传输完成后,马上传给 A;
2)如果完整数据大小不少于 busy_buffer 大小,则装满 busy_buffer 后,马上传给 A;
5. proxy_temp_path
语法:proxy_temp_path path [level1 level2 level3]
定义 proxy 的临时文件存在目录以及目录的层级。
例:proxy_temp_path /usr/local/nginx/proxy_temp 1 2;
其中 /usr/local/nginx/proxy_temp 为临时文件所在目录,1 表示层级 1 的目录名为一个 数字(0-9),2 表示层级 2 目录名为 2 个 数字(00-99)
6. proxy_max_temp_file_size
设置临时文件的总大小,例如 proxy_max_temp_file_size 100M;
7. proxy_temp_file_wirte_size
设置同时写入临时文件的数据量的总大小。通常设置为 8k 或者 16k。
proxy_buffer 示例
server
{
listen 80;
server_name www.a.com;
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 2 4k;
proxy_busy_buffers_size 4k;
proxy_temp_path /tmp/nginx_proxy_tmp 1 2;
proxy_max_temp_file_size 20M;
proxy_temp_file_write_size 8k;
location /
{
proxy_pass http://192.168.10.110:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
proxy_cache 设置
proxy_cache 将从 C 上获取到的数据根据预设规则存放到 B 上(内存+磁盘)留着备用,
A 请求 B 时,B 会把缓存的这些数据直接给 A,而不需要再去向 C 去获取。
proxy_cache 相关功能生效的前提是,需要设置 proxy_buffering on;
proxy_cache 主要参数
1. proxy_cache
语法:proxy_cache zone|off
默认为 off,即关闭 proxy_cache 功能,zone 为用于存放缓存的内存区域名称。
例:proxy_cache my_zone;
从 nginx 0.7.66 版本开始,proxy_cache 机制开启后会检测被代理端的 HTTP 响应头中的 "Cache-Control"、"Expire" 头域。
如,Cache-Control为no-cache 时,是不会缓存数据的。
2. proxy_cache_bypass
语法:proxy_cache_bypass string;
该参数设定,什么情况下的请求不读取 cache 而是直接从后端的服务器上获取资源。
这里的 string 通常为 nginx 的一些变量。
例:proxy_cahce_bypass $cookie_nocache $arg_nocache$arg_comment;
意思是,如果 $cookie_nocache $arg_nocache$arg_comment 这些变量的值只要任何一个不为 0 或者不为空时,
则响应数据不从 cache 中获取,而是直接从后端的服务器上获取。
3. proxy_no_cache
语法:proxy_no_cache string;
该参数和proxy_cache_bypass类似,用来设定什么情况下不缓存。
例:proxy_no_cache $cookie_nocache $arg_nocache $arg_comment;
表示,如果 $cookie_nocache $arg_nocache $arg_comment 的值只要有一项不为 0 或者不为空时,不缓存数据。
4. proxy_cache_key
语法:proxy_cache_key string;
定义cache key,如:proxy_cache_key $scheme$proxy_host$uri$is_args$args; (该值为默认值,一般不用设置)
5. proxy_cache_path
语法:proxy_cache_path path [levels=levels] keys_zone=name:size [inactive=time] [max_size=size]
path 设置缓存数据存放的路径;
levels 设置目录层级,如 levels=1:2,表示有两级子目录,第一个目录名取 md5 值的倒数第一个值,第二个目录名取 md5 值的第 2 和 3 个值。如下图:
keys_zone 设置内存 zone 的名字和大小,如 keys_zone=my_zone:10m
inactive 设置缓存多长时间就失效,当硬盘上的缓存数据在该时间段内没有被访问过,就会失效了,该数据就会被删除,默认为 10s。
max_size 设置硬盘中最多可以缓存多少数据,当到达该数值时,nginx 会删除最少访问的数据。
例:proxy_cache_path /data/nginx_cache/ levels=1:2 keys_zone=my_zone:10m inactive=300s max_size=5g
proxy_cache 示例
http
{
...;
proxy_cache_path /data/nginx_cache/ levels=1:2 keys_zone=my_zone:10m inactive=300s max_size=5g;
...;
server
{
listen 80;
server_name www.a.com;
proxy_buffering on;
proxy_buffer_size 4k;
proxy_buffers 2 4k;
proxy_busy_buffers_size 4k;
proxy_temp_path /tmp/nginx_proxy_tmp 1 2;
proxy_max_temp_file_size 20M;
proxy_temp_file_write_size 8k;
location /
{
proxy_cache my_zone;
proxy_pass http://192.168.10.110:8080/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
}
说明:核心配置为 proxy_cache_path 那一行。
Nginx 的负载均衡
- Nginx 通过 upstream 和 proxy_pass 实现了负载均衡。本质上也是 Nginx 的反向代理功能,只不过后端的 server 为多个。
案例一(简单的轮询)
upstream www {
server 172.37.150.109:80;
server 172.37.150.101:80;
server 172.37.150.110:80;
}
server {
listen 80;
server_name www.a.com;
location / {
proxy_pass http://www/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
说明:当被代理的机器有多台时,需要使用 upstream 来定义一个服务器组,
其中 www 名字可以自定义,在后面的 proxy_pass 那里引用。
这样 nginx 会将请求均衡地轮询发送给 www 组内的三台服务器。
案例二(带权重轮询 + ip_hash 算法)
upstream www {
server 172.37.150.109:80 weight=50;
server 172.37.150.101:80 weight=100;
server 172.37.150.110:80 weight=50;
ip_hash;
}
server {
listen 80;
server_name www.a.com;
location / {
proxy_pass http://www/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
说明:可以给 www 组内的三台机器配置权重,权重越高,则分配到的请求越多。
ip_hash 为 nginx 负载均衡算法,原理很简单,它根据请求所属的客户端 IP 计算得到一个数值,然后把请求发往该数值对应的后端。
所以同一个客户端的请求,都会发往同一台后端,除非该后端不可用了。ip_hash 能够达到保持会话的效果。
案例三(upstream 其他配置)
upstream www {
server 172.37.150.109:80 weight=50 max_fails=3 fail_timeout=30s;
server 172.37.150.101:80 weight=100;
server 172.37.150.110:80 down;
server 172.37.150.110:80 backup;
}
server
{
listen 80;
server_name www.a.com;
location / {
proxy_next_upstream off;
proxy_pass http://www/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
说明:
down,表示当前的 server 不参与负载均衡;
backup 为预留的机器,当其他的 server(非 backup)出现故障或者忙的时候,才会请求 backup 机器;
max_fails,允许请求失败的次数,默认为 1。当失败次数达到该值,就认为该机器 down 掉了。 失败的指标是由 proxy_next_upstream 模块定义,其中 404 状态码不认为是失败。
fail_timeount,定义失败的超时时间,也就是说在该时间段内达到 max_fails,才算真正的失败。默认是 10 秒。
proxy_next_upstream,通过后端服务器返回的响应状态码,表示服务器死活,可以灵活控制后端机器是否加入分发列表。
语法: proxy_next_upstream error | timeout | invalid_header | http_500 | http_502 | http_503 | http_504 |http_404 | off ...;
默认值: proxy_next_upstream error timeout
error:和后端服务器建立连接时,或者向后端服务器发送请求时,或者从后端服务器接收响应头时,出现错误
timeout:和后端服务器建立连接时,或者向后端服务器发送请求时,或者从后端服务器接收响应头时,出现超时
invalid_header:后端服务器返回空响应或者非法响应头
http_500:后端服务器返回的响应状态码为 500
http_502:后端服务器返回的响应状态码为 502
http_503:后端服务器返回的响应状态码为 503
http_504:后端服务器返回的响应状态码为 504
http_404:后端服务器返回的响应状态码为 404
off:停止将请求发送给下一台后端服务器
案例四(根据不同的 uri)
upstream aa.com {
server 192.168.0.121;
server 192.168.0.122;
}
upstream bb.com {
server 192.168.0.123;
server 192.168.0.124;
}
server {
listen 80;
server_name www.a.com;
location ~ aa.php
{
proxy_pass http://aa.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location ~ bb.php
{
proxy_pass http://bb.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /
{
proxy_pass http://bb.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
说明:请求 aa.php 的,会到 aa.com 组,请求 bb.php 的会到 bb.com,其他请求全部到 bb.com。
案例五(根据不同的目录)
upstream aaa.com
{
server 192.168.111.6;
}
upstream bbb.com
{
server 192.168.111.20;
}
server {
listen 80;
server_name www.a.com;
location /aaa/
{
proxy_pass http://aaa.com/aaa/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /bbb/
{
proxy_pass http://bbb.com/bbb/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
location /
{
proxy_pass http://bbb.com/;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}