Redis和Nginx實(shí)現(xiàn)限制接口請(qǐng)求頻率的示例
前言
為啥需要限制接口請(qǐng)求頻率?這個(gè)是因?yàn)榉乐菇涌谝恢北凰?,比如發(fā)送手機(jī)驗(yàn)證碼的接口,一直被刷的話(huà),費(fèi)錢(qián)費(fèi)資源的,至少做點(diǎn)基本的防護(hù)工作。以下分別使用Redis和Nginx實(shí)現(xiàn)限制接口請(qǐng)求頻率方案。
一、基于Redis實(shí)現(xiàn)接口限流
1.ZADD 命令
(1)用法:ZADD key score_1 value_1 score_2 value_2 ...
(2)作用:將一個(gè)或多個(gè)成員元素及其分?jǐn)?shù)值加入到有序集當(dāng)中。某個(gè)成員已經(jīng)是有序集的成員,那么更新這個(gè)成員的分?jǐn)?shù)值,并通過(guò)重新插入這個(gè)成員元素,來(lái)保證該成員在正確的位置上。分?jǐn)?shù)值可以是整數(shù)值或雙精度浮點(diǎn)數(shù)。
(3)返回值:被成功添加的新成員的數(shù)量,不包括那些被更新的、已經(jīng)存在的成員。
(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)作用:移除有序集中的一個(gè)或多個(gè)成員,不存在的成員將被忽略。
(3)返回值:被成功添加的新成員的數(shù)量,不包括那些被更新的、已經(jīng)存在的成員。
(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)作用:獲取有序集合中成員的數(shù)量。
(3)返回值:當(dāng)key存在且是有序集類(lèi)型時(shí),返回有序集的基數(shù)。 當(dāng)key不存在時(shí),返回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)作用:移除有序集中,指定分?jǐn)?shù)區(qū)間內(nèi)的所有成員。
(3)返回值:被移除成員的數(shù)量。
(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.具體實(shí)現(xiàn)
(1)新建一個(gè)過(guò)濾器,如【/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; // 時(shí)間窗口,單位為秒
@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); // 刪除有序集合中分?jǐn)?shù)在指定范圍內(nèi)的元素,返回刪除元素的數(shù)量
stringRedisTemplate.opsForZSet().removeRangeByScore(key, 0, beforeTime); // 刪除有序集合中60秒之前存進(jìn)去的所有數(shù)據(jù),如:
// Long memberNum = stringRedisTemplate.opsForZSet().size(K key); // 獲取有序集合中元素的數(shù)量
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); // 向有序集合中添加一個(gè)或多個(gè)元素,并指定其分?jǐn)?shù)
stringRedisTemplate.opsForZSet().add(key, String.valueOf(currentTime), currentTime);
// Boolean expireFlag = stringRedisTemplate.expire(K key, long timeout, TimeUnit unit); // 對(duì)指定key的數(shù)據(jù)設(shè)置過(guò)期時(shí)間
stringRedisTemplate.expire(key, TIME_WINDOW, TimeUnit.SECONDS);
return true;
}
}
}
(2)在SpringMVC配置類(lèi)中注入此過(guò)濾器,如【/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.運(yùn)行效果
// ~
二、基于Nginx實(shí)現(xiàn)接口限流
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;
# 負(fù)載均衡
upstream springboot {
server xxx.xxx.xxx.xxx:8080;
}
# limit_req_zone $binary_remote_addr zone=limit_zone:10m rate=10r/s; # 定義了一個(gè)名為limit_zone的限流區(qū)域,使用IP地址進(jìn)行限流,該區(qū)域的大小為10MB,限流速率為10個(gè)請(qǐng)求每秒。
limit_req_zone $binary_remote_addr zone=limit_zone:10m rate=10r/m; # 定義了一個(gè)名為limit_zone的限流區(qū)域,使用IP地址進(jìn)行限流,該區(qū)域的大小為10MB,限流速率為10個(gè)請(qǐng)求每分鐘。
# 80端口的服務(wù)
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指令進(jìn)行限流,限流區(qū)域?yàn)閘imit_zone,同時(shí)設(shè)置了一個(gè)瞬時(shí)突發(fā)流量為20個(gè)請(qǐng)求的閾值。
# 這樣,當(dāng)同一個(gè)IP地址在一秒鐘內(nèi)發(fā)送超過(guò)10個(gè)請(qǐng)求到此接口時(shí),Nginx會(huì)返回503錯(cuò)誤碼,表示請(qǐng)求被限流了。
# limit_req zone=limit_zone burst=20;
# 在/xxx/api/sendCode接口的location中使用limit_req指令進(jìn)行限流,限流區(qū)域?yàn)閘imit_zone,同時(shí)設(shè)置了一個(gè)瞬時(shí)突發(fā)流量為5個(gè)請(qǐng)求的閾值。
# 這樣,當(dāng)同一個(gè)IP地址在一秒鐘內(nèi)發(fā)送超過(guò)10個(gè)請(qǐng)求到此接口時(shí),如果在一分鐘內(nèi)有超過(guò)10個(gè)請(qǐng)求,則允許其中的5個(gè)請(qǐng)求通過(guò),nodelay表示不延遲響應(yīng),即立即返回503錯(cuò)誤碼。
limit_req zone=limit_zone burst=5 nodelay;
proxy_pass http://springboot;
}
}
}
2.運(yùn)行效果
// ~
到此這篇關(guān)于Redis和Nginx實(shí)現(xiàn)限制接口請(qǐng)求頻率的示例的文章就介紹到這了,更多相關(guān)Redis和Nginx限制接口請(qǐng)求頻率內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
- Spring使用Redis限制用戶(hù)登錄失敗的次數(shù)及暫時(shí)鎖定用戶(hù)登錄權(quán)限功能
- Redis實(shí)現(xiàn)驗(yàn)證碼發(fā)送并限制每日發(fā)送次數(shù)的示例代碼
- 如何利用 Redis 實(shí)現(xiàn)接口頻次限制
- spring boot+ redis 接口訪問(wèn)頻率限制的實(shí)現(xiàn)
- Redis密碼設(shè)置與訪問(wèn)限制實(shí)現(xiàn)方法
- 基于Redis實(shí)現(xiàn)每日登錄失敗次數(shù)限制
- Redis實(shí)戰(zhàn)記錄之限制操作頻率
- Python利用redis限制用戶(hù)重復(fù)刷新帶來(lái)的數(shù)據(jù)問(wèn)題
相關(guān)文章
利用Redis如何實(shí)現(xiàn)自動(dòng)補(bǔ)全功能
這篇文章主要給大家介紹了關(guān)于如何利用Redis如何實(shí)現(xiàn)自動(dòng)補(bǔ)全功能的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家學(xué)習(xí)或者使用Redis具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2019-09-09
redis發(fā)布訂閱_動(dòng)力節(jié)點(diǎn)Java學(xué)院整理
這篇文章主要介紹了redis發(fā)布訂閱,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08
redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?
本文主要介紹了redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?Authentication?required數(shù)據(jù)操作異常的解決方法,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05
Redis如何使用樂(lè)觀鎖(CAS)保證數(shù)據(jù)一致性
本文主要介紹了Redis如何使用樂(lè)觀鎖(CAS)保證數(shù)據(jù)一致性,文中通過(guò)示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-03-03

