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
相关产品推荐
相关产品推荐

