Redis和Nginx實現(xiàn)限制接口請求頻率的示例
前言
為啥需要限制接口請求頻率?這個是因為防止接口一直被刷,比如發(fā)送手機(jī)驗證碼的接口,一直被刷的話,費錢費資源的,至少做點基本的防護(hù)工作。以下分別使用Redis和Nginx實現(xiàn)限制接口請求頻率方案。
一、基于Redis實現(xiàn)接口限流
1.ZADD 命令
(1)用法:ZADD key score_1 value_1 score_2 value_2 ...
(2)作用:將一個或多個成員元素及其分?jǐn)?shù)值加入到有序集當(dāng)中。某個成員已經(jīng)是有序集的成員,那么更新這個成員的分?jǐn)?shù)值,并通過重新插入這個成員元素,來保證該成員在正確的位置上。分?jǐn)?shù)值可以是整數(shù)值或雙精度浮點數(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)作用:移除有序集中的一個或多個成員,不存在的成員將被忽略。
(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存在且是有序集類型時,返回有序集的基數(shù)。 當(dāng)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)作用:移除有序集中,指定分?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.具體實現(xiàn)
(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); // 刪除有序集合中分?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); // 向有序集合中添加一個或多個元素,并指定其分?jǐn)?shù) stringRedisTemplate.opsForZSet().add(key, String.valueOf(currentTime), currentTime); // Boolean expireFlag = stringRedisTemplate.expire(K key, long timeout, TimeUnit unit); // 對指定key的數(shù)據(jù)設(shè)置過期時間 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實現(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; # 定義了一個名為limit_zone的限流區(qū)域,使用IP地址進(jìn)行限流,該區(qū)域的大小為10MB,限流速率為10個請求每秒。 limit_req_zone $binary_remote_addr zone=limit_zone:10m rate=10r/m; # 定義了一個名為limit_zone的限流區(qū)域,使用IP地址進(jìn)行限流,該區(qū)域的大小為10MB,限流速率為10個請求每分鐘。 # 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ū)域為limit_zone,同時設(shè)置了一個瞬時突發(fā)流量為20個請求的閾值。 # 這樣,當(dāng)同一個IP地址在一秒鐘內(nèi)發(fā)送超過10個請求到此接口時,Nginx會返回503錯誤碼,表示請求被限流了。 # limit_req zone=limit_zone burst=20; # 在/xxx/api/sendCode接口的location中使用limit_req指令進(jìn)行限流,限流區(qū)域為limit_zone,同時設(shè)置了一個瞬時突發(fā)流量為5個請求的閾值。 # 這樣,當(dāng)同一個IP地址在一秒鐘內(nèi)發(fā)送超過10個請求到此接口時,如果在一分鐘內(nèi)有超過10個請求,則允許其中的5個請求通過,nodelay表示不延遲響應(yīng),即立即返回503錯誤碼。 limit_req zone=limit_zone burst=5 nodelay; proxy_pass http://springboot; } } }
2.運行效果
// ~
到此這篇關(guān)于Redis和Nginx實現(xiàn)限制接口請求頻率的示例的文章就介紹到這了,更多相關(guān)Redis和Nginx限制接口請求頻率內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
redis發(fā)布訂閱_動力節(jié)點Java學(xué)院整理
這篇文章主要介紹了redis發(fā)布訂閱,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-08-08redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?
本文主要介紹了redis.clients.jedis.exceptions.JedisDataException:?NOAUTH?Authentication?required數(shù)據(jù)操作異常的解決方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2024-05-05Redis如何使用樂觀鎖(CAS)保證數(shù)據(jù)一致性
本文主要介紹了Redis如何使用樂觀鎖(CAS)保證數(shù)據(jù)一致性,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-03-03