最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Redis分布式限流生產(chǎn)環(huán)境落地方案

 更新時間:2026年02月06日 10:10:53   作者:三水不滴  
本文主要介紹了Redis分布式限流生產(chǎn)環(huán)境落地方案,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧

該方案適配Spring Boot 2.x/3.x + Spring Cloud微服務(wù)生態(tài),覆蓋Lua 腳本原子限流(固定窗口 / 滑動窗口)、Redisson 令牌桶 / 信號量限流Spring Cloud Gateway 網(wǎng)關(guān)統(tǒng)一限流、Redis 宕機(jī)兜底降級四大核心能力,兼顧原子性、動態(tài)配置、高可用、可監(jiān)控,可直接復(fù)制到生產(chǎn)環(huán)境并按需微調(diào)。

前置依賴

生產(chǎn)環(huán)境統(tǒng)一引入以下核心依賴(Maven),版本適配自身 Spring Cloud/Spring Boot 版本:

<!-- Redis核心依賴 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
<!-- Redisson分布式限流 -->
<dependency>
    <groupId>org.redisson</groupId>
    <artifactId>redisson-spring-boot-starter</artifactId>
    <version>3.23.3</version>
</dependency>
<!-- Spring Cloud Gateway網(wǎng)關(guān) -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
<!-- Nacos動態(tài)配置(限流規(guī)則熱更新) -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
</dependency>
<!-- 熔斷降級(Sentinel,可選替換為Resilience4j) -->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
<!-- 工具類 -->
<dependency>
    <groupId>cn.hutool</groupId>
    <artifactId>hutool-all</artifactId>
    <version>5.8.23</version>
</dependency>
<!-- 本地降級限流(Guava) -->
<dependency>
    <groupId>com.google.guava</groupId>
    <artifactId>guava</artifactId>
    <version>32.1.3-jre</version>
</dependency>

一、基礎(chǔ)環(huán)境配置

1. Redis 連接配置(application.yml)

包含連接池、序列化優(yōu)化,適配單機(jī) / 主從 / 集群:

spring:
  redis:
    host: 192.168.1.100 # 生產(chǎn)替換為Redis集群地址
    port: 6379
    password: prod_redis_123
    database: 2 # 限流專用庫,與業(yè)務(wù)隔離
    lettuce:
      pool:
        max-active: 50 # 最大連接數(shù),按QPS調(diào)整
        max-idle: 20
        min-idle: 5
        max-wait: 3000ms # 連接超時,生產(chǎn)建議3s內(nèi)
    timeout: 2000ms # Redis操作超時
  # Nacos配置(動態(tài)限流規(guī)則)
  cloud:
    nacos:
      config:
        server-addr: 192.168.1.101:8848
        namespace: prod
        group: DEFAULT_GROUP
        file-extension: yml
        shared-configs:
          - data-id: redis-limit-rules.yml # 限流規(guī)則統(tǒng)一配置文件
            group: DEFAULT_GROUP
            refresh: true # 支持熱更新

2. Redisson 配置(生產(chǎn)高可用版)

支持單機(jī) / 主從 / 哨兵 / 集群,這里以Redis Cluster為例(生產(chǎn)推薦),創(chuàng)建RedissonConfig.java

import org.redisson.Redisson;
import org.redisson.api.RedissonClient;
import org.redisson.config.Config;
import org.redisson.config.ReadMode;
import org.redisson.config.SubscriptionMode;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class RedissonConfig {
    // 從Nacos/配置文件讀取Redis集群地址
    @Value("${spring.redis.cluster.nodes}")
    private String clusterNodes;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.database:0}")
    private int database;

    @Bean(destroyMethod = "shutdown")
    public RedissonClient redissonClient() {
        Config config = new Config();
        // 集群模式配置(生產(chǎn)核心)
        config.useClusterServers()
                .addNodeAddress(clusterNodes.split(","))
                .setPassword(password)
                .setDatabase(database)
                .setScanInterval(2000) // 節(jié)點掃描間隔
                .setMasterConnectionPoolSize(50) // 主節(jié)點連接池
                .setSlaveConnectionPoolSize(20) // 從節(jié)點連接池
                .setReadMode(ReadMode.SLAVE) // 讀從節(jié)點,分擔(dān)壓力
                .setSubscriptionMode(SubscriptionMode.SLAVE); // 訂閱從節(jié)點
        // 超時配置,避免Redis阻塞
        config.setConnectTimeout(2000);
        config.setTimeout(2000);
        return Redisson.create(config);
    }
}

Nacos 補(bǔ)充配置:在application.yml添加 Redis 集群地址,支持熱更新:

spring:
  redis:
    cluster:
      nodes: redis://192.168.1.100:7001,redis://192.168.1.100:7002,redis://192.168.1.100:7003

二、Lua 腳本原子限流模板(固定窗口 + 滑動窗口)

依托 Redis單線程原子執(zhí)行 Lua 腳本,解決計數(shù)限流的并發(fā)問題,分為固定窗口(簡單高效,生產(chǎn) 80% 場景適用)和滑動窗口(解決固定窗口臨界超量問題,高精度場景適用),封裝通用調(diào)用工具類,支持接口 / IP / 用戶多維度限流。

1. 核心 Lua 腳本(放在resources/lua目錄下,生產(chǎn)建議統(tǒng)一管理)

(1)固定窗口計數(shù)限流limit_fixed_window.lua

核心邏輯:計數(shù) + 過期時間原子設(shè)置,達(dá)到閾值返回 0(拒絕),否則返回 1(允許),支持自定義窗口時長和閾值。

-- 固定窗口限流Lua腳本(原子性)
-- KEYS[1]:限流key(如limit:api:order:192.168.1.1)
-- ARGV[1]:限流閾值(如100)
-- ARGV[2]:窗口過期時間(秒,如60)
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local expire = tonumber(ARGV[2])

-- 原子計數(shù)
local current = redis.call('incr', key)
-- 首次計數(shù),設(shè)置過期時間(避免內(nèi)存泄漏)
if current == 1 then
    redis.call('expire', key, expire)
end
-- 超過閾值返回0,否則返回1
if current > limit then
    return 0
else
    return 1
end

(2)滑動窗口計數(shù)限流limit_slide_window.lua

核心邏輯:基于 ZSet 存儲請求時間戳,自動清理過期請求,統(tǒng)計窗口內(nèi)總請求數(shù),解決固定窗口臨界超量問題(如 1 分鐘窗口,59 秒和 1 秒各請求 100 次,固定窗口會允許 200 次,滑動窗口僅允許 100 次)。

-- 滑動窗口限流Lua腳本(原子性)
-- KEYS[1]:限流key(如limit:api:seckill:192.168.1.1)
-- ARGV[1]:限流閾值(如100)
-- ARGV[2]:窗口時長(毫秒,如60000)
-- ARGV[3]:當(dāng)前請求時間戳(毫秒,如System.currentTimeMillis())
local key = KEYS[1]
local limit = tonumber(ARGV[1])
local windowMs = tonumber(ARGV[2])
local now = tonumber(ARGV[3])
-- 窗口開始時間:當(dāng)前時間 - 窗口時長
local windowStart = now - windowMs

-- 1. 移除ZSet中過期的請求記錄(score < 窗口開始時間)
redis.call('zremrangebyscore', key, 0, windowStart)
-- 2. 統(tǒng)計當(dāng)前窗口內(nèi)的請求數(shù)
local current = redis.call('zcard', key)
-- 3. 超過閾值,返回0拒絕
if current >= limit then
    return 0
end
-- 4. 未超閾值,添加當(dāng)前請求到ZSet(score=時間戳,value=唯一標(biāo)識避免重復(fù))
redis.call('zadd', key, now, now .. math.random(10000))
-- 5. 設(shè)置ZSet過期時間,避免內(nèi)存泄漏(窗口時長+1秒)
redis.call('expire', key, math.ceil(windowMs / 1000) + 1)
return 1

2. Lua 腳本通用調(diào)用工具類RedisLimitLuaUtil.java

封裝腳本加載、參數(shù)組裝、Redis 調(diào)用邏輯,全局唯一,避免重復(fù)加載腳本,支持多維度限流 key 生成:

import cn.hutool.core.util.StrUtil;
import org.springframework.core.io.ClassPathResource;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scripting.support.ResourceScriptSource;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;
import java.util.Collections;
import java.util.Objects;

/**
 * Redis Lua腳本限流通用工具類(固定窗口+滑動窗口)
 */
@Component
public class RedisLimitLuaUtil {
    @Resource
    private RedisTemplate<String, Object> redisTemplate;

    // 固定窗口腳本
    private DefaultRedisScript<Long> fixedWindowScript;
    // 滑動窗口腳本
    private DefaultRedisScript<Long> slideWindowScript;

    // 限流key前綴,統(tǒng)一規(guī)范
    private static final String LIMIT_KEY_PREFIX = "limit:";

    /**
     * 初始化Lua腳本(項目啟動時加載,避免重復(fù)加載)
     */
    @PostConstruct
    public void initScript() {
        // 固定窗口腳本初始化
        fixedWindowScript = new DefaultRedisScript<>();
        fixedWindowScript.setResultType(Long.class);
        fixedWindowScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limit_fixed_window.lua")));

        // 滑動窗口腳本初始化
        slideWindowScript = new DefaultRedisScript<>();
        slideWindowScript.setResultType(Long.class);
        slideWindowScript.setScriptSource(new ResourceScriptSource(new ClassPathResource("lua/limit_slide_window.lua")));
    }

    /**
     * 固定窗口限流判斷
     * @param limitDimension 限流維度(api/ip/user)
     * @param target 限流目標(biāo)(如接口名order/create、IP192.168.1.1、用戶ID1001)
     * @param limit 限流閾值
     * @param expire 窗口過期時間(秒)
     * @return true=允許,false=拒絕
     */
    public boolean fixedWindowLimit(String limitDimension, String target, int limit, int expire) {
        try {
            String key = generateLimitKey(limitDimension, target);
            // 執(zhí)行Lua腳本,原子判斷
            Long result = redisTemplate.execute(
                    fixedWindowScript,
                    Collections.singletonList(key),
                    limit,
                    expire
            );
            // 結(jié)果為1則允許,0則拒絕
            return Objects.nonNull(result) && result == 1;
        } catch (Exception e) {
            // Redis異常時,返回false觸發(fā)降級策略
            log.error("固定窗口限流Redis執(zhí)行異常,維度:{},目標(biāo):{}", limitDimension, target, e);
            return false;
        }
    }

    /**
     * 滑動窗口限流判斷
     * @param limitDimension 限流維度(api/ip/user)
     * @param target 限流目標(biāo)
     * @param limit 限流閾值
     * @param windowMs 窗口時長(毫秒)
     * @return true=允許,false=拒絕
     */
    public boolean slideWindowLimit(String limitDimension, String target, int limit, long windowMs) {
        try {
            String key = generateLimitKey(limitDimension, target);
            long now = System.currentTimeMillis();
            // 執(zhí)行Lua腳本,原子判斷
            Long result = redisTemplate.execute(
                    slideWindowScript,
                    Collections.singletonList(key),
                    limit,
                    windowMs,
                    now
            );
            return Objects.nonNull(result) && result == 1;
        } catch (Exception e) {
            log.error("滑動窗口限流Redis執(zhí)行異常,維度:{},目標(biāo):{}", limitDimension, target, e);
            return false;
        }
    }

    /**
     * 生成限流key:limit:維度:目標(biāo)
     * 例:limit:ip:192.168.1.1、limit:api:order/create
     */
    private String generateLimitKey(String limitDimension, String target) {
        return StrUtil.format("{}{}:{}", LIMIT_KEY_PREFIX, limitDimension, target);
    }
}

3. 業(yè)務(wù)層調(diào)用示例(Controller/Service)

直接注入工具類,按需選擇固定 / 滑動窗口,一行代碼實現(xiàn)限流

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

import javax.annotation.Resource;
import javax.servlet.http.HttpServletRequest;

@RestController
@RequestMapping("/api/order")
public class OrderController {
    @Resource
    private RedisLimitLuaUtil redisLimitLuaUtil;
    @Resource
    private LimitDegradeUtil limitDegradeUtil; // 降級工具類(后續(xù)實現(xiàn))

    @PostMapping("/create")
    public Result createOrder(HttpServletRequest request) {
        // 1. 提取限流目標(biāo)(接口+IP雙維度,生產(chǎn)常用)
        String apiTarget = "order/create";
        String ipTarget = request.getRemoteAddr();
        // 2. 限流規(guī)則:接口每分鐘1000次,IP每分鐘100次
        boolean apiLimit = redisLimitLuaUtil.fixedWindowLimit("api", apiTarget, 1000, 60);
        boolean ipLimit = redisLimitLuaUtil.fixedWindowLimit("ip", ipTarget, 100, 60);
        // 3. 限流判斷+降級(Redis異常時觸發(fā)本地降級)
        if (!apiLimit || !ipLimit) {
            // 觸發(fā)降級:先嘗試本地兜底,失敗則返回限流提示
            if (!limitDegradeUtil.localLimitFallback(apiTarget)) {
                return Result.fail(429, "請求過于頻繁,請稍后再試");
            }
        }
        // 4. 執(zhí)行業(yè)務(wù)邏輯
        return Result.success("訂單創(chuàng)建成功");
    }
}

三、Redisson 限流核心模板(令牌桶 + 信號量)

Redisson 底層基于 Lua 腳本實現(xiàn)原子性,開箱即用支持令牌桶限流(應(yīng)對突發(fā)流量,如秒殺 / 網(wǎng)關(guān))和信號量限流(控制并發(fā)數(shù),如數(shù)據(jù)庫 / 中間件資源訪問),封裝通用工具類,支持全局唯一令牌池動態(tài)規(guī)則調(diào)整。

1. Redisson 限流通用工具類RedissonLimitUtil.java

封裝令牌桶(RateLimiter)和信號量(Semaphore)核心方法,生產(chǎn)建議單例,避免重復(fù)創(chuàng)建 Redisson 對象:

import org.redisson.api.RRateLimiter;
import org.redisson.api.RSemaphore;
import org.redisson.api.RateIntervalUnit;
import org.redisson.api.RateType;
import org.redisson.api.RedissonClient;
import org.springframework.stereotype.Component;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * Redisson限流工具類(令牌桶+信號量)
 * 令牌桶:控制QPS,支持突發(fā)流量
 * 信號量:控制并發(fā)數(shù),保護(hù)下游資源
 */
@Component
public class RedissonLimitUtil {
    @Resource
    private RedissonClient redissonClient;

    // 限流key前綴
    private static final String RATE_LIMIT_PREFIX = "rate:limit:";
    private static final String SEMAPHORE_PREFIX = "semaphore:limit:";

    /**
     * 令牌桶限流(獲取1個令牌,無超時)
     * @param target 限流目標(biāo)(如seckill/sku1001、gateway/api)
     * @param rate 令牌生成速率(如100)
     * @param rateInterval 令牌生成時間間隔(如1)
     * @param unit 時間單位(如SECONDS)
     * @param capacity 令牌桶最大容量(突發(fā)流量上限,如200)
     * @return true=獲取令牌成功,false=失敗
     */
    public boolean rateLimit(String target, int rate, int rateInterval, RateIntervalUnit unit, int capacity) {
        try {
            String key = RATE_LIMIT_PREFIX + target;
            // 獲取令牌桶實例(全局唯一,Redisson自動緩存)
            RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
            // 初始化令牌桶規(guī)則(僅首次執(zhí)行有效,后續(xù)修改需調(diào)用setRate)
            rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, unit, capacity);
            // 獲取1個令牌,無超時等待
            return rateLimiter.tryAcquire(1);
        } catch (Exception e) {
            log.error("Redisson令牌桶限流異常,目標(biāo):{}", target, e);
            return false;
        }
    }

    /**
     * 令牌桶限流(帶超時等待,適合高并發(fā)突發(fā)場景)
     * @param target 限流目標(biāo)
     * @param rate 生成速率
     * @param rateInterval 時間間隔
     * @param unit 時間單位
     * @param capacity 桶容量
     * @param waitTime 超時等待時間
     * @param waitUnit 等待時間單位
     * @return true=成功,false=失敗
     */
    public boolean rateLimitWithWait(String target, int rate, int rateInterval, RateIntervalUnit unit,
                                     int capacity, long waitTime, TimeUnit waitUnit) {
        try {
            String key = RATE_LIMIT_PREFIX + target;
            RRateLimiter rateLimiter = redissonClient.getRateLimiter(key);
            rateLimiter.trySetRate(RateType.OVERALL, rate, rateInterval, unit, capacity);
            return rateLimiter.tryAcquire(1, waitTime, waitUnit);
        } catch (Exception e) {
            log.error("Redisson令牌桶限流(帶等待)異常,目標(biāo):{}", target, e);
            return false;
        }
    }

    /**
     * 信號量限流(獲取1個許可,控制并發(fā)數(shù))
     * @param target 限流目標(biāo)(如resource/mysql、resource/kafka)
     * @param permits 最大許可數(shù)(最大并發(fā)數(shù))
     * @return true=成功,false=失敗
     */
    public boolean semaphoreLimit(String target, int permits) {
        try {
            String key = SEMAPHORE_PREFIX + target;
            RSemaphore semaphore = redissonClient.getSemaphore(key);
            // 初始化許可數(shù)(僅首次有效)
            if (!semaphore.isExists()) {
                semaphore.trySetPermits(permits);
            }
            // 獲取1個許可,無超時
            return semaphore.tryAcquire(1);
        } catch (Exception e) {
            log.error("Redisson信號量限流異常,目標(biāo):{}", target, e);
            return false;
        }
    }

    /**
     * 信號量限流(帶超時,釋放許可必須在finally中執(zhí)行)
     * @param target 限流目標(biāo)
     * @param permits 最大并發(fā)數(shù)
     * @param waitTime 超時時間
     * @param unit 時間單位
     * @return RSemaphore實例(用于釋放許可),null=獲取失敗
     */
    public RSemaphore semaphoreLimitWithWait(String target, int permits, long waitTime, TimeUnit unit) {
        try {
            String key = SEMAPHORE_PREFIX + target;
            RSemaphore semaphore = redissonClient.getSemaphore(key);
            if (!semaphore.isExists()) {
                semaphore.trySetPermits(permits);
            }
            if (semaphore.tryAcquire(1, waitTime, unit)) {
                return semaphore;
            }
        } catch (Exception e) {
            log.error("Redisson信號量限流(帶等待)異常,目標(biāo):{}", target, e);
        }
        return null;
    }

    /**
     * 釋放信號量許可(必須調(diào)用,避免資源泄漏)
     */
    public void releaseSemaphore(RSemaphore semaphore) {
        if (semaphore != null && semaphore.isAcquired()) {
            semaphore.release(1);
        }
    }
}

2. 核心場景調(diào)用示例

(1)令牌桶限流(秒殺場景,支持突發(fā)流量)

@RestController
@RequestMapping("/api/seckill")
public class SeckillController {
    @Resource
    private RedissonLimitUtil redissonLimitUtil;
    @Resource
    private LimitDegradeUtil limitDegradeUtil;

    @PostMapping("/buy/{skuId}")
    public Result seckill(@PathVariable String skuId) {
        String target = "seckill/sku" + skuId;
        // 限流規(guī)則:每秒生成100個令牌,桶容量200(支持200的突發(fā)流量)
        boolean acquire = redissonLimitUtil.rateLimitWithWait(
                target, 100, 1, RateIntervalUnit.SECONDS, 200,
                500, TimeUnit.MILLISECONDS // 超時等待500ms
        );
        if (!acquire) {
            if (!limitDegradeUtil.localLimitFallback(target)) {
                return Result.fail(429, "秒殺太火爆了,請稍后再試");
            }
        }
        // 執(zhí)行秒殺業(yè)務(wù)
        return Result.success("秒殺成功");
    }
}

(2)信號量限流(數(shù)據(jù)庫資源保護(hù),控制并發(fā)數(shù))

@Service
public class OrderQueryService {
    @Resource
    private RedissonLimitUtil redissonLimitUtil;
    @Resource
    private LimitDegradeUtil limitDegradeUtil;

    public List<Order> queryOrderByUserId(Long userId) {
        String target = "resource/mysql/orderQuery";
        // 限流規(guī)則:最大并發(fā)數(shù)20(避免數(shù)據(jù)庫連接池打滿)
        RSemaphore semaphore = redissonLimitUtil.semaphoreLimitWithWait(
                target, 20, 1, TimeUnit.SECONDS
        );
        if (semaphore == null) {
            if (!limitDegradeUtil.localLimitFallback(target)) {
                throw new BusinessException(429, "當(dāng)前查詢?nèi)藬?shù)過多,請稍后再試");
            }
        }
        // 必須在finally中釋放許可,避免資源泄漏
        try {
            // 執(zhí)行數(shù)據(jù)庫查詢
            return orderMapper.selectByUserId(userId);
        } finally {
            redissonLimitUtil.releaseSemaphore(semaphore);
        }
    }
}

四、Spring Cloud Gateway 網(wǎng)關(guān)整合限流模板

網(wǎng)關(guān)是分布式限流的最佳入口,實現(xiàn)全鏈路統(tǒng)一限流,避免限流邏輯散落在各個微服務(wù),這里整合Redisson 令牌桶限流(網(wǎng)關(guān)主流方案),支持路由級 / 全局級限流、Nacos 動態(tài)規(guī)則、多維度提取(接口 / IP / 用戶),返回標(biāo)準(zhǔn) HTTP 429 響應(yīng)。

1. 網(wǎng)關(guān)限流規(guī)則配置(Nacosredis-limit-rules.yml,支持熱更新)

# 網(wǎng)關(guān)限流規(guī)則(key=路由ID,與gateway路由配置一致)
gateway:
  limit:
    rules:
      order-service: # 訂單服務(wù)路由ID
        enable: true # 是否開啟限流
        target: gateway/order-service # 限流目標(biāo)
        rate: 200 # 令牌生成速率(QPS)
        rateInterval: 1 # 時間間隔(秒)
        capacity: 400 # 令牌桶容量
        waitTime: 500 # 超時等待時間(毫秒)
      seckill-service: # 秒殺服務(wù)路由ID
        enable: true
        target: gateway/seckill-service
        rate: 500
        rateInterval: 1
        capacity: 1000
        waitTime: 300
      default: # 全局默認(rèn)規(guī)則
        enable: true
        target: gateway/default
        rate: 1000
        rateInterval: 1
        capacity: 2000
        waitTime: 200

2. 限流規(guī)則實體類GatewayLimitRule.java

映射 Nacos 配置,支持配置熱更新

import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.util.Map;

/**
 * 網(wǎng)關(guān)限流規(guī)則配置(Nacos熱更新)
 */
@Data
@Component
@RefreshScope // 開啟配置熱更新
@ConfigurationProperties(prefix = "gateway.limit")
public class GatewayLimitRule {
    // key=路由ID,value=具體規(guī)則
    private Map<String, GatewayLimitItem> rules;

    @Data
    public static class GatewayLimitItem {
        private boolean enable; // 是否開啟限流
        private String target; // 限流目標(biāo)
        private int rate; // 令牌速率
        private int rateInterval; // 時間間隔
        private int capacity; // 桶容量
        private long waitTime; // 超時等待時間(毫秒)
    }
}

3. 網(wǎng)關(guān)全局限流過濾器GatewayLimitGlobalFilter.java

實現(xiàn)GlobalFilterOrdered,攔截所有網(wǎng)關(guān)請求,按路由 ID 匹配限流規(guī)則,原子執(zhí)行限流判斷,限流失敗直接返回 429 響應(yīng):

import cn.hutool.core.util.StrUtil;
import org.redisson.api.RateIntervalUnit;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.http.server.reactive.ServerHttpResponse;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import javax.annotation.Resource;
import java.util.Objects;

/**
 * Spring Cloud Gateway全局限流過濾器(Redisson令牌桶)
 * 優(yōu)先級最高,最先執(zhí)行
 */
@Component
public class GatewayLimitGlobalFilter implements GlobalFilter, Ordered {
    @Resource
    private RedissonLimitUtil redissonLimitUtil;
    @Resource
    private GatewayLimitRule gatewayLimitRule;
    @Resource
    private LimitDegradeUtil limitDegradeUtil;

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        ServerHttpRequest request = exchange.getRequest();
        ServerHttpResponse response = exchange.getResponse();
        // 1. 獲取當(dāng)前路由ID(gateway路由配置的ID)
        String routeId = exchange.getAttribute("org.springframework.cloud.gateway.support.ServerWebExchangeUtils.GATEWAY_ROUTE_ID");
        if (StrUtil.isBlank(routeId)) {
            routeId = "default"; // 無路由ID,使用默認(rèn)規(guī)則
        }
        // 2. 匹配限流規(guī)則
        GatewayLimitRule.GatewayLimitItem limitItem = gatewayLimitRule.getRules().get(routeId);
        if (Objects.isNull(limitItem) || !limitItem.isEnable()) {
            return chain.filter(exchange); // 未開啟限流,直接放行
        }
        // 3. 執(zhí)行令牌桶限流
        boolean acquire = redissonLimitUtil.rateLimitWithWait(
                limitItem.getTarget(),
                limitItem.getRate(),
                limitItem.getRateInterval(),
                RateIntervalUnit.SECONDS,
                limitItem.getCapacity(),
                limitItem.getWaitTime(),
                java.util.concurrent.TimeUnit.MILLISECONDS
        );
        // 4. 限流判斷+降級
        if (!acquire) {
            // 觸發(fā)本地降級,失敗則返回429
            if (!limitDegradeUtil.localLimitFallback(limitItem.getTarget())) {
                response.setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
                response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
                String errorMsg = "{"code":429,"msg":"網(wǎng)關(guān)限流:請求過于頻繁"}";
                return response.writeWith(Mono.just(response.bufferFactory().wrap(errorMsg.getBytes())));
            }
        }
        // 5. 放行,執(zhí)行后續(xù)過濾器
        return chain.filter(exchange);
    }

    /**
     * 過濾器優(yōu)先級:最高(Ordered.HIGHEST_PRECEDENCE)
     */
    @Override
    public int getOrder() {
        return Ordered.HIGHEST_PRECEDENCE;
    }
}

4. 網(wǎng)關(guān)路由配置(application.yml)

關(guān)聯(lián)限流規(guī)則的路由 ID,生產(chǎn)建議配合 Nacos 動態(tài)路由:

spring:
  cloud:
    gateway:
      routes:
        # 訂單服務(wù)路由(ID=order-service,與限流規(guī)則一致)
        - id: order-service
          uri: lb://order-service # 負(fù)載均衡到訂單服務(wù)
          predicates:
            - Path=/api/order/**
          filters:
            - StripPrefix=1 # 去除/api前綴
        # 秒殺服務(wù)路由(ID=seckill-service,與限流規(guī)則一致)
        - id: seckill-service
          uri: lb://seckill-service
          predicates:
            - Path=/api/seckill/**
          filters:
            - StripPrefix=1
        # 全局默認(rèn)路由
        - id: default
          uri: lb://common-service
          predicates:
            - Path=/**

五、生產(chǎn)環(huán)境降級策略模板(Redis 宕機(jī)兜底 + 限流降級)

限流的核心降級目標(biāo):Redis 宕機(jī) / 超時 / 壓力過大時,不影響核心業(yè)務(wù)可用,通過本地內(nèi)存限流(Guava RateLimiter) 做兜底,同時結(jié)合Sentinel 熔斷限制降級后的請求量,避免本地限流被擊穿。

1. 降級核心工具類LimitDegradeUtil.java

封裝本地令牌桶兜底限流指標(biāo)統(tǒng)計,支持按目標(biāo)單獨(dú)配置本地規(guī)則,避免全局本地限流被擊穿:

import com.google.common.util.concurrent.RateLimiter;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

/**
 * 限流降級工具類(Redis異常時本地兜底)
 * 基于Guava RateLimiter實現(xiàn)本地令牌桶限流
 */
@Component
public class LimitDegradeUtil {
    // 本地限流實例緩存:key=限流目標(biāo),value=Guava RateLimiter
    private final Map<String, RateLimiter> localRateLimiterMap = new ConcurrentHashMap<>();
    // 本地默認(rèn)QPS(可從Nacos動態(tài)配置)
    private static final double DEFAULT_LOCAL_QPS = 50.0;

    /**
     * 初始化核心目標(biāo)的本地限流規(guī)則(生產(chǎn)建議從Nacos加載)
     */
    @PostConstruct
    public void initLocalLimit() {
        // 秒殺服務(wù)本地兜底QPS=10
        localRateLimiterMap.put("seckill/sku1001", RateLimiter.create(10.0));
        // 訂單服務(wù)本地兜底QPS=20
        localRateLimiterMap.put("order/create", RateLimiter.create(20.0));
        // 網(wǎng)關(guān)全局本地兜底QPS=100
        localRateLimiterMap.put("gateway/default", RateLimiter.create(100.0));
    }

    /**
     * 本地限流兜底方法
     * @param target 限流目標(biāo)
     * @return true=獲取本地令牌成功,false=失敗
     */
    public boolean localLimitFallback(String target) {
        try {
            // 從緩存獲取本地限流實例,無則創(chuàng)建默認(rèn)實例
            RateLimiter rateLimiter = localRateLimiterMap.getOrDefault(target, RateLimiter.create(DEFAULT_LOCAL_QPS));
            // 嘗試獲取1個本地令牌(無超時)
            return rateLimiter.tryAcquire();
        } catch (Exception e) {
            log.error("本地限流兜底異常,目標(biāo):{}", target, e);
            return false;
        }
    }

    /**
     * 動態(tài)更新本地限流規(guī)則(支持Nacos熱更新調(diào)用)
     * @param target 限流目標(biāo)
     * @param qps 目標(biāo)QPS
     */
    public void updateLocalLimit(String target, double qps) {
        localRateLimiterMap.put(target, RateLimiter.create(qps));
        log.info("本地限流規(guī)則更新成功,目標(biāo):{},QPS:{}", target, qps);
    }
}

2. Redis 異常熔斷降級(Sentinel 整合)

通過Sentinel對 Redis 操作做熔斷,當(dāng) Redis 異常率達(dá)到閾值時,直接觸發(fā)本地降級,避免 Redis 異常拖垮整個服務(wù),添加SentinelConfig.java

import com.alibaba.csp.sentinel.annotation.aspectj.SentinelResourceAspect;
import com.alibaba.csp.sentinel.slots.block.RuleConstant;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRule;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeRuleManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.util.ArrayList;
import java.util.List;

/**
 * Sentinel熔斷配置(Redis操作熔斷)
 */
@Configuration
public class SentinelConfig {
    /**
     * 開啟Sentinel注解支持
     */
    @Bean
    public SentinelResourceAspect sentinelResourceAspect() {
        return new SentinelResourceAspect();
    }

    /**
     * 初始化Redis操作熔斷規(guī)則
     * 策略:異常比例熔斷,5秒內(nèi)異常率>50%,熔斷10秒
     */
    @PostConstruct
    public void initDegradeRules() {
        List<DegradeRule> rules = new ArrayList<>();
        // Redis Lua腳本限流熔斷規(guī)則
        DegradeRule luaRule = new DegradeRule();
        luaRule.setResource("redisLimitLua"); // 資源名,與@SentinelResource一致
        luaRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO); // 異常比例
        luaRule.setCount(0.5); // 異常比例閾值50%
        luaRule.setTimeWindow(10); // 熔斷時間10秒
        luaRule.setMinRequestAmount(10); // 最小請求數(shù):5秒內(nèi)至少10次請求才觸發(fā)

        // Redisson限流熔斷規(guī)則
        DegradeRule redissonRule = new DegradeRule();
        redissonRule.setResource("redissonLimit");
        redissonRule.setGrade(RuleConstant.DEGRADE_GRADE_EXCEPTION_RATIO);
        redissonRule.setCount(0.5);
        redissonRule.setTimeWindow(10);
        redissonRule.setMinRequestAmount(10);

        rules.add(luaRule);
        rules.add(redissonRule);
        DegradeRuleManager.loadRules(rules);
    }
}

3. 熔斷注解使用(修改限流工具類)

在 Lua/Redisson 限流工具類的核心方法添加@SentinelResource,指定熔斷降級方法

// 在RedisLimitLuaUtil的fixedWindowLimit方法添加
@SentinelResource(value = "redisLimitLua", fallback = "fixedWindowFallback")
public boolean fixedWindowLimit(String limitDimension, String target, int limit, int expire) {
    // 原有邏輯
}
// 熔斷降級方法(參數(shù)與原方法一致)
public boolean fixedWindowFallback(String limitDimension, String target, int limit, int expire) {
    log.warn("Redis Lua限流觸發(fā)Sentinel熔斷,觸發(fā)本地降級,目標(biāo):{}", target);
    return false; // 返回false觸發(fā)本地兜底
}

// 在RedissonLimitUtil的rateLimit方法添加
@SentinelResource(value = "redissonLimit", fallback = "rateLimitFallback")
public boolean rateLimit(String target, int rate, int rateInterval, RateIntervalUnit unit, int capacity) {
    // 原有邏輯
}
// 熔斷降級方法
public boolean rateLimitFallback(String target, int rate, int rateInterval, RateIntervalUnit unit, int capacity) {
    log.warn("Redisson令牌桶限流觸發(fā)Sentinel熔斷,觸發(fā)本地降級,目標(biāo):{}", target);
    return false;
}

六、生產(chǎn)環(huán)境通用配置 & 監(jiān)控埋點

1. Redis 序列化優(yōu)化(避免默認(rèn) JDK 序列化問題)

創(chuàng)建RedisConfig.java,替換為Jackson2JsonRedisSerializer,節(jié)省內(nèi)存且避免序列化兼容問題:

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
public class RedisConfig {
    @Bean
    public RedisTemplate<String, Object> redisTemplate(RedisConnectionFactory factory) {
        RedisTemplate<String, Object> template = new RedisTemplate<>();
        template.setConnectionFactory(factory);
        // JSON序列化器
        Jackson2JsonRedisSerializer<Object> jacksonSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.activateDefaultTyping(om.getPolymorphicTypeValidator(), ObjectMapper.DefaultTyping.NON_FINAL);
        jacksonSerializer.setObjectMapper(om);
        // String序列化器
        StringRedisSerializer stringSerializer = new StringRedisSerializer();
        // key采用String序列化
        template.setKeySerializer(stringSerializer);
        template.setHashKeySerializer(stringSerializer);
        // value采用JSON序列化
        template.setValueSerializer(jacksonSerializer);
        template.setHashValueSerializer(jacksonSerializer);
        template.afterPropertiesSet();
        return template;
    }
}

2. 限流監(jiān)控埋點(Micrometer+Prometheus)

添加監(jiān)控指標(biāo),統(tǒng)計限流次數(shù)、Redis 異常次數(shù)、本地降級次數(shù),方便 Grafana 可視化和告警:

import io.micrometer.core.instrument.Counter;
import io.micrometer.core.instrument.MeterRegistry;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import javax.annotation.Resource;

/**
 * 限流監(jiān)控指標(biāo)工具類
 */
@Component
public class LimitMonitorUtil {
    @Resource
    private MeterRegistry meterRegistry;
    // 限流拒絕次數(shù)
    private Counter limitRejectCounter;
    // Redis異常次數(shù)
    private Counter redisErrorCounter;
    // 本地降級次數(shù)
    private Counter localDegradeCounter;

    @PostConstruct
    public void initCounter() {
        limitRejectCounter = Counter.builder("redis.limit.reject.count")
                .description("Redis分布式限流拒絕請求次數(shù)")
                .register(meterRegistry);
        redisErrorCounter = Counter.builder("redis.limit.error.count")
                .description("Redis分布式限流Redis操作異常次數(shù)")
                .register(meterRegistry);
        localDegradeCounter = Counter.builder("redis.limit.local.degrade.count")
                .description("Redis分布式限流本地降級觸發(fā)次數(shù)")
                .register(meterRegistry);
    }

    // 記錄限流拒絕
    public void recordLimitReject() {
        limitRejectCounter.increment();
    }

    // 記錄Redis異常
    public void recordRedisError() {
        redisErrorCounter.increment();
    }

    // 記錄本地降級
    public void recordLocalDegrade() {
        localDegradeCounter.increment();
    }
}

使用示例:在限流判斷失敗處調(diào)用監(jiān)控方法:

if (!apiLimit || !ipLimit) {
    limitMonitorUtil.recordLimitReject(); // 記錄限流拒絕
    if (!limitDegradeUtil.localLimitFallback(apiTarget)) {
        limitMonitorUtil.recordLocalDegrade(); // 記錄本地降級
        return Result.fail(429, "請求過于頻繁");
    }
}

七、生產(chǎn)環(huán)境落地關(guān)鍵注意事項

  1. Redis 高可用:必須使用Redis 主從 + 哨兵Redis Cluster,開啟RDB+AOF 混合持久化,避免 Redis 單點故障導(dǎo)致限流失效;
  2. 限流 key 設(shè)計:遵循前綴:維度:目標(biāo)規(guī)范,避免鍵沖突,同時設(shè)置過期時間,防止 Redis 內(nèi)存泄漏;
  3. 避免熱點 key:對高并發(fā)限流 key(如秒殺接口)采用分段限流(如按 IP 哈希到不同 key:limit:seckill:sku1001:{hash(ip)%10}),分擔(dān) Redis 單節(jié)點壓力;
  4. 動態(tài)配置:所有限流規(guī)則(閾值、窗口、QPS)均從Nacos/Apollo加載,支持熱更新,無需重啟服務(wù);
  5. 異常隔離:Redis 操作添加超時時間(生產(chǎn)建議 200-500ms),避免 Redis 阻塞導(dǎo)致服務(wù)線程池耗盡;
  6. 降級兜底:本地限流的 QPS 必須遠(yuǎn)低于分布式限流,避免本地兜底被擊穿,同時結(jié)合 Sentinel 做熔斷;
  7. 監(jiān)控告警:對redis.limit.reject.count、redis.limit.error.count設(shè)置告警閾值,及時發(fā)現(xiàn)限流規(guī)則不合理或 Redis 故障;
  8. 壓測驗證:上線前通過 JMeter/Gatling 做壓測,驗證限流規(guī)則的有效性和 Redis 的性能瓶頸。

總結(jié)

該模板是 Redis 分布式限流的生產(chǎn)級開箱即用方案,核心亮點:

  1. 雙核心限流:Lua 腳本(輕量原子)+ Redisson(開箱即用),覆蓋 99% 生產(chǎn)場景;
  2. 網(wǎng)關(guān)統(tǒng)一限流:Spring Cloud Gateway 整合,實現(xiàn)全鏈路入口限流,避免邏輯散落;
  3. 高可用降級:Redis 宕機(jī) / 異常時自動觸發(fā) Guava 本地兜底,結(jié)合 Sentinel 熔斷,保證服務(wù)可用;
  4. 可配置可監(jiān)控:Nacos 動態(tài)配置規(guī)則,Micrometer 埋點監(jiān)控,支持告警和可視化;
  5. 規(guī)范可擴(kuò)展:統(tǒng)一的 key 設(shè)計、工具類封裝,支持多維度限流(接口 / IP / 用戶 / 租戶)和自定義擴(kuò)展。

使用時僅需根據(jù)自身業(yè)務(wù)調(diào)整限流規(guī)則降級 QPSRedis 部署方式,即可快速落地到生產(chǎn)環(huán)境。

到此這篇關(guān)于Redis分布式限流生產(chǎn)環(huán)境落地方案的文章就介紹到這了,更多相關(guān)Redis 分布式限流內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • redis連接報錯error:NOAUTH Authentication required

    redis連接報錯error:NOAUTH Authentication required

    本文主要介紹了redis連接報錯error:NOAUTH Authentication required,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-05-05
  • 基于Redission的分布式鎖實戰(zhàn)

    基于Redission的分布式鎖實戰(zhàn)

    本文主要介紹了基于Redission的分布式鎖實戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-08-08
  • springboot中操作redis實例分享

    springboot中操作redis實例分享

    本文介紹了如何在Spring?Boot應(yīng)用中整合Redis緩存技術(shù),包括配置Redis連接、定義Redis模板、實現(xiàn)Redis的基本操作以及使用Spring?Cache注解。這些內(nèi)容可幫助開發(fā)者快速掌握Spring?Boot與Redis的集成,并提高應(yīng)用性能。
    2023-06-06
  • Redis查看KEY的數(shù)據(jù)類型的方法和步驟

    Redis查看KEY的數(shù)據(jù)類型的方法和步驟

    在Redis中,可以使用 TYPE 命令來查看指定key的數(shù)據(jù)類型,該命令會返回存儲在指定key中的值的數(shù)據(jù)類型,本文給大家介紹了具體的使用方法和步驟,感興趣的朋友可以參考下
    2024-04-04
  • redis yml配置的用法小結(jié)

    redis yml配置的用法小結(jié)

    RedisYML配置是Redis的一種配置文件格式,,對Redis的配置進(jìn)行統(tǒng)一管理,本文就來介紹了redis yml配置的用法小結(jié),具有一定的參考價值,感興趣的可以了解一下
    2024-02-02
  • redis+lua實現(xiàn)分布式限流的示例

    redis+lua實現(xiàn)分布式限流的示例

    本文主要介紹了redis+lua實現(xiàn)分布式限流的示例,可以實現(xiàn)復(fù)雜的限流邏輯,如滑動窗口限流,并且避免了多步操作導(dǎo)致的并發(fā)問題,具有一定的參考價值,感興趣的可以了解一下
    2025-03-03
  • Redis整合SpringBoot的RedisTemplate實現(xiàn)類(實例詳解)

    Redis整合SpringBoot的RedisTemplate實現(xiàn)類(實例詳解)

    這篇文章主要介紹了Redis整合SpringBoot的RedisTemplate實現(xiàn)類,本文給大家介紹的非常詳細(xì),對大家的學(xué)習(xí)或工作具有一定的參考借鑒價值,需要的朋友可以參考下
    2021-01-01
  • Govern Service 基于 Redis 的服務(wù)治理平臺安裝過程詳解

    Govern Service 基于 Redis 的服務(wù)治理平臺安裝過程詳解

    Govern Service 是一個輕量級、低成本的服務(wù)注冊、服務(wù)發(fā)現(xiàn)、 配置服務(wù) SDK,通過使用現(xiàn)有基礎(chǔ)設(shè)施中的 Redis 不用給運(yùn)維部署帶來額外的成本與負(fù)擔(dān),接下來通過本文給大家分享Govern Service 基于 Redis 的服務(wù)治理平臺的相關(guān)知識,感興趣的朋友一起看看吧
    2021-05-05
  • 解析Redis未授權(quán)訪問漏洞復(fù)現(xiàn)與利用危害

    解析Redis未授權(quán)訪問漏洞復(fù)現(xiàn)與利用危害

    這篇文章主要介紹了Redis未授權(quán)訪問漏洞復(fù)現(xiàn)與利用,介紹了redis未授權(quán)訪問漏洞的基本概念及漏洞的危害,本文給大家介紹的非常詳細(xì),需要的朋友可以參考下
    2022-01-01
  • Redis實現(xiàn)短信登錄的企業(yè)實戰(zhàn)

    Redis實現(xiàn)短信登錄的企業(yè)實戰(zhàn)

    本文主要介紹了Redis實現(xiàn)短信登錄的企業(yè)實戰(zhàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2022-07-07

最新評論

长泰县| 始兴县| 崇文区| 漯河市| 阆中市| 定结县| 乐清市| 长丰县| 馆陶县| 屏东市| 武城县| 阳江市| 龙陵县| 屯留县| 同德县| 长子县| 九龙县| 九龙坡区| 收藏| 平谷区| 资中县| 日喀则市| 大荔县| 简阳市| 农安县| 赣榆县| 若尔盖县| 威宁| 定结县| 晋州市| 石柱| 梅州市| 南平市| 屯昌县| 玉林市| 三明市| 茶陵县| 保德县| 慈溪市| 南宁市| 穆棱市|