中文字幕av专区_日韩电影在线播放_精品国产精品久久一区免费式_av在线免费观看网站

溫馨提示×

溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊×
其他方式登錄
點擊 登錄注冊 即表示同意《億速云用戶服務條款》

SpingBoot中怎么利用Redis對接口限流

發布時間:2021-07-02 13:50:25 來源:億速云 閱讀:167 作者:Leah 欄目:開發技術

這期內容當中小編將會給大家帶來有關SpingBoot中怎么利用Redis對接口限流,文章內容豐富且以專業的角度為大家分析和敘述,閱讀完這篇文章希望大家可以有所收獲。

實現的思路

使用 Hash 存儲接口的限流配置

request_limit_config    "/api2" : {"limit": 10, "time": 1, "timeUnit": "SECONDS"}

hash中的key就是請求的uri路徑,value是一個對象。通過3個屬性,描述限制策略

  • limit 最多請求次數

  • time 時間

  • timeUnit 時間單位

使用普通kv,存儲api的請求次數

request_limit:/api  1

處理請求的時候,通過increment對該key進行 +1 操作,如果返回1,則表示是第一次請求,此時設置它的過期時間。為限制策略中定義時間限制信息。再通過命名的返回值,判斷是否超出了限制。

increment 指令是線程安全的,不用擔心并發的問題。

使用SpringBoot實現

創建SpringBoot工程,添加

spring-boot-starter-data-redis依賴,并且給出正確的配置。

這里不做工程的創建,配置,以及其他額外代碼的演示,僅僅給出關鍵的代碼。

RedisKeys

定義兩個Key,限流用到的2個Key

public interface RedisKeys {
    /**
     * api的限制配置,hash key
     */
    String REQUEST_LIMIT_CONFIG = "request_limit_config";

    /**
     * api的請求的次數
     */
    String REQUEST_LIMIT = "request_limit";
}

ObjectRedisTemplate

為了提高hash value的序列化效率,自定義一個RedisTemplate的實現。使用jdk的序列化,而不是json。

import org.springframework.data.redis.core.RedisTemplate;

public class ObjectRedisTemplate extends RedisTemplate<String, Object> {

}

RedisConfigration

把自定義的ObjectRedisTemplate配置到IOC

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.RedisSerializer;

import io.springboot.jwt.redis.ObjectRedisTemplate;

@Configuration
public class RedisConfiguration {
    @Bean
    public ObjectRedisTemplate objectRedisTemplate(@Autowired RedisConnectionFactory redisConnectionFactory) {

        ObjectRedisTemplate objectRedisTemplate = new ObjectRedisTemplate();
        objectRedisTemplate.setConnectionFactory(redisConnectionFactory);

        objectRedisTemplate.setKeySerializer(RedisSerializer.string());
        objectRedisTemplate.setValueSerializer(RedisSerializer.java());

        // hash的key使用String序列化
        objectRedisTemplate.setHashKeySerializer(RedisSerializer.string());
        // hash的value使用jdk的序列化
        objectRedisTemplate.setHashValueSerializer(RedisSerializer.java());
        return objectRedisTemplate;
    }
}

RequestLimitConfig

用于描述限制策略的對象。

import java.io.Serializable;
import java.util.concurrent.TimeUnit;

public class RequestLimitConfig implements Serializable {

    /**
     * 
     */
    private static final long serialVersionUID = 1101875328323558092L;

    // 最大請求次數
    private long limit;
    // 時間
    private long time;
    // 時間單位
    private TimeUnit timeUnit;
    public RequestLimitConfig() {
        super();
    }
    public RequestLimitConfig(long limit, long time, TimeUnit timeUnit) {
        super();
        this.limit = limit;
        this.time = time;
        this.timeUnit = timeUnit;
    }
    public long getLimit() {
        return limit;
    }
    public void setLimit(long limit) {
        this.limit = limit;
    }
    public long getTime() {
        return time;
    }
    public void setTime(long time) {
        this.time = time;
    }
    public TimeUnit getTimeUnit() {
        return timeUnit;
    }
    public void setTimeUnit(TimeUnit timeUnit) {
        this.timeUnit = timeUnit;
    }
    @Override
    public String toString() {
        return "RequestLimitConfig [limit=" + limit + ", time=" + time + ", timeUnit=" + timeUnit + "]";
    }
}

RequestLimitInterceptor

通過攔截器,來完成限流的實現。

import java.nio.charset.StandardCharsets;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.MediaType;
import org.springframework.util.StringUtils;
import org.springframework.web.servlet.handler.HandlerInterceptorAdapter;

import io.springboot.jwt.redis.ObjectRedisTemplate;
import io.springboot.jwt.redis.RedisKeys;
import io.springboot.jwt.web.RequestLimitConfig;

public class RequestLimitInterceptor extends HandlerInterceptorAdapter {

    private static final Logger LOGGER = LoggerFactory.getLogger(RequestLimitInterceptor.class);

    @Autowired
    private ObjectRedisTemplate objectRedisTemplate;

    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {

        /**
         * 獲取到請求的URI
         */
        String contentPath = request.getContextPath();
        String uri = request.getRequestURI().toString();
        if (!StringUtils.isEmpty(contentPath) && !contentPath.equals("/")) {
            uri =  uri.substring(uri.indexOf(contentPath) + contentPath.length());
        }
        LOGGER.info("uri={}",  uri);

        /**
         * 嘗試從hash中讀取得到當前接口的限流配置
         */
        RequestLimitConfig requestLimitConfig = (RequestLimitConfig) this.objectRedisTemplate.opsForHash().get(RedisKeys.REQUEST_LIMIT_CONFIG, uri);
        if (requestLimitConfig == null) {
            LOGGER.info("該uri={}沒有限流配置", uri);
            return true;
        }

        String limitKey = RedisKeys.REQUEST_LIMIT + ":" + uri;

        /**
         * 當前接口的訪問次數 +1
         */
        long count = this.objectRedisTemplate.opsForValue().increment(limitKey);
        if (count == 1) {
            /**
             * 第一次請求,設置key的過期時間
             */
            this.objectRedisTemplate.expire(limitKey, requestLimitConfig.getTime(), requestLimitConfig.getTimeUnit());
            LOGGER.info("設置過期時間:time={}, timeUnit={}", requestLimitConfig.getTime(), requestLimitConfig.getTimeUnit());
        }

        LOGGER.info("請求限制。limit={}, count={}", requestLimitConfig.getLimit(), count);

        if (count > requestLimitConfig.getLimit()) {
            /**
             * 限定時間內,請求超出限制,響應客戶端錯誤信息。
             */
            response.setContentType(MediaType.TEXT_PLAIN_VALUE);
            response.setCharacterEncoding(StandardCharsets.UTF_8.name());
            response.getWriter().write("服務器繁忙,稍后再試");
            return false;
        }
        return true;
    }
}

Controller

一個用于測試的接口類

import java.util.Collections;

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/test")
public class TestController {

    @GetMapping
    public Object test () {
        return Collections.singletonMap("success", true);
    }
}

WebMvcConfigration

攔截器的配置

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

import io.springboot.jwt.web.interceptor.RequestLimitInterceptor;

@Configuration
public class WebMvcConfiguration implements WebMvcConfigurer {

    @Override
    public void addInterceptors(InterceptorRegistry registry) {
        registry.addInterceptor(this.requestLimitInterceptor())
            .addPathPatterns("/test");
    }

    @Bean
    public RequestLimitInterceptor requestLimitInterceptor() {
        return new RequestLimitInterceptor();
    }
}

通過@Test測試,初始化一個限流配置

@Autowired
private ObjectRedisTemplate objectRedisTemplate;

@Test
public void test () {
    // 3秒內,只能請求2次
    RequestLimitConfig requestLimitConfig = new RequestLimitConfig(2, 3, TimeUnit.SECONDS);
    // 限制的uri是 /test
    this.objectRedisTemplate.opsForHash().put(RedisKeys.REQUEST_LIMIT_CONFIG, "/test", requestLimitConfig);
}

使用瀏覽器演示

SpingBoot中怎么利用Redis對接口限流

最后一些問題

怎么靈活的配置

都寫到這個份兒上了,如果熟悉Redis以及客戶端,我想提供一個“限流管理”接口的并不是難事兒。

針對指定的用戶限流

這里演示的方法是,針對接口的限流。有時候,也有一些特殊的需求,需要“針對不同”的用戶來做限流。打個比方。針對A用戶,允許有他1分鐘請求20次接口,針對B用戶,允許他1分鐘請求10次接口。 這個其實也簡單,只需要修改一下上面的兩個限制key,在key中添加用戶的唯一標識(例如:ID)

request_limit_config    "/api2:{userId}" : {"limit": 10, "time": 1, "timeUnit": "SECONDS"}
request_limit:{userId}:/api  1

在攔截器中獲取到用戶的ID,加上用戶ID進行檢索和判斷,就可以完成針對用戶的限流。

Restful 接口的問題

@GetMapping("/user/{id}")  // restful的檢索接口,往往把ID信息放在了URI中

這就會導致上面的代碼有問題,因為這里采用的是根據URI來完成的限流操作。檢索不同ID的用戶,會導致URI不同。 解決辦法我認為也很簡單。那就不要使用URI,可以通過 自定義注解,方式,不同的接口,定義不同的唯一標識。在攔截器中獲取到注解,讀取到唯一的編碼,代替原來的URI,即可。

上述就是小編為大家分享的SpingBoot中怎么利用Redis對接口限流了,如果剛好有類似的疑惑,不妨參照上述分析進行理解。如果想知道更多相關知識,歡迎關注億速云行業資訊頻道。

向AI問一下細節

免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。

AI

德令哈市| 旬阳县| 定边县| 永宁县| 海伦市| 阿拉善右旗| 咸宁市| 宁津县| 兴城市| 扎鲁特旗| 佛教| 揭西县| 阿巴嘎旗| 澄城县| 珠海市| 新竹市| 醴陵市| 秦皇岛市| 罗平县| 大港区| 泰宁县| 乡城县| 朝阳市| 宣城市| 九龙坡区| 黔南| 高邑县| 正定县| 鄂托克旗| 微博| 区。| 昭觉县| 布尔津县| 明星| 改则县| 连州市| 上栗县| 孝义市| 阿尔山市| 广河县| 宁陵县|