[关闭]
@yulongsun 2018-08-16T16:04:13.000000Z 字数 8068 阅读 1600

网关技术调研 - 负载均衡器Nginx

网关技术调研


1. 简介

1.1 设计一个网关需要考虑的因素

设计一个网关需要考虑的因素

1.2 常见网站部署结构

image_1cj7gqbf71eh5eddicc1sg51dme9.png-127.4kB


2. Nginx介绍

2.1 为什么选择Nginx?

image_1cjar4u859a312b91iqtf1huo033.png-333.2kB

设计为一个主进程,多个工作进程,每个进程都是单线程处理请求;

2.2 Nginx常见正则表达式使用

语法:location [=|~|~*|^~] /uri/

表达式 含义
= 表示开头精准匹配
^~ 表示以某个常规字符串开头
~ 表示区分大小写
~* 表示不区分大小写
!~ 表示区分大小写不匹配
!~* 表示不区分大小写不匹配
.* .表示任意字符串,*表示0到正无穷
\. 转义字符,\.匹配.

匹配顺序规则:= > ^~ >正则匹配顺序 > 通配符/
匹配成功之后,停止匹配,按当前匹配规则处理。
例如:

  1. location = / {
  2. #规则A
  3. }
  4. location = /login {
  5. #规则B
  6. }
  7. location ^~ /static/ {
  8. #规则C
  9. }
  10. location ~ \.(gif|jpg|png|js|css)$ {
  11. #规则D
  12. }
  13. location ~* \.png$ {
  14. #规则E
  15. }
  16. location !~ \.xhtml$ {
  17. #规则F
  18. }
  19. location !~* \.xhtml$ {
  20. #规则G
  21. }
  22. location / {
  23. #规则H
  24. }

比如:
1. 访问localhost/会匹配规则A。
2. 访问localhost/login会匹配规则B, 访问localhost/register会匹配规则H。
3. 访问localhost/static/a.html会匹配规则C。
4. 访问localhost/a.png将匹配规则D/E。但是因为规则D顺序优先,所以规则D生效,E不生效。
5. 访问localhost/a.PNG会匹配规则E,不会匹配D,因为区分大小写。
6. 访问localhost/a.xhtml不会匹配F/G。访问localhost/a.XHTML不会匹配规则G。
7. 访问localhost/a/b/c/d,则最终匹配到规则H,因为以上规则都匹配。


3. Nginx实战

image_1cjas12jf1b0k1oloc6ltv6ti23g.png-90.4kB

实验设备 IP 端口 备注
node0 192.168.142.1 通讯机1:6610 通讯机2:7610
node1 192.168.142.21 httpd:8001 nginx:80 图片资源服务器
node2 192.168.142.22 httpd:8002 tomcat:8080 Tomcat服务器
node3 192.168.142.23 httpd:8003 镜像服务器
node4 192.168.142.24 nginx:80 自编译nginx代理服务器

3.1 负载均衡|反向代理

3.1.1 TCP负载均衡

Nginx1.9.0版本,新增支持TCP代理和负载均衡的stream模块。
【启用】./configure --with-stream

  1. #设定TCP服务器
  2. stream{
  3. #上游服务器集群
  4. upstream commserver{
  5. server 192.168.142.1:6610;
  6. server 192.168.142.1:7610;
  7. }
  8. #虚拟节点
  9. server{
  10. listen 6610;
  11. proxy_pass commserver;
  12. }
  13. }

3.1.2 HTTP负载均衡

  1. http{
  2. #上游服务器集群
  3. upstream backend{
  4. server 192.168.142.21:8001 weight=1;
  5. server 192.168.142.22:8002 weight=1;
  6. server 192.168.142.23:8003 weight=1;
  7. server 192.168.142.24:8004 weight=1;
  8. }
  9. #虚拟主机
  10. server {
  11. ...
  12. }
  13. }

3.1.3 Nginx支持的负载均衡策略

3.1.4 实验

通讯机负载均衡实验

3.1.5 动态负载均衡

Nginx+Consul+Consul-template

image_1cj9b53sb185jb31lm01hn0de22n.png-100.9kB


3.2 动静分离

3.2.1 部署图

TODO

3.2.2 配置

  1. #定义静态资源集群
  2. upstream image{
  3. server 192.168.142.21:8001 weight=1 max_fails=2 fail_timeout=10s;
  4. }
  5. #静态页面集群
  6. upstream html{
  7. server 192.168.142.21:8001;
  8. server 192.168.142.22:8002 backup;
  9. }
  10. #动态资源集群
  11. upstream backend{
  12. server 192.168.142.22:8080;
  13. }
  14. server {
  15. #监听端口
  16. listen 80;
  17. #域名可以有多个,用空格隔开
  18. server_name node4;
  19. #转发图片资源
  20. location ~ .*\.(jpg)$ {
  21. proxy_pass http://image;
  22. }
  23. #静态资源
  24. location ~ .*\.(html|htm)$ {
  25. proxy_pass http://html;
  26. }
  27. #动态站点转发
  28. location ~ .*\.(do|jsp)$ {
  29. proxy_pass http://backend;
  30. }
  31. }

3.2.3 实验

静态页面:http://node4/index.html
图片:http://node4/icons/hundsun.jpg
JSP/Servlet:http://node4/demo/hello.do http://node4/demo/index.jsp


3.3 流量拷贝|镜像发布

3.3.1 需求

虽然经过多重测试,但是由于生产环境中的情况很复杂,测试环境很难完全模拟线上环境。为了能够更真实的测试,可以使用线上真实的流量来对测试环境中的服务器进行测试。通常用于仿真压测。

方案1:ngx_http_mirror_module
方案2:TcpCopy

image_1cjab38m11eem10tr1rrppl815402m.png-82.7kB

3.3.2 配置

  1. #源地址配置
  2. location / {
  3. #指定镜像uri为/mirror
  4. mirror /mirror;
  5. #指定是否镜像请求body部分
  6. mirror_request_body on;
  7. proxy_pass http://192.168.142.21:8001/$request_uri;
  8. }
  9. #镜像地址配置
  10. location /mirror {
  11. proxy_pass http://192.168.142.23:8003$request_uri;
  12. }

3.3.3 实验

http://node4/hello.html
image_1cj6mu94p1qsc1klr1rp619lq21513.png-88.1kB


3.4 接入层限流

3.4.1 需求

电商平台营销时候,经常会碰到的大流量问题,除了做流量分流处理,可能还要做用户黑白名单、信誉分析,进而根据用户ip信誉权重做相应的流量拦截、限制流量。
Nginx自身有的请求限制模块ngx_http_limit_req_module、流量限制模块ngx_stream_limit_conn_module基于漏桶算法,可以方便的控制令牌速率,自定义调节限流,实现基本的限流控制。

高并发三大利器:目的是保证核心服务可用,即使是有损的。

3.4.2 限流

1、连接限流

  1. #limit_conn_zone用来配置限流Key和存放Key的内存大小。
  2. #$binary_remote_addr是IP维度限流。
  3. limit_conn_zone $binary_remote_addr zone=conn_ip_zone:10m;
  4. #$server_name是域名维度限流。
  5. limit_conn_zone $server_name zone=conn_servername_zone:10m;
  6. server {
  7. ...
  8. location ~ .*\.(html|htm)$ {
  9. #配置限流后返回码
  10. limit_conn_status 503;
  11. #IP维度,每个IP最大并发连接数
  12. limit_conn conn_ip_zone 2;
  13. #配置限流后的日志级别
  14. limit_conn_log_level error;
  15. #域名维度。每个域名最大并发数
  16. limit_conn conn_servername_zone 1;
  17. }
  18. }

PS: Zone定义IP状态及URL访问频率的共享内存区域。zone=keyword标识区域的名字,以及冒号后面跟区域大小。16000个IP地址的状态信息约1MB,所以示例中区域可以存储160000个IP地址。

基于域名维度连接限流实验

前提:模拟每个请求耗时1s
第一次:
1. 每个域名最大并发数设置为1
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
image_1cj99uojgml911cg1n6gb4c1a7713.png-71.4kB

第二次:
1. 每个域名最大并发数调整为5
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
image_1cj9a25vihpg1suptq0ho41f471g.png-24.4kB

2、请求限流

  1. #漏桶算法实现。配置Key及存放Key的内存大小。
  2. #rate固定请求速率。如rate=500r/s 即每秒500个请求,每2毫秒一个请求
  3. limit_req_zone $binary_remote_addr zone=req_ip_zone:10m rate=500r/s;
  4. server {
  5. ...
  6. location ~ .*\.(html|htm)$ {
  7. #配置限流区域。桶容量(burst 默认为0) 是否延迟(默认延迟)
  8. limit_req zone=req_ip_zone burst=0 delay;
  9. #配置限流后返回码
  10. limit_req_status 503;
  11. #配置限流后的日志级别
  12. limit_conn_log_level error;
  13. }
  14. }
基于IP维度请求限流实验

前提:模拟每个请求耗时1s
第一次:
1. 桶容量=0,延迟
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
image_1cj9ag5f7fgdiqgjbi11tj17561t.png-67.4kB
第二次:
1. 每个域名最大并发数调整为5
2. 压测5个并发 ab -c 5 -n 5 http://node4/demo/test.do
3. 结果
image_1cj9aiiv51pq116pq57pgti3le2a.png-22.8kB

3.4.3 代理层缓存

  1. #代理层缓存定义:
  2. #代理缓存路径。1:2表示创建2层目录。inactive表示缓存多久不被访问将被移除,默认10m。use_temp_path=on缓存会先被临时写入proxy_temp_path
  3. proxy_cache_path /opt/nginx/cache levels=1:2 keys_zone=my_cache:200m inactive=10m max_size=1g use_temp_path=off;
  4. proxy_connect_timeout 90; #nginx跟后端服务器连接超时时间(代理连接超时)
  5. proxy_send_timeout 90; #后端服务器数据回传时间(代理发送超时)
  6. proxy_read_timeout 90; #连接成功后,后端服务器响应时间(代理接收超时)
  7. proxy_buffer_size 4k; #设置代理服务器(nginx)保存用户头信息的缓冲区大小
  8. proxy_buffers 4 32k; #proxy_buffers缓冲区,网页平均在32k以下的设置
  9. ...
  10. location ...{
  11. #指定哪个缓存
  12. proxy_cache my_cache;
  13. #为不同状态码设定缓存时间
  14. proxy_cache_valid 200 1h;
  15. #响应头中添加缓存命中状态。 HIT命中 MISS未命中 EXPIRED过期
  16. add_header cache_status $upstream_cache_status;
  17. }

image_1cj9cr8hm1efv1jf0bup7f21c2534.png-84.3kB


3.5 黑白名单

  1. allow 127.0.0.1;
  2. deny all;

3.6 健康检查|心跳

【依赖第三方模块】:nginx_upstream_check_module https://github.com/yaoweibin/nginx_upstream_check_module

3.6.1 TCP心跳检查

【踩坑】nginx_upstream_check_module不支持nginx_1.14.0。

image_1cj3njk0va6b6m02dp1imp1i2o13.png-26.8kB

image_1cj3nf0ipnd913c8o521u4bblt9.png-6.7kB
Github Issues:
https://github.com/yaoweibin/nginx_upstream_check_module/issues/135
image_1cj3nncnm17l21pgbj6c1nif8t1g.png-78.5kB

3.6.2 HTTP心跳检查

  1. upstream backend{
  2. server 192.168.142.21:8001 weight=1;
  3. server 192.168.142.22:8002 weight=1;
  4. server 192.168.142.23:8003 weight=1;
  5. server 192.168.142.24:8004 weight=1;
  6. check interval=3000 rise=1 fall=3 timeout=2000 type=http;
  7. #check_http_send "HEAD /status HTTP /1.0\r\n\r\n";
  8. #check_http_expect_alive http_2XX http_3XX;
  9. }

3.7 灰度发布|分流

3.7.1 场景

我们可以将一个“新的版本代码”发布到集群中的少数几台(组)机器上,以便引入线上少量真实用户进行测试,用于验证产品改进的收益、小规模试错等
扩展:
nginx提供了“nginx_http_split_clients_module”、“nginx_stream_split_clients_module”,分别适用于http和tcp,可以帮助我们简单实现这些功能

3.7.2 根据Cookide分流

  1. #演示环境
  2. upstream staging{
  3. server 192.168.142.21:8001 weight=1;
  4. }
  5. #生产环境
  6. upstream prod{
  7. server 192.168.142.22:8002 weight=1;
  8. }
  9. server {
  10. listen 80;
  11. server_name node4;
  12. set $group "default";
  13. if ($http_cookie ~* "version=V1"){
  14. set $group staging;
  15. }
  16. if ($http_cookie ~* "version=V2"){
  17. set $group prod;
  18. }
  19. location / {
  20. proxy_set_header Host $host;
  21. proxy_set_header X-Real-IP $remote_addr;
  22. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  23. proxy_pass http://$group/$request_uri;
  24. }
  25. }

image_1cj6onn771gitd2bbtjvqc1jhk1g.png-16.8kB

3.7.3 根据IP分流

  1. #演示环境
  2. upstream staging{
  3. server 192.168.142.21:8001 weight=1;
  4. }
  5. #生产环境
  6. upstream prod{
  7. server 192.168.142.22:8002 weight=1;
  8. }
  9. server {
  10. listen 80;
  11. server_name node4;
  12. set $group "default";
  13. #本地走演示环境
  14. if ($remote_addr ~ "127.0.0.1"){
  15. set $group staging;
  16. }
  17. #外部走生产环境
  18. if ($remote_addr !~ "127.0.0.1"){
  19. set $group prod;
  20. }
  21. location / {
  22. proxy_set_header Host $host;
  23. proxy_set_header X-Real-IP $remote_addr;
  24. proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
  25. proxy_pass http://$group/$request_uri;
  26. }
  27. }

本地走演示环境
外网走生产环境

3.8 代理层失败重试

3.8.1 网络连接超时设置

  1. location ~ .*.(html|htm)$ {
  2. #连接超时,默认60s
  3. proxy_connect_timeout 5s;
  4. #读取超时。默认60s。
  5. proxy_read_timeout 5s;
  6. #发送请求超时时间。默认60s。
  7. proxy_send_timeout 5s;
  8. proxy_pass http://backend$request_uri;
  9. }

备注:proxy_read_timeout、proxy_send_timeout:是指连续2次成功读取/发送的时间间隔,而不是读取/发送整个响应体的超时时间。如果超过这个时间间隔,则Nginx关闭此连接。

3.8.2 失败重试机制设置

  1. location ~ .*.(html|htm)$ {
  2. #配置什么情况下需要重试。{error|timeout|http_500|http_502|http_503|http_403|http_404|off}
  3. #off表示禁用重试
  4. #timeout慎用
  5. proxy_next_upstream error timeout;
  6. #重试次数。0表示不限制。
  7. proxy_next_upstream_tries 2;
  8. #重试超时时间。0表示不限制。
  9. proxy_next_upstream_timeout 6s;
  10. proxy_pass http://backend$request_uri;
  11. }

3.8.3 upstream存活超时设置

  1. #定义上游服务器
  2. upstream backend{
  3. server 192.168.142.21:8001 weight=1 max_fails=2 fail_timeout=10s;
  4. server 192.168.142.22:8002 weight=1 max_fails=2 fail_timeout=10s;
  5. }

fail_timeout时间内失败了max_fails次,则认为上游服务器不存活,然后移除上游服务器。
fail_timeout之后,再次加入存活服务器列表重试。
image_1cj82h3eb108i11n3aoc1r24a8f9.png-37.8kB

【问题】
一个请求被重复提交,原因是nginx代理后面挂着2个服务器,请求超时的时候(其实已经处理了),结果nginx发现超时,有把请求转给另外台服务器又做了次处理。
解决方案:去掉proxy_next_upstream的timeout

3.9 Tips

3.9.1 XSS防御

  1. add_header Set-Cookie "HttpOnly";

Cookie的HttpOnly属性,指示浏览器不要在除HTTP(和HTTPS)请求之外暴露Cookie。一个有HttpOnly属性的Cookie,是不可以通过例如调用JavaScript(引用document.cookie)这种非HTTP方式来访问。因此,也不可能通过跨域脚本来偷走这种Cookie。
image_1cja6qrpr116fve1q6dnco1qs9.png-20.5kB

3.9.2 隐藏Nginx版本

  1. #隐藏nginx版本号
  2. server_tokens off;

隐藏之前
隐藏之前
隐藏之后
隐藏之后

参考

  1. Nginx配置之负载均衡、限流、缓存、黑名单和灰度发布
  2. HTTP 请求头中的 X-Forwarded-For,X-Real-IP
  3. 使用Nginx实现灰度发布
  4. Nginx 限流
  5. Nginx官方中文文档
  6. Nginx负载均衡学习笔记
  7. 通过Consul-Template实现动态配置服务
添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注