今天分享下,如何使用 nginx 搭建具有反向代理功能的服务器。
首先把 nginx 官方文档地址贴出来,有问题可以随时查阅:
1 http://nginx.org/en/docs/
反向代理 首先介绍下正向代理与反向代理的区别。
正向代理是发送请求时,隐藏了真正的客户端,即服务端不知道请求的客户端信息,客户端的请求都被代理服务器代替来请求。正向代理的常用场景就是在爬虫系统中。 反向代理是客户端不知道将要请求的服务器的信息,而是请求一个代理服务器,代理服务器再去请求上游服务器,并把上游服务器的响应返回给客户端。
其实,正向代理和反向代理的请求流程都是一样的,只是被代理对象不同。正向代理代理的对象是客户端,反向代理代理的对象是服务端,也就是说,谁被代理就隐藏了谁。
反向代理服务器搭建 下面看一个 nginx 配置反向代理的例子。
使用 nginx 搭建一个反向代理,上游服务器是一个静态资源 web 服务,配置如下:
nginx.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 worker_processes 1 ;events { worker_connections 1024 ; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local ] "$request " ' '$status $body_bytes_sent "$http_referer " ' '"$http_user_agent " "$http_x_forwarded_for "' ; sendfile on ; keepalive_timeout 65 ; server { listen 127.0.0.1:8081 ; index index.html index.htm; access_log logs/host.access.log main; location / { alias docs/; autoindex on ; set $limit_rate 1k ; } } }
反向代理服务器配置如下:
nginx2.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 worker_processes 1 ;events { worker_connections 1024 ; } http { include mime.types; include ../confs/*.conf ; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local ] "$request " ' '$status $body_bytes_sent "$http_referer " ' '"$http_user_agent " "$http_x_forwarded_for "' ; sendfile on ; keepalive_timeout 65 ; upstream local { server 127.0.0.1:8081 ; } server { listen 8082 ; server_name 192.168.0.105 ; access_log logs/host.access.log main; location / { 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 http://local; } } }
分别启动两个服务器:
1 2 sudo nginx -c /usr/local/nginx/conf/nginx.conf sudo nginx -c /usr/local/nginx/conf/nginx2.conf
启动后,再浏览器地址栏输入 http://192.168.0.105:8082/ 便可以访问静态资源了。
反向代理服务加入缓存功能 如果想提高响应速率,可以在 nginx 反向代理服务器上增加缓存功能。
反向代理增加缓存后,客户端的请求会首先从nginx缓存中获取数据,并返回给客户端,当没有缓存数据或者缓存数据过期时,才会请求上游服务器。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 worker_processes 1 ;events { worker_connections 1024 ; } http { include mime.types; include ../confs/*.conf ; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local ] "$request " ' '$status $body_bytes_sent "$http_referer " ' '"$http_user_agent " "$http_x_forwarded_for "' ; 指定保存缓存数据的路径, keys_zone表示开了一个10m的共享内存用于存放key,my_cache是共享内存名称 proxy_cache_path /tmp/nginx_cache levels=1 :2 keys_zone=my_cache:10m max_size=10g inactive=60m use_temp_path=off ; sendfile on ; keepalive_timeout 65 ; upstream local { server 127.0.0.1:8081 ; } server { listen 8082 ; server_name 192.168.0.105 ; access_log logs/host.access.log main; location / { 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 my_cache; proxy_cache_key $host $uri $is_args $args ; proxy_cache_valid 200 304 302 1d ; proxy_pass http://local; } } }
上面的配置中也介绍了一些常用的缓存参数,详细参数信息可以参考 nginx 的官方文档,文档地址为:
1 http://nginx.org/en/docs/http/ngx_http_proxy_module.html#proxy_cache_path
负载均衡 既然提到了反向代理,那么负载均衡是一定要说一下的。负载均衡的大意就是反向代理服务器把客户端的请求均匀的,或者按照一定的规则分发给上游服务器,从而保证服务可用性。
实现负载均衡有以下几种方式:
1 2 3 1. 水平扩展:基于 Round-Robin或者least-connect算法进行请求分发 2. 按功能扩展:根据请求URL,按照功能分发请求到上游服务器 3. 基于用户信息扩展:根据请求客户端ip或者其他信息分发客户端请求(基于hash的某些算法)
上面的方式可以组合起来使用,并不是只能单独使用。
nginx支持多种协议的反向代理,即客户端使用http协议请求服务时,nginx可以将其转换成fastcgi、uwsgi、rpc、websocket等协议,再向上游服务器发起请求。
负载均衡策略 nginx 中负责与上游服务器交互的模块是 upstream 模块,该模块中提供了一个基本的负载均衡算法 Round-Robin。
upstream 模块中,指定上游服务器的指令是 upstream,上面的例子中已经见过,该指令的作用域是 http 上下文。在 upstream 中,使用 server 指令指定上游服务器地址信息。
Round-Robin负载均衡算法介绍:
1 该算法功能:以轮询的方式访问 server 指令指定的上游服务器,该算法集成在 Nginx 的 upstream 模块中。
该算法提供以下指定:
1 2 3 4 wieght: 上游服务器权重,默认1 max_conns:上游服务器的最大并发连接数,仅用于单 worker 进程,默认0,表示没有限制 max_fails: 与fail_timeout配合使用。在fail_timeout时间段内,最大的失败次数。当达到最大失败次数时,在fail_timeout时间内,该上游服务器不再被选择 fail_timeout:单位秒,默认为10,与max_fails配合使用,有两个功能:,指定一段时间内最大的失败次数max_fails;当到达max_fails后,该上游服务器不能再被访问
反向代理服务中加入负载均衡 基于 Round-Robin 算法的负载均衡 反向代理服务配置 nginx.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 worker_processes 1 ;events { worker_connections 1024 ; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local ] "$request " ' '$status $body_bytes_sent "$http_referer " ' '"$http_user_agent " "$http_x_forwarded_for "' ; sendfile on ; keepalive_timeout 65 ; server { listen 127.0.0.1:8081 ; index index.html index.htm; access_log logs/access.log main; error_log logs/error.log; location / { alias docs/; autoindex on ; set $limit_rate 1k ; } } include reverse_proxy.conf; }
reverse_proxy.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 upstream proxy { server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5 ; server 127.0.0.1:8012 ; } server { listen 8091 ; error_log logs/reverse_proxy.err.log; location / { proxy_pass http://proxy; } }
上游服务配置 nginx2.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 worker_processes 1 ;events { worker_connections 1024 ; } http { include mime.types; default_type application/octet-stream; log_format main '$remote_addr - $remote_user [$time_local ] "$request " ' '$status $body_bytes_sent "$http_referer " ' '"$http_user_agent " "$http_x_forwarded_for "' ; sendfile on ; keepalive_timeout 65 ; server { listen 127.0.0.1:8082 ; index index.html index.htm; access_log logs/access.log main; error_log logs/error.log; location / { alias docs/; autoindex on ; set $limit_rate 1k ; } } include upstream.conf; }
upstream.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 server { listen 8011 ; default_type text/plain; return 200 '8011 server response' ; } server { listen 8012 ; default_type text/plain; return 200 '8012 server response' ; }
上面的服务配置好以后,可以分别启动服务:
1 2 启动反向代理服务:sudo nginx -c nginx.conf 启动上游服务:sudo nginx -c nginx2.conf
服务启动后,在终端多次执行 curl localhost:8091,可以发现,响应结果中,’8011 server response’ 和 ‘8012 server response’ 的比例大致为 2:1,这说明我们配置的负载均衡已经生效了。
基于 ip hash 的负载均衡 基于 ip 地址的 hash 算法可以根据客户端 ip 的不同,将请求转发到不同的上游服务器,它的配置如下(仅仅修改了 reverse_proxy.conf文件):
reverse_proxy.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 upstream proxy { ip_hash; server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5; server 127.0.0.1:8012 weight=1; } server { set_real_ip_from 192.168.0.105; real_ip_recursive on; real_ip_header X-Forwarded-For; listen 8091; error_log logs/reverse_proxy.err.log; location / { proxy_pass http://proxy; } }
需要注意的是,上面使用了 realip 模块的一些指令,现在的 nginx 版本中,realip模块默认没有编译进 nginx,如果要使用 该模块,需要将 realip 模块编译进 nginx 中。如何把某个模块编译进已安装的 nginx 中,可以查看我的另一篇博客。
上面指定的大致意思如下:
1 2 3 4 ip_hash:根据用户的 ip 地址分发客户端的请求到上游服务器 set_real_ip_from:设置可信任的地址 real_ip_recursive on:表示原始客户端 ip 地址会被请求头中的某个非信任 ip 地址(由real_ip_header决定)替换 real_ip_header X-Forwarded-For:表示以 X-Forwarded-For 中的最后一个 ip 作为 ip_hash 对应的 ip 地址
具体解释可以查看官方文档:
1 http://nginx.org/en/docs/http/ngx_http_realip_module.html#set_real_ip_from
配置完成重启后,我们可以在终端执行以下命令来测试:
1 curl -H "X-Forwarded-For: 10.112.23.45" localhost:8091
可以更换 ip ,查看返回的结果。
基于关键字 hash 的负载均衡 使用关键字 hash 时,reverse_proxy.conf的配置如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 upstream proxy { # 使用特定的字符串作为hash值 hash user_$arg_username; server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5; server 127.0.0.1:8012 weight=1; } server { set_real_ip_from 192.168.0.105; real_ip_recursive on; real_ip_header X-Forwarded-For; listen 8091; error_log logs/reverse_proxy.err.log; location / { proxy_pass http://proxy; } }
请求服务时时,传入不同的 username 参数,会请求不同的上游服务器。
hash算法优化 当有上游服务器宕机或者需要扩容时,hash 算法会导致负载均衡的路由发生变化,这样会导致一系列问题(例如缓存失效)。一致性 hash 算法可以解决这个问题。
nginx 中配置一致性 hash 比较简单,配置如下:
reverse_proxy.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 upstream proxy { # 使用特定的字符串作为hash值 hash user_$arg_username consistent; server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5; server 127.0.0.1:8012 weight=1; } server { # set_real_ip_from 192.168.0.105; # real_ip_recursive on; real_ip_header X-Forwarded-For; listen 8091; error_log logs/reverse_proxy.err.log; location / { proxy_pass http://proxy; } }
在原来的 hash key 后面加上 consistent 即可实现一致性 hash,具体介绍可以查看官方文档:
1 http://nginx.org/en/docs/http/ngx_http_upstream_module.html#hash
基于最少连接数算法的负载均衡 最少连接数算法中,nginx 反向代理服务器会将客户端请求转发到连接数最少的上游服务器中。当有多个上游服务器最少连接数相同时,会按照 Roubd-Robin 算法将请求发送到这几个服务器中的一个。最少连接数算法的配置如下:
reverse_proxy.conf:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 upstream proxy { least_conn; server 127.0.0.1:8011 weight=2 max_conns=2 max_fails=2 fail_timeout=5; server 127.0.0.1:8012 weight=1; } server { # set_real_ip_from 192.168.0.105; # real_ip_recursive on; real_ip_header X-Forwarded-For; listen 8091; error_log logs/reverse_proxy.err.log; location / { proxy_pass http://proxy; } }
在 upstream 上下文中,加入 least_conn 指令即可配置最少连接数算法。
官方文档如下:
1 http://nginx.org/en/docs/http/ngx_http_upstream_module.html#least_conn