两个简单的API限流实现方案
最近的工作中需要对我们提供的一个API进行限流来保证服务的稳定行。
参考网络,提出了两个简单的方案:
1, Ngnix限流
Nginx在架构中起到请求转发与负载均衡器的作用。外部req首先到Nginx监听的80端口,然后Nginx将req交给到监听8080端口的APP服务器处理。处理结果再经由Nginx返回给调用方。
Nginx限流的配置:(/usr/local/etc/nginx/nginx.conf)
#user nobody; worker_processes 1; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; sendfile on; keepalive_timeout 65; # Rate limitation conf limit_req_zone $binary_remote_addr zone=mylimit:1m rate=1r/s; server { listen 80; server_name localhost; location / { root html; index index.html index.htm; } error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } # rate limitation and redirection to APP location ~* /(inventories|prices) { limit_req zone=mylimit; proxy_pass http://localhost:8080; } } include servers/*; }
其中, limit_req_zone $binary_remote_addr zone=mylimit:1m rate=1r/s;
将流量限制为 1QPS,如调用方超过该限制则返回 503
重启nginx: sudo nginx -s reload
参考:https://www.nginx.com/blog/rate-limiting-nginx/
2, Redis 计数器
原理: 每个req都对redis中生命周期为一个时间单位的计数器(key:callLimit)加1,如果单位时间内的访问次数超过限制,则拒绝❌所有后来的请求直到下一个单位时间开始。
public Mono<ServerResponse> req_handle(ServerRequest request) { if (callLimitCheck()){ return getResp(request){ … business logics to handle the req … }; } else { return ServerResponse.ok().body(Mono.just(“Over call limit!”), String.class); } } private boolean callLimitCheck() { String callLimit = this.template.opsForValue().get("callLimit"); if(callLimit != null && Integer.valueOf(callLimit)>30) { return false; } DefaultRedisScript<String> script = new DefaultRedisScript<>( "local current current = redis.call('incr',KEYS[1]) if tonumber(current) == 1 then redis.call('expire', KEYS[1], 60) end"); script.setResultType(String.class); List<String> keys = new ArrayList<>(); keys.add("callLimit"); this.template.execute(script, keys); return true; }
上面的代码将有效的QPS限制为 0.5/s ( 30/m).
其中为了避免race condition,将 incr 与 expire两个操作写到一个Lua脚本中实现原子性。
"local current current = redis.call('incr',KEYS[1]) if tonumber(current) == 1 then redis.call('expire', KEYS[1], 60) end"