前言
其它项目组需要调用接口,添加接口限流,防止项目被狂掉宕机。生产用了openresty,所以在openresty上添加按接口限流,同时,需按照不同接口有不同的限流规则,使用openresty中内置的漏桶算法方式限流。
漏桶算法
漏桶算法思路简单,水(请求)先进入到漏桶里,漏桶以一定的速度出水,当水流入速度过大会直接溢出,可以看出漏桶算法能强行限制数据的传输速率。
通俗解释。
比如设置rate为100,burst为50,即允许1s放进来100个请求,桶大小为50。
那么1s内:
第1-100个请求会访问成功。
第101-150个请求会进入burst。
第150个请求之后的会直接失败返回。
openresty的说明文档:https://github.com/openresty/lua-resty-limit-traffic/blob/master/lib/resty/limit/req.md
限流配置
使用OpenResty的漏桶算法进行限流配置,不同接口配置不同的标准,所以测试了两个接口test1和test2
主要分两步:
- 添加限流使用的lua脚本
- 在反向代理中配置限流的lua脚本
ps: 因为大多数使用情况还是会反向代理,所以直接在反向代理中配置lua
添加lua脚本01和02的区别,仅限于漏桶的参数配置不同
添加lua脚本01
在 lualib\utils
路径下创建lua脚本
lua脚本内容:
lua
-- utils/limit_req_leaky_bucket.lua
local limit_req = require "resty.limit.req"
-- rate: 5/s即为每秒3个请求,增加桶容量为1/s,超过5/s不到(5+1)/s的delay,排队等候
local lim, err = limit_req.new("my_limit_req_store1", 5, 1)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local _M = {}
function _M.incoming()
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503) -- 超过的请求直接返回503
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
-- 此方法返回,当前请求需要delay秒后才会被处理,和他前面对请求数
-- 所以此处对桶中请求进行延时处理,让其排队等待,就是应用了漏桶算法
-- 此处也是与令牌桶的主要区别
if delay >= 0.001 then
ngx.sleep(delay)
end
end
return _M
添加lua脚本02
在 lualib\utils
路径下创建lua脚本
lua脚本内容:
lua
-- utils/limit_req_leaky_bucket.lua
local limit_req = require "resty.limit.req"
-- rate: 3/s即为每秒3个请求,增加桶容量为1/s,超过3/s不到(3+1)/s的delay,排队等候
local lim, err = limit_req.new("my_limit_req_store2", 3, 1)
if not lim then
ngx.log(ngx.ERR, "failed to instantiate a resty.limit.req object: ", err)
return ngx.exit(500)
end
local _M = {}
function _M.incoming()
local key = ngx.var.binary_remote_addr
local delay, err = lim:incoming(key, true)
if not delay then
if err == "rejected" then
return ngx.exit(503) -- 超过的请求直接返回503
end
ngx.log(ngx.ERR, "failed to limit req: ", err)
return ngx.exit(500)
end
-- 此方法返回,当前请求需要delay秒后才会被处理,和他前面对请求数
-- 所以此处对桶中请求进行延时处理,让其排队等待,就是应用了漏桶算法
-- 此处也是与令牌桶的主要区别
if delay >= 0.001 then
ngx.sleep(delay)
end
end
return _M
在nginx.conf中添加配置文件
# --- 限流 ---
worker_processes 1;
events {
worker_connections 1024;
}
# ------------
http {
# --- 反向代理 ---
include /etc/nginx/conf.d/*.conf;
# -----------------------
include mime.types;
default_type application/octet-stream;
sendfile on;
#keepalive_timeout 0;
keepalive_timeout 65;
# --- 限流 ---
lua_code_cache on;
# 共享内存
lua_shared_dict my_limit_req_store1 100M;
lua_shared_dict my_limit_req_store2 100M;
# -----------
# --- 反向代理 ---
upstream backend_server {
server 127.0.0.1:8080;
}
# -----------------------
server {
listen 80;
server_name localhost;
#charset koi8-r;
#access_log logs/host.access.log main;
location / {
root html;
index index.html index.htm;
}
location /test1 {
# --- 限流 ---
access_by_lua_block {
local limit_count = require "utils.limit_req_leaky_bucket1"
-- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。
if ngx.req.is_internal() then
return
end
limit_count.incoming()
}
# ------------
# --- 反向代理 ---
# 如果内容源是反向代理
proxy_pass http://backend_server;
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 60;
proxy_read_timeout 600;
proxy_send_timeout 600;
# -----------------------
}
location /test2 {
# --- 限流 ---
access_by_lua_block {
local limit_count = require "utils.limit_req_leaky_bucket2"
-- 对于内部重定向或子请求,不进行限制。因为这些并不是真正对外的请求。
if ngx.req.is_internal() then
return
end
limit_count.incoming()
}
# ------------
# --- 反向代理 ---
# 如果内容源是反向代理
proxy_pass http://backend_server;
proxy_set_header Host $host;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_connect_timeout 60;
proxy_read_timeout 600;
proxy_send_timeout 600;
# -----------------------
}
}
}