nginx在CDN加速或使用SLB代理后,获取真实IP,做并发访问限制的方法(限流)

本文主要是介绍nginx在CDN加速或使用SLB代理后,获取真实IP,做并发访问限制的方法(限流),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

本文80%转载于张戈博客,后续加入了自己的理解和想法
原文地址:https://zhangge.net/4879.html

站点在运行时,为了防止DDoS 攻击、或内部接口调用造成的数据迸发,nginx提供了limit限流模块:

HttpLimitZoneModule 限制同时并发访问的数量
HttpLimitReqModule 限制访问数据,每秒内最多几个请求

一、普通配置:

什么叫普通配置?

普通配置就是针对【用户浏览器】→【网站服务器】这种常规模式的 nginx 配置。那么,如果我要对单 IP 做访问限制,绝大多数教程都是这样写的:

## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址最多有 50 个并发连接
## 你想开 几千个连接 刷死我? 超过 50 个连接,直接返回 503 错误给你,根本不处理你的请求了
limit_conn_zone $binary_remote_addr zone=TotalConnLimitZone:10m ;
limit_conn  TotalConnLimitZone  50;
limit_conn_log_level notice;## 用户的 IP 地址 $binary_remote_addr 作为 Key,每个 IP 地址每秒处理 10 个请求
## 你想用程序每秒几百次的刷我,没戏,再快了就不处理了,直接返回 503 错误给你
limit_req_zone $binary_remote_addr zone=ConnLimitZone:10m  rate=10r/s;
limit_req_log_level notice;## 具体服务器配置
server {listen   80;location ~ \.php$ {## 最多 5 个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 503 错误给你了limit_req zone=ConnLimitZone burst=5 nodelay;fastcgi_pass   127.0.0.1:9000;fastcgi_index  index.php;include fastcgi_params;}   }

这样一个最简单的服务器安全限制访问就完成了,这个基本上你 Google 一搜索能搜索到 90% 的网站都是这个例子,而且还强调用“$binary_remote_addr”可以节省内存之类的云云。

二、CDN 或 SLB 代理之后

为了增加安全、性能,许多站点都用到了CDN加速,或者其他的二级代理,例如阿里的SLB负载均衡等等。

于是,网站的访问模式就变为:
用户浏览器 → CDN 节点 / SLB 节点→ 网站源服务器

甚至是更复杂的模式:
用户浏览器 → CDN/SLB 节点(CDN 入口、CC\DDoS 攻击流量清洗等) → 阿里云盾 → 源服务器

可以看到,我们的网站中间经历了好几层的透明加速和安全过滤, 这种情况下,我们就不能用上面的“普通配置”。因为普通配置中基于【源 IP 的限制】的结果就是,我们把【CDN /SLB节点】或者【阿里云盾】给限制了,因为这里“源 IP”地址不再是真实用户的 IP,而是中间 CDN /SLB节点的 IP 地址。

我们需要限制的是最前面的真实用户,而不是中间为我们做加速的加速服务器。

其实,当一个 CDN /SLB或者透明代理服务器把用户的请求转到后面服务器的时候,这个 CDN /SLB服务器会在 Http 的头中加入一个记录

X-Forwarded-For : 用户 IP, 代理服务器 IP

如果中间经历了不止一个代理服务器,这个记录会是这样

X-Forwarded-For : 用户 IP, 代理服务器 1-IP, 代理服务器 2-IP, 代理服务器 3-IP, ….

可以看到经过好多层代理之后, 用户的真实 IP 在第一个位置, 后面会跟一串中间代理服务器的 IP 地址,从这里取到用户真实的 IP 地址,针对这个 IP 地址做限制就可以了。

那么针对 CDN /SLB模式下的访问限制配置就应该这样写:

http{## 这里取得原始用户的IP地址,没走CDN/SLB的,给到$remote_addrmap $http_x_forwarded_for  $clientRealIp {default $remote_addr;~^(?P<firstAddr>[0-9\.]+),?.*$	$firstAddr;}#设置IP白名单,对内部的IP不设限map $clientRealIp $limit{default $clientRealIp;xx.xx.xx.xx "";}#以真实IP为单位,限制请求数,并返回429状态;limit_req_status 429;limit_req_zone $limit zone=ConnLimitZone:20m rate=5r/s;limit_req_log_level notice;#以真实IP为单位,限制该IP的并发连接数,并返回429状态;limit_conn_status 429;limit_conn_zone $limit zone=TotalConnLimitZone:20m ;limit_conn  TotalConnLimitZone 100;limit_conn_log_level notice;#以访问域名为单位,限制总并发链接数;limit_conn_zone $server_name zone=SumConnLimitZone:20m;
}## 具体Server:如下在监听php/go/java部分新增限制规则即可,或直接放在域名下面,限制全部访问
server {listen   80;location ~ \.php$ {#限制总并发连接数limit_conn SumConnLimitZone 10000;#最多5个排队, 由于每秒处理 10 个请求 + 5个排队,你一秒最多发送 15 个请求过来,再多就直接返回 429 错误给你了limit_req  zone=ConnLimitZone  burst=5  nodelay;fastcgi_pass   127.0.0.1:9000;fastcgi_index  index.php;include fastcgi_params;}   
}

三、如何验证

根据以上配置,我们知道nginx,每秒最多允许通过10+5个请求,在压测时,就会有两种情况:

  1. 在白名单内(到白名单的服务器测试),压测该站点,应该全部通过
  2. 不在白名单内,最多只允许通过10 +5 个请求,余下部分应该返回429

前提:压测总次数超过 10 +5,否则看不出效果。

centos一般都自带有 siege压测工具,还比较好用:
yum -y install siege

使用方法:
siege -c 3 -r 10 -b https://xxxx.xx.com/api/xxxx
-c 3 表示3个用户
-r 10 表示访问10次
以上表示:3个用户,每个用户访问10次请求,共计30次

经测试,每增加一台nginx,相同的配置应该是 x 2,例如:nginx1 的配置是 10+5,nginx2的配置也是10+5,域名部署在这两台nginx上,请求数最大允许为:20+10

以上测试,如果不能通过,应该是配置问题,那么我们用echo模块来调试下:

四、echo 模块
作者原文提到了 nginx 的一个 echo 模块,特意玩了下感觉挺有意思的,下面贴一下简单集成步骤。

①、给 nginx 集成 echo 模块:

cd /usr/local/src
#下载echo模块并解压:
wget https://github.com/openresty/echo-nginx-module/archive/v0.61.tar.gz
tar zxvf v0.61.tar.gz#下载nginx并解压
wget http://nginx.org/download/nginx-1.12.2.tar.gz
tar -xzvf nginx-1.12.2.tar.gz
cd nginx-1.12.2/#查看在用nginx的编译参数(如果是全新安装则省略)
/usr/local/nginx/sbin/nginx -V
nginx version: nginx/1.12.2
built by gcc 4.4.7 20120313 (Red Hat 4.4.7-4) (GCC) #以下这行即为旧的编译参数:
configure arguments: --user=www --group=www --prefix=/usr/local/nginx --with-http_gzip_static_module
#在旧的编译参数基础上新增【--add-module=/echo模块的解压路径】参数,开始编译
./configure --prefix=/usr/local/nginx/nginx  --add-module=/usr/local/src/echo-nginx-module-0.61#make编译
make -j2#平滑升级nginx (如果是全新安装请执行:make install)
mv /usr/local/nginx/sbin/nginx /usr/local/nginx/sbin/nginx.old
cp -f objs/nginx /usr/local/nginx/sbin/
make upgrade

以上升级、编译和添加第三方模块不熟悉的朋友,可以参考我另外一篇博客:
https://mp.csdn.net/mdeditor/81136273

②、echo 用法举例:
其实就和 shell 的 echo 差不多,能否输出自定义信息。

比如,在 nginx 里面配置如下:

location /hello {echo "hello, world!";
}

访问 http://xxx.com/hello 就会在浏览器里面输出 hello, world! 了(如果域名开了 CDN 可能会报 404)。

又比如,测试本文提到的真实用户的 IP,只要在本文第二步配置基础上,加上如下规则并 reload 即可:

server {listen   80;server_name  yourdomain.com;## 以下是新增规则:## 当用户访问 /ip 的时候,我们输出 $clientRealIp 和 $limit变量,看看这个变量## 值是不是真的 用户源IP 地址location /ip{echo $clientRealIp;echo $limit;}
}

认真看的朋友,会问 clientRealIp 和 limit 有什么区别:
clientRealIp 如果走 SLB/CDN,获取的就是真实IP,反之,获取的就是remote_addr

limit 是在clientRealIp的基础上,排除了“IP白名单”,也就是说,当你的源IP,是白名单时,你的limit,应该为“空”,这样就不受限流了

所以,我们可以以此来判断,IP白名单是否有效:
curl http://xxx.xxx.cn/ip
这里写图片描述

本文介绍到这就差不多结束了,也是在神作的基础上精简整理并测试的,如果看完还有些许疑问,请前往查看神作原文,也许还是大神写的比较好理解(是否是原创我就不深究了,感觉也是转来转去,都没留链接,悲哀的互联网)!

转载:https://zhangge.net/4879.html

这篇关于nginx在CDN加速或使用SLB代理后,获取真实IP,做并发访问限制的方法(限流)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/250924

相关文章

Qt spdlog日志模块的使用详解

《Qtspdlog日志模块的使用详解》在Qt应用程序开发中,良好的日志系统至关重要,本文将介绍如何使用spdlog1.5.0创建满足以下要求的日志系统,感兴趣的朋友一起看看吧... 目录版本摘要例子logmanager.cpp文件main.cpp文件版本spdlog版本:1.5.0采用1.5.0版本主要

Java中使用Hutool进行AES加密解密的方法举例

《Java中使用Hutool进行AES加密解密的方法举例》AES是一种对称加密,所谓对称加密就是加密与解密使用的秘钥是一个,下面:本文主要介绍Java中使用Hutool进行AES加密解密的相关资料... 目录前言一、Hutool简介与引入1.1 Hutool简介1.2 引入Hutool二、AES加密解密基础

使用Python将JSON,XML和YAML数据写入Excel文件

《使用Python将JSON,XML和YAML数据写入Excel文件》JSON、XML和YAML作为主流结构化数据格式,因其层次化表达能力和跨平台兼容性,已成为系统间数据交换的通用载体,本文将介绍如何... 目录如何使用python写入数据到Excel工作表用Python导入jsON数据到Excel工作表用

Pytest多环境切换的常见方法介绍

《Pytest多环境切换的常见方法介绍》Pytest作为自动化测试的主力框架,如何实现本地、测试、预发、生产环境的灵活切换,本文总结了通过pytest框架实现自由环境切换的几种方法,大家可以根据需要进... 目录1.pytest-base-url2.hooks函数3.yml和fixture结论你是否也遇到过

鸿蒙中Axios数据请求的封装和配置方法

《鸿蒙中Axios数据请求的封装和配置方法》:本文主要介绍鸿蒙中Axios数据请求的封装和配置方法,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友参考下吧... 目录1.配置权限 应用级权限和系统级权限2.配置网络请求的代码3.下载在Entry中 下载AxIOS4.封装Htt

鸿蒙中@State的原理使用详解(HarmonyOS 5)

《鸿蒙中@State的原理使用详解(HarmonyOS5)》@State是HarmonyOSArkTS框架中用于管理组件状态的核心装饰器,其核心作用是实现数据驱动UI的响应式编程模式,本文给大家介绍... 目录一、@State在鸿蒙中是做什么的?二、@Spythontate的基本原理1. 依赖关系的收集2.

Python基础语法中defaultdict的使用小结

《Python基础语法中defaultdict的使用小结》Python的defaultdict是collections模块中提供的一种特殊的字典类型,它与普通的字典(dict)有着相似的功能,本文主要... 目录示例1示例2python的defaultdict是collections模块中提供的一种特殊的字

Python获取C++中返回的char*字段的两种思路

《Python获取C++中返回的char*字段的两种思路》有时候需要获取C++函数中返回来的不定长的char*字符串,本文小编为大家找到了两种解决问题的思路,感兴趣的小伙伴可以跟随小编一起学习一下... 有时候需要获取C++函数中返回来的不定长的char*字符串,目前我找到两种解决问题的思路,具体实现如下:

C++ Sort函数使用场景分析

《C++Sort函数使用场景分析》sort函数是algorithm库下的一个函数,sort函数是不稳定的,即大小相同的元素在排序后相对顺序可能发生改变,如果某些场景需要保持相同元素间的相对顺序,可使... 目录C++ Sort函数详解一、sort函数调用的两种方式二、sort函数使用场景三、sort函数排序

Redis实现延迟任务的三种方法详解

《Redis实现延迟任务的三种方法详解》延迟任务(DelayedTask)是指在未来的某个时间点,执行相应的任务,本文为大家整理了三种常见的实现方法,感兴趣的小伙伴可以参考一下... 目录1.前言2.Redis如何实现延迟任务3.代码实现3.1. 过期键通知事件实现3.2. 使用ZSet实现延迟任务3.3