You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Spring Cloud网关:用Hazelcast替代Redis实现请求限流示例

用Hazelcast替换Redis实现Spring Cloud Gateway请求限流

下面是完整的实现步骤,和你原Redis版本的限流逻辑对齐,仅替换缓存为Hazelcast:

1. 引入依赖

在pom.xml中添加Hazelcast和Spring Cloud Gateway的核心依赖:

<!-- Hazelcast Spring 集成依赖 -->
<dependency>
    <groupId>com.hazelcast</groupId>
    <artifactId>hazelcast-spring</artifactId>
</dependency>
<!-- Spring Cloud Gateway 依赖 -->
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

2. 配置Hazelcast实例

通过配置类初始化Hazelcast集群实例,按需调整集群名称和其他参数:

import com.hazelcast.config.Config;
import com.hazelcast.core.Hazelcast;
import com.hazelcast.core.HazelcastInstance;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

@Configuration
public class HazelcastConfig {

    @Bean
    public HazelcastInstance hazelcastInstance() {
        Config config = new Config();
        // 设置集群名称,可根据环境调整
        config.setClusterName("gateway-rate-limiter-cluster");
        // 可添加网络、持久化等额外配置
        return Hazelcast.newHazelcastInstance(config);
    }
}

3. 保留(或定义)限流KeyResolver

和原Redis版本一致,这里以按请求IP生成限流Key为例:

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import reactor.core.publisher.Mono;

@Configuration
public class RateLimiterKeyConfig {

    @Bean
    public KeyResolver ipKeyResolver() {
        return exchange -> Mono.just(
                exchange.getRequest().getRemoteAddress().getAddress().getHostAddress()
        );
    }
}

4. 实现Hazelcast版的RateLimiter

自定义实现RateLimiter接口,基于Hazelcast分布式Map存储限流计数:

import org.springframework.cloud.gateway.filter.ratelimit.RateLimiter;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;
import com.hazelcast.core.HazelcastInstance;
import com.hazelcast.map.IMap;
import java.time.Instant;
import java.util.concurrent.TimeUnit;

@Component("hazelcastRateLimiter")
public class HazelcastRateLimiter implements RateLimiter<String> {

    private final HazelcastInstance hazelcastInstance;
    // 示例限流参数,可通过配置文件注入
    private static final int MAX_REQUESTS_PER_WINDOW = 10;
    private static final int WINDOW_DURATION_SECONDS = 1;

    public HazelcastRateLimiter(HazelcastInstance hazelcastInstance) {
        this.hazelcastInstance = hazelcastInstance;
    }

    @Override
    public Mono<Response> isAllowed(String routeId, String key) {
        String mapKey = String.format("%s:%s", routeId, key);
        IMap<String, RateLimitCounter> counterMap = hazelcastInstance.getMap("gateway-rate-limits");

        return Mono.fromCallable(() -> {
            RateLimitCounter counter = counterMap.get(mapKey);
            Instant now = Instant.now();

            if (counter == null) {
                // 首次请求,初始化计数器
                counter = new RateLimitCounter(1, now.plusSeconds(WINDOW_DURATION_SECONDS));
                counterMap.put(mapKey, counter, WINDOW_DURATION_SECONDS, TimeUnit.SECONDS);
                return buildResponse(true, 1, MAX_REQUESTS_PER_WINDOW);
            }

            if (now.isAfter(counter.getWindowEndTime())) {
                // 时间窗口到期,重置计数器
                counter.setCount(1);
                counter.setWindowEndTime(now.plusSeconds(WINDOW_DURATION_SECONDS));
                counterMap.put(mapKey, counter, WINDOW_DURATION_SECONDS, TimeUnit.SECONDS);
                return buildResponse(true, 1, MAX_REQUESTS_PER_WINDOW);
            }

            if (counter.getCount() < MAX_REQUESTS_PER_WINDOW) {
                // 计数+1,更新缓存
                counter.setCount(counter.getCount() + 1);
                counterMap.put(mapKey, counter);
                return buildResponse(true, counter.getCount(), MAX_REQUESTS_PER_WINDOW);
            }

            // 超过限流阈值,返回拒绝
            return buildResponse(false, counter.getCount(), MAX_REQUESTS_PER_WINDOW);
        });
    }

    // 封装限流响应
    private Response buildResponse(boolean allowed, int currentCount, int maxRequests) {
        Response.Header headers = new Response.Header(maxRequests, maxRequests - currentCount);
        return new Response(allowed, headers);
    }

    // 序列化的计数器实体,用于Hazelcast存储
    private static class RateLimitCounter implements java.io.Serializable {
        private int count;
        private Instant windowEndTime;

        public RateLimitCounter(int count, Instant windowEndTime) {
            this.count = count;
            this.windowEndTime = windowEndTime;
        }

        // getter/setter
        public int getCount() { return count; }
        public void setCount(int count) { this.count = count; }
        public Instant getWindowEndTime() { return windowEndTime; }
        public void setWindowEndTime(Instant windowEndTime) { this.windowEndTime = windowEndTime; }
    }
}

5. 配置Gateway的RequestRateLimiter过滤器

修改路由配置,指定使用自定义的Hazelcast限流实现,替换原Redis配置:

spring:
  cloud:
    gateway:
      routes:
        # 其他路由配置...
        - id: your_target_route_id
          uri: http://your-service-endpoint
          predicates:
            - Path=/your-api-path/**
          filters:
            # 其他过滤器...
            - name: RequestRateLimiter
              args:
                rate-limiter: "#{@hazelcastRateLimiter}" # 指定自定义的Hazelcast限流Bean
                key-resolver: "#{@ipKeyResolver}" # 关联之前定义的KeyResolver

如果需要将限流参数(最大请求数、时间窗口)放到配置文件,可通过@ConfigurationProperties注入,避免硬编码。

内容的提问来源于stack exchange,提问作者spyder

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 02:50:54