Redis和Nginx實現限制接口請求頻率的示例
前言
為啥需要限制接口請求頻率?這個是因為防止接口一直被刷,比如發(fā)送手機驗證碼的接口,一直被刷的話,費錢費資源的,至少做點基本的防護工作。以下分別使用Redis和Nginx實現限制接口請求頻率方案。
一、基于Redis實現接口限流
1.ZADD 命令
(1)用法:ZADD key score_1 value_1 score_2 value_2 ...
(2)作用:將一個或多個成員元素及其分數值加入到有序集當中。某個成員已經是有序集的成員,那么更新這個成員的分數值,并通過重新插入這個成員元素,來保證該成員在正確的位置上。分數值可以是整數值或雙精度浮點數。
(3)返回值:被成功添加的新成員的數量,不包括那些被更新的、已經存在的成員。
(4)示例
redis > ZADD runoobkey 1 redis (integer) 1 redis > ZADD runoobkey 2 mongodb (integer) 1 redis > ZADD runoobkey 3 mysql (integer) 1 redis > ZADD runoobkey 3 mysql (integer) 0 redis > ZADD runoobkey 4 mysql (integer) 0 redis > ZRANGE runoobkey 0 10 WITHSCORES 1) "redis" 2) "1" 3) "mongodb" 4) "2" 5) "mysql" 6) "4"
2.ZREM 命令
(1)用法:ZREM key value_1 value_2 ...
(2)作用:移除有序集中的一個或多個成員,不存在的成員將被忽略。
(3)返回值:被成功添加的新成員的數量,不包括那些被更新的、已經存在的成員。
(4)示例
redis > ZRANGE runoobkey 0 10 WITHSCORES 1) "redis" 2) "1" 3) "mongodb" 4) "2" 5) "mysql" 6) "4" redis > ZREM mongodb (integer) 1 redis > ZRANGE runoobkey 0 10 WITHSCORES 1) "redis" 2) "1" 3) "mysql" 4) "4"
3.ZCARD 命令
(1)用法:ZCARD key
(2)作用:獲取有序集合中成員的數量。
(3)返回值:當key存在且是有序集類型時,返回有序集的基數。 當key不存在時,返回0 。
(4)示例
redis > ZADD myzset 1 "one" (integer) 1 redis > ZADD myzset 2 "two" (integer) 1 redis > ZCARD myzset (integer) 2
4.ZREMRANGEBYSCORE 命令
(1)用法:ZREMRANGEBYSCORE key min max
(2)作用:移除有序集中,指定分數區(qū)間內的所有成員。
(3)返回值:被移除成員的數量。
(4)示例
redis > ZRANGE salary 0 -1 WITHSCORES 1) "tom" 2) "2000" 3) "peter" 4) "3500" 5) "jack" 6) "5000" redis > ZREMRANGEBYSCORE salary 1500 3500 (integer) 2 redis> ZRANGE salary 0 -1 WITHSCORES 1) "jack" 2) "5000"
5.具體實現
(1)新建一個過濾器,如【/src/main/java/org/example/interceptor/RateLimiterInterceptor.java】
package org.example.interceptor;
import cn.hutool.json.JSONObject;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.web.servlet.HandlerInterceptor;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.HashMap;
import java.util.concurrent.TimeUnit;
/**
* 限流攔截器
*/
@Component
public class RateLimiterInterceptor implements HandlerInterceptor {
private static final String RATE_LIMITER_PREFIX = "Rate-Limiter:";
private static final int LIMIT = 10; // 限流閾值
private static final int TIME_WINDOW = 60; // 時間窗口,單位為秒
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
response.setCharacterEncoding("utf-8");
response.setContentType("application/json");
String key = RATE_LIMITER_PREFIX + request.getRequestURI() + ":" + request.getRemoteAddr(); // Rate-Limiter:/api/sendCode:127.0.0.1
long currentTime = System.currentTimeMillis(); // 1703036748554
long beforeTime = currentTime - TIME_WINDOW * 1000; // 1703036748554 - 60000 = 1703036688554
// Long removeNum = stringRedisTemplate.opsForZSet().removeRangeByScore(K key, double min, double max); // 刪除有序集合中分數在指定范圍內的元素,返回刪除元素的數量
stringRedisTemplate.opsForZSet().removeRangeByScore(key, 0, beforeTime); // 刪除有序集合中60秒之前存進去的所有數據,如:
// Long memberNum = stringRedisTemplate.opsForZSet().size(K key); // 獲取有序集合中元素的數量
long count = stringRedisTemplate.opsForZSet().size(key); // 3
if (count >= LIMIT) {
HashMap<String, Object> responseObj = new HashMap<>();
responseObj.put("code", HttpStatus.TOO_MANY_REQUESTS.value());
responseObj.put("success", false);
responseObj.put("msg", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());
JSONObject json = new JSONObject(responseObj);
response.getWriter().println(json);
return false;
} else {
// Boolean addFlag = stringRedisTemplate.opsForZSet().add(K var1, V var2, double var3); // 向有序集合中添加一個或多個元素,并指定其分數
stringRedisTemplate.opsForZSet().add(key, String.valueOf(currentTime), currentTime);
// Boolean expireFlag = stringRedisTemplate.expire(K key, long timeout, TimeUnit unit); // 對指定key的數據設置過期時間
stringRedisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);
return true;
}
}
}
(2)在SpringMVC配置類中注入此過濾器,如【/src/main/java/org/example/config/ResourceConfig.java】
package org.example.config;
import org.example.interceptor.RateLimiterInterceptor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
@Configuration
public class ResourceConfig extends WebMvcConfigurationSupport {
@Autowired
private RateLimiterInterceptor rateLimiterInterceptor;
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(rateLimiterInterceptor).addPathPatterns("/abcd/api/sendCode");
}
}
6.運行效果
// ~
二、基于Nginx實現接口限流
1.在nginx.conf文件中新增限流配置
user nginx;
worker_processes 1;
error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;
events {
worker_connections 1024;
}
http {
include /etc/nginx/mime.types;
default_type application/octet-stream;
log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';
access_log /var/log/nginx/access.log main;
sendfile on;
#tcp_nopush on;
keepalive_timeout 65;
#gzip on;
include /etc/nginx/conf.d/*.conf;
# 負載均衡
upstream springboot {
server xxx.xxx.xxx.xxx:8080;
}
# limit_req_zone $binary_remote_addr zone=limit_zone:10m rate=10r/s; # 定義了一個名為limit_zone的限流區(qū)域,使用IP地址進行限流,該區(qū)域的大小為10MB,限流速率為10個請求每秒。
limit_req_zone $binary_remote_addr zone=limit_zone:10m rate=10r/m; # 定義了一個名為limit_zone的限流區(qū)域,使用IP地址進行限流,該區(qū)域的大小為10MB,限流速率為10個請求每分鐘。
# 80端口的服務
server {
listen 80;
server_name xxx.xxx.xxx.xxx;
location / {
alias html;
index index.html index.htm;
try_files $uri $uri/ /love/index.html;
proxy_pass http://localhost;
}
location ^~ /love/ {
root html/love;
index index.html index.htm;
proxy_pass http://localhost;
}
location ^~ /abcd/api/sendCode {
# 在/xxx/api/sendCode接口的location中使用limit_req指令進行限流,限流區(qū)域為limit_zone,同時設置了一個瞬時突發(fā)流量為20個請求的閾值。
# 這樣,當同一個IP地址在一秒鐘內發(fā)送超過10個請求到此接口時,Nginx會返回503錯誤碼,表示請求被限流了。
# limit_req zone=limit_zone burst=20;
# 在/xxx/api/sendCode接口的location中使用limit_req指令進行限流,限流區(qū)域為limit_zone,同時設置了一個瞬時突發(fā)流量為5個請求的閾值。
# 這樣,當同一個IP地址在一秒鐘內發(fā)送超過10個請求到此接口時,如果在一分鐘內有超過10個請求,則允許其中的5個請求通過,nodelay表示不延遲響應,即立即返回503錯誤碼。
limit_req zone=limit_zone burst=5 nodelay;
proxy_pass http://springboot;
}
}
}
2.運行效果
// ~
到此這篇關于Redis和Nginx實現限制接口請求頻率的示例的文章就介紹到這了,更多相關Redis和Nginx限制接口請求頻率內容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
redis發(fā)布訂閱_動力節(jié)點Java學院整理
這篇文章主要介紹了redis發(fā)布訂閱,小編覺得挺不錯的,現在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08
redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?
本文主要介紹了redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?Authentication?required數據操作異常的解決方法,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2024-05-05

