开放平台的请求限流方案.docx
《开放平台的请求限流方案.docx》由会员分享,可在线阅读,更多相关《开放平台的请求限流方案.docx(18页珍藏版)》请在冰豆网上搜索。
开放平台的请求限流方案
开放平台的请求限流方案
场景
在设计开放性平台系统(尤其OpenAPI类型平台)时有三把利器用来保护系统:
缓存、降级和限流。
●缓存:
提升系统访问速度和增大系统能处理的容量,抗高并发流量;
●降级:
当服务出问题或者影响到核心流程的性能则需要暂时屏蔽掉,待高峰或者问题解决后再打开;
●限流:
有些场景并不能用缓存和降级来解决,比如稀缺资源(秒杀、抢购)、写服务(如评论、下单)、频繁的复杂查询(评论的最后几页)、恶意爬虫攻击,因此需有一种手段来限制这些场景的并发/请求量,即限流。
目的
限流的目的是通过对并发连接/访问请求进行限量,或者对一个时间窗口内的请求进行限速来保护系统,一旦达到限制阈值或速率则可以拒绝服务(返回HTTP状态码、重定向到错误页或告知资源没有了)、排队或等待(比如秒杀、评论、下单)、降级(返回兜底数据或默认数据,如商品详情页库存默认有货)。
限流策略
1.IP
针对调用方IP进行限制,黑白名单、请求速率、带宽流量
2.EndPoint
系统平台开放的url、接口地址、服务域名等都可视为一个限流端点(EndPoint)对其进行不同粒度限制
3.UserToken
使用用户唯一标识限制用户级别的调用,常用唯一标识:
●Cookies\SessionId\Token:
会话唯一标识
●UserId:
用户唯一标识
●DeviceId:
用户设备号
4.客户端Key
使用调用方客户端的唯一标识,对第三方调用进行限制,常用标识:
●AppId:
第三方唯一ID
●Header\User-agent
●AppDomain:
第三方域名
●APPName:
第三方服务名
流控方式
1.针对资源的限制
●总并发数:
Web容器并发连接数、数据库连接池、应用线程池、文件句柄
●瞬时并发数:
Web服务器Http连接数,socket连接数等
●CPU\内存负载情况
●网络带宽流量情况
2.针对频率的限制
●时间窗口请求速率:
一定时间窗口期内的request请求速率(QPS\TPS)
●RPC调用速率
●MQ生产消费速率
●
总体概览
限流算法
1.计数器
简单直接,配合redis等缓存原子操作实现
2.漏桶
漏桶(LeakyBucket)算法思路很简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水(接口有响应速率),当水流入速度过大会直接溢出(访问频率超过接口响应速率),然后就拒绝请求,可以看出漏桶算法能强行限制数据的传输速率.示意图如下:
可见这里有两个变量,一个是桶的大小,支持流量突发增多时可以存多少的水(burst),另一个是水桶漏洞的大小(rate),在某些情况下,漏桶算法不能够有效地使用网络资源。
因为漏桶的漏出速率是固定的参数,所以,即使网络中不存在资源冲突(没有发生拥塞),漏桶算法也不能使某一个单独的流突发到端口速率。
因此,漏桶算法对于存在突发特性的流量来说缺乏效率。
而令牌桶算法则能够满足这些具有突发特性的流量。
通常,漏桶算法与令牌桶算法可以结合起来为网络流量提供更大的控制。
3.令牌桶
令牌桶算法(TokenBucket)和LeakyBucket效果一样但方向相反的算法,更加容易理解.随着时间流逝,系统会按恒定1/QPS时间间隔(如果QPS=100,则间隔是10ms)往桶里加入Token(想象和漏洞漏水相反,有个水龙头在不断的加水),如果桶已经满了就不再加了.新请求来临时,会各自拿走一个Token,如果没有Token可拿了就阻塞或者拒绝服务.
令牌桶的另外一个好处是可以方便的改变速度.一旦需要提高速率,则按需提高放入桶中的令牌的速率.一般会定时(比如100毫秒)往桶中增加一定数量的令牌,有些变种算法则实时的计算应该增加的令牌的数量.
限流技术
常用限流技术可针对其作用的位置分为如下几个层级:
代理层
基于网络的接入入口层的流量整形(Trafficshaping)策略进行控制,针对使用的负载均衡策略可分为:
●Nginx:
使用软负载,可利用Nginx自带的模块
ngx_http_limit_conn_module:
http连接限制,可对某个KEY对应的总的网络连接数进行限流,可以按照如IP、域名维度进行限流
ngx_http_limit_req_module:
request请求限制,可对某个KEY对应的请求的平均速率进行限流
ngx_stream_limit_conn_module:
流量限制
●Nginx+lua:
基于Nginxlua模块ngx_lua,或直接使用Openresty的lua-resty-limit-traffic,动态控制负责限流规则
Nginx-Lua-API:
●HAproxy:
●硬件负载均衡器(A10\F5):
略
API层
应用业务逻辑加入Ratelimit控制逻辑,实现方式可结合如下框架:
●Redis:
计数器
●Google-Guava:
google令牌桶算法开源框架,提供了限流工具类RateLimiter
●Gateway模式
线程层
线程级别对请求控制:
●线程池
●熔断器
●舱壁隔离技术
●信号量隔离技术
●降级方案
限流粒度
根据上文提到的限流策略,可将限流的粒度级别由粗到细做如下划分:
●IP级:
针对调用方IP限制
●应用级:
针对调用方(第三方)域名、租户级(AppId)
●业务级:
针对用户、应用服务url、开放接口方法地址
由上向下粒度范围逐级变小,限制更精确
限流方案
本文针对作用在代理层的限流控制,提供一套完整开放性平台限流技术解决方案。
现有方案
当前已有的限流方案调研:
●OpenResty——推荐
一组基于Lua开发的nginxmodule,开源后是对nginx相关功能的扩展,可视为对nginx的扩展插件,对nginx侵入性低、可跟随nginx主版本发布,兼容性好
http:
//openresty.org/cn/
●整合nginxmodule——较复杂
自行安装配置ngx_lua、LuaJIT及相关扩展依赖
●Tengine——定制化
淘宝开源webserver,是淘宝从nginxfork的一个分支,加入二次开发功能,依赖特定nginx主版本
●Kong——APIGateway
Kong是一个集成了Nginx的工具(也可嵌入Openresty整合运行),限流仅是其提供的功能之一,更多功能定位是偏向于在客户端和(微)服务间转发API通信的API网关(同样功能的开源软件还有Zuul),专为保护、管理和扩展业务侧API而设计。
同时通过插件(Lua编写)扩展功能,提供了日志记录,身份认证,流量控制、系统监控统计等功能。
Kong有两个主要组件:
◆KongServer:
基于 nginx 的服务器,用来接收API请求
◆DataStore:
基于ApacheCassandra(2.2.x )或PostgreSQL(9.4+) 存储Kong相关内部数据
以上相关方案提供了基本的限流、分流、降级等基础功能,但未实现规则的动态配置与更新等管理功能,因此基于以上问题提出如下解决构想,以整合限流系统与后台规则系统
核心思想
●前端:
1)动态模式:
基于Nginx+lua+redis技术对网络请求进行流量整形,动态拦截请求并匹配规则
2)静态模式:
基于Nginx的limit_conn与limit_req模块对IP、域名等固态规则拦截,以应对动态模式中redis出现异常导致规则库瘫痪时的兜底限流策略
注:
limit_conn与limit_req模块的限流拦截顺序优先于Lua实现的限流策略
●后端:
基于WebUI管理、配置、更新限流规则的后台系统,对限流规则进行可视化动态配置与刷新
总体架构
业务流程
通过Nginx模块拦截入口HTTP请求,先根据limit_conn与limit_req模块做静态拦截匹配校验,然后由lua模块从redis读取后台限流规则进行动态校验,规则读取策略可分为:
●静态固化规则:
类似限制总并发数、连接数、流量上限阈值、速率阈值等字典规则,可在nginx启动时加入初始化脚本从redis获取后存储在nginx本地共享缓存ngx.shared.DICT(lua_shared_dict),当规则更新时同步刷新本地缓存,以保证每次请求匹配规则时直接从本地缓存获取,避免每次都请求redis
●动态规则:
类似时间窗口内的接口调用次数阈值、用户请求频次等,涉及到修改数值“+1”运算类型的规则,可动态从redis每次请求获取并更新回redis
●本地缓存的设计:
1)Nginx启动时加入lua初始化脚本,在启动阶段完成从redis拉取限流规则,装载本地缓存,如发生异常,则在本地缓存标记限流开关关闭,从而不拦截请求,而走limit_conn与limit_req模块的固化静态规则兜底
2)本地缓存根据规则分区存储:
●静态字典规则:
固化数据,根据规则分类,开辟不同缓存区存储
●动态规则:
涉及到数值修改的规则,根据上层负载均衡策略选择使用本地缓存还是分布式限流缓存。
如负载均衡策略采用IP-Hash等可路由终端到同一个Nginx节点的策略时,可通过取模方式平均分配规则阈值,并完成每次请求拦截的数值修改操作,即将分布式限流转换为本地限流
3)本地缓存刷新:
◆定时器:
通过ngx.timer.at在nginx进程中注册定时器,定期回调方法刷新缓存
TimerDemo:
timer.lua
--nginx.conf:
in"http"tosetting:
--init_worker_by_lua_filelua/timer.lua
localdelay=120--inseconds
localnew_timer=ngx.timer.at
locallog=ngx.log
localINFO=ngx.INFO
localERR=ngx.ERR
localhandler
handler=function(premature,var)
--dobusiness
log(INFO,"=======>timerwork!
==="..ngx.worker.pid().."==="..tostring(premature).."==="..var)
ifprematurethen
--nginxreload:
premature==true
return
end
if0==ngx.worker.id()then
localok,err=new_timer(delay,handler,"test")
ifnotokthen
log(ERR,"failedtocreatetimer:
",err)
return
end
end
end
if0==ngx.worker.id()then
localok,err=new_timer(1,handler,"test")
ifnotokthen
log(ERR,"failedtocreatetimer:
",err)
return
end
end
◆接口触发:
通过Lua开放刷新本地ngx.shared.DICT的接口,外部调用此url,完成刷新操作
注:
●接口逻辑需校验认证安全,保证调用端的授权有效,防止非法调用
●接口需处理redis异常导致的刷新失败情况
请求时序
数据结构
ngx.shared.DICT
通过ngx.shared.DICT本地缓存可在Nginx中创建全局共享的一定大小的内存区,
参见:
特点:
●在nginx的多个worker进程间共享
●通过热加载方式(-sreload或HUP)重启nginx时,缓存不丢失,除非nginx进程退出才会失效
●缓存的容量超过预先申请的内存池大小的时,ngx.shared.DICT.set方法会尝试以LRU的形式淘汰一部分内容
●支持“原子级”(atomically)操作,保证线程安全
1.缓存分类
●开关类
●静态规则类
●动态规则类
●
2.运行机制
●启动时加载
●刷新机制:
定时刷新或接口触发刷新
Redis
限流逻辑
具体限流方式和流程策略如下:
限流开关
作用:
通过配置参数控制是否打开\关闭限流拦截,以达到动态开闭流控目的;
分类:
●总开关:
拦截入口总控制,判定请求是否进入限流拦截系统
●各级限流开关:
分别针对IP\域名级、应用服务级、API级等不同限制级别是否生效的开关
IP\域名黑白名单
作用:
定制服务端可访问IP或域名黑白名单,直接限制远程端对服务端的访问,同时也可防止恶意爬虫
限制级别:
IP\域名级
IP\域名访问频率
作用:
针对特定IP或域名限制访问频率
限制级别:
IP\域名级
服务并发数
作用:
定制当前应用对外提供的总并发连接数限制
限制级别:
应用级
服务访问频率
作用:
对特定外部调用服务名或第三方应用AppId进行总请求频率限制
限制级别:
应用级、App级
API访问频率
作用:
对调用特定API的外部服务或第三方应用进行总请求频率限制
限制级别:
API级(接口级)
规则管理
后台规则可视化管理系统
配置部署
OpenResty部署
1.下载安装包
wgethttps:
//openresty.org/download/openresty-1.11.2.2.tar.gz
2.依赖库安装:
将这些相关的库perl5.6.1+,libreadline,libpcre,libssl安装在系统中。
步骤:
1)输入以下命令,一次性安装需要的库
yuminstallreadline-develpcre-developenssl-develperl
2)相关库安装成功。
安装成功后会有“Complete!
”字样
3.解压编译安装:
tar-xzvfopenresty-1.11.2.2.tar.gz
cdopenresty-1.11.2.2/
./configure
make
makeinstall
安装完成后,会默认在/usr/local目录下安装openresty目录,结构如下:
4.配置Nginxconf文件:
viopenresty/nginx/conf/nginx.conf
worker_processes8;#根据cpu配置
error_loglogs/error.loginfo;#生产环境不配置info
events{
worker_connections65535;
}
http{
server{
listen80;
#test
location/hello{
default_typetext/html;
content_by_lua'
ngx.say("
hello,world
")
';
}
}
}
5.启动Nginx
./nginx/sbin/nginx
验证:
访问:
http:
//${nginx_host}/hello
6.热加载nginx.conf
./nginx/sbin/nginx-sreload
7.关闭nginx
./nginx/sbin/nginx-squit
8.查看日志:
tail-fnginx/logs/error.log
tail-fnginx/logs/access.log
9.
问题汇总
1.限流方案前方如配置A10\F5或nginx\LVS做负载或反向代理后可能产生的问题
1)A10自带流量整形策略,是否启用
2)Http请求接入方式:
域名?
IP(虚实)?
3)A10的负载均衡方式及Targetserver的心跳检查机制
4)A10路由策略,Ip-hash?
5)客户端真实IP(Client-Real-IP)的透传方式、参数名
6)区分不同服务请求的方法:
端口?
Url前缀?
7)Https的负载及路由策略
2.Lua访问redis的性能与限制、限流性能影响
3.Nginxconf文件reload问题
4.限流规则的匹配逻辑--流程
5.是否引入规则引擎及其执行过程
6.业务需求、粒度
7.运维、部署
8.与WAF整合
9.可能存在的安全隐患