我在我的 spring 云 api 网关中使用 redis 实现了速率限制。这是
application.yml
的一部分:
spring:
cloud:
gateway:
httpclient:
ssl:
useInsecureTrustManager: true
discovery:
locator:
enabled: true
routes:
- id: test-rest-service
uri: lb://test-rest-service
predicates:
- Path=/test/**
filters:
- RewritePath=/test/(?<path>.*), /$\{path}
- name: RequestRateLimiter
args:
key-resolver: "#{@userRemoteAddressResolver}"
redis-rate-limiter.replenishRate: 2
redis-rate-limiter.burstCapacity: 3
我通过邮递员调用了 GET API 并检查了响应标头。
X-RateLimit-Remaining -1
X-RateLimit-Burst-Capacity 3
X-RateLimit-Replenish-Rate 2
速率限制不起作用。为什么我得到
X-RateLimit-Remaining
的负值?这是什么意思?我该如何解决?
这发生在我身上,因为没有启动 Redis 实例。你有两个选择:
1) 使用 docker 下载并运行 Redis 实例:
docker run --name redis -d redis
2) 您可以通过添加 maven 依赖项来测试嵌入式 Redis 服务器,正如以下文章 中所解释的那样:
<dependency>
<groupId>it.ozimov</groupId>
<artifactId>embedded-redis</artifactId>
<version>0.7.2</version>
<scope>test</scope>
</dependency>
包括以下片段:
@TestConfiguration
public class TestRedisConfiguration {
private RedisServer redisServer;
public TestRedisConfiguration() {
this.redisServer = new RedisServer(6379);
}
@PostConstruct
public void postConstruct() {
redisServer.start();
}
@PreDestroy
public void preDestroy() {
redisServer.stop();
}
}
我最近遇到了同样的问题。就我而言,安装了旧版本的 Redis,导致 X-RateLimit-Remaining 不断设置为 -1。
redis-cli shutdown
原因:SCG(Spring Cloud Gateway)无法连接redis服务器
修复:将 SCG 连接到 redis 服务器以获取正确的标头
您可以通过在 application.yml 中添加以下属性来查看日志
logging:
level:
root: DEBUG
执行请求时,您将看到以下日志(并且该请求响应将带有 X-RateLimit-Remaining -1)。
2023-04-02T15:56:19.293+05:00 DEBUG 860131 --- [or-http-epoll-3] io.lettuce.core.RedisClient : Trying to get a Redis connection for: redis://localhost:50989?timeout=1s
2023-04-02T15:56:19.545+05:00 DEBUG 860131 --- [or-http-epoll-3] i.l.c.r.DefaultEventLoopGroupProvider : Allocating executor io.netty.channel.epoll.EpollEventLoopGroup
2023-04-02T15:56:19.545+05:00 DEBUG 860131 --- [or-http-epoll-3] i.l.c.r.DefaultEventLoopGroupProvider : Creating executor io.netty.channel.epoll.EpollEventLoopGroup
2023-04-02T15:56:19.547+05:00 DEBUG 860131 --- [or-http-epoll-3] i.l.c.r.DefaultEventLoopGroupProvider : Adding reference to io.netty.channel.epoll.EpollEventLoopGroup@5231db1e, existing ref count 0
2023-04-02T15:56:19.620+05:00 DEBUG 860131 --- [or-http-epoll-3] io.lettuce.core.RedisClient : Resolved SocketAddress localhost/<unresolved>:50989 using redis://localhost:50989?timeout=1s
2023-04-02T15:56:19.620+05:00 DEBUG 860131 --- [or-http-epoll-3] io.lettuce.core.AbstractRedisClient : Connecting to Redis at localhost/<unresolved>:50989
2023-04-02T15:56:19.656+05:00 DEBUG 860131 --- [llEventLoop-5-1] i.lettuce.core.protocol.CommandHandler : [channel=0x1c09eca5, [id: 0x63790b3a] (inactive), epid=0x1, chid=0x1] channelRegistered()
2023-04-02T15:56:19.673+05:00 DEBUG 860131 --- [llEventLoop-5-1] io.lettuce.core.AbstractRedisClient : Connecting to Redis at localhost/<unresolved>:50989: localhost/<unresolved>:50989
io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: localhost/127.0.0.1:50989
Caused by: java.net.ConnectException: finishConnect(..) failed: Connection refused
at io.netty.channel.unix.Errors.newConnectException0(Errors.java:164) ~[netty-transport-native-unix-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.unix.Errors.handleConnectErrno(Errors.java:129) ~[netty-transport-native-unix-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.unix.Socket.finishConnect(Socket.java:359) ~[netty-transport-native-unix-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.doFinishConnect(AbstractEpollChannel.java:710) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.finishConnect(AbstractEpollChannel.java:687) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.epollOutReady(AbstractEpollChannel.java:567) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:489) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
2023-04-02T15:56:19.678+05:00 DEBUG 860131 --- [llEventLoop-5-1] io.lettuce.core.RedisChannelHandler : closeAsync()
2023-04-02T15:56:19.678+05:00 DEBUG 860131 --- [llEventLoop-5-1] i.lettuce.core.protocol.DefaultEndpoint : [unknown, epid=0x1] closeAsync()
2023-04-02T15:56:19.680+05:00 DEBUG 860131 --- [llEventLoop-5-1] i.lettuce.core.protocol.CommandHandler : [channel=0x1c09eca5, [id: 0x63790b3a] (inactive), epid=0x1, chid=0x1] channelUnregistered()
2023-04-02T15:56:19.685+05:00 DEBUG 860131 --- [or-http-epoll-3] o.s.c.g.f.ratelimit.RedisRateLimiter : Error calling rate limiter lua
org.springframework.data.redis.RedisConnectionFailureException: Unable to connect to Redis
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.translateException(LettuceConnectionFactory.java:1602) ~[spring-data-redis-3.0.4.jar:3.0.4]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1533) ~[spring-data-redis-3.0.4.jar:3.0.4]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getNativeConnection(LettuceConnectionFactory.java:1358) ~[spring-data-redis-3.0.4.jar:3.0.4]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$SharedConnection.getConnection(LettuceConnectionFactory.java:1341) ~[spring-data-redis-3.0.4.jar:3.0.4]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getSharedReactiveConnection(LettuceConnectionFactory.java:1083) ~[spring-data-redis-3.0.4.jar:3.0.4]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getReactiveConnection(LettuceConnectionFactory.java:479) ~[spring-data-redis-3.0.4.jar:3.0.4]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory.getReactiveConnection(LettuceConnectionFactory.java:105) ~[spring-data-redis-3.0.4.jar:3.0.4]
at reactor.core.publisher.MonoSupplier.call(MonoSupplier.java:67) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxUsingWhen.subscribe(FluxUsingWhen.java:81) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.Mono.subscribe(Mono.java:4485) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxFlatMap.trySubscribeScalarMap(FluxFlatMap.java:200) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoFlatMap.subscribeOrReturn(MonoFlatMap.java:53) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:57) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.4.jar:3.5.4]
at io.github.resilience4j.reactor.circuitbreaker.operator.MonoCircuitBreaker.subscribe(MonoCircuitBreaker.java:38) ~[resilience4j-reactor-2.0.2.jar:2.0.2]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxRetryWhen.subscribe(FluxRetryWhen.java:77) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoRetryWhen.subscribeOrReturn(MonoRetryWhen.java:46) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoFromFluxOperator.subscribe(MonoFromFluxOperator.java:74) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.Mono.subscribe(Mono.java:4485) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoIgnoreThen$ThenIgnoreMain.subscribeNext(MonoIgnoreThen.java:263) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoIgnoreThen.subscribe(MonoIgnoreThen.java:51) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.InternalMonoOperator.subscribe(InternalMonoOperator.java:64) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoDefer.subscribe(MonoDefer.java:52) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoFlatMap$FlatMapMain.onNext(MonoFlatMap.java:165) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxSwitchIfEmpty$SwitchIfEmptySubscriber.onNext(FluxSwitchIfEmpty.java:74) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxMap$MapSubscriber.onNext(FluxMap.java:122) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2071) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxDefaultIfEmpty$DefaultIfEmptySubscriber.onComplete(FluxDefaultIfEmpty.java:134) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxContextWrite$ContextWriteSubscriber.onComplete(FluxContextWrite.java:126) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxMapFuseable$MapFuseableConditionalSubscriber.onComplete(FluxMapFuseable.java:350) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxFilterFuseable$FilterFuseableConditionalSubscriber.onComplete(FluxFilterFuseable.java:391) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.Operators$BaseFluxToMonoOperator.completePossiblyEmpty(Operators.java:2072) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.MonoCollect$CollectSubscriber.onComplete(MonoCollect.java:145) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxPeek$PeekSubscriber.onComplete(FluxPeek.java:260) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.core.publisher.FluxMap$MapSubscriber.onComplete(FluxMap.java:144) ~[reactor-core-3.5.4.jar:3.5.4]
at reactor.netty.channel.FluxReceive.onInboundComplete(FluxReceive.java:415) ~[reactor-netty-core-1.1.5.jar:1.1.5]
at reactor.netty.channel.ChannelOperations.onInboundComplete(ChannelOperations.java:431) ~[reactor-netty-core-1.1.5.jar:1.1.5]
at reactor.netty.http.server.HttpServerOperations.onInboundNext(HttpServerOperations.java:663) ~[reactor-netty-http-1.1.5.jar:1.1.5]
at reactor.netty.channel.ChannelOperationsHandler.channelRead(ChannelOperationsHandler.java:113) ~[reactor-netty-core-1.1.5.jar:1.1.5]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:444) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at reactor.netty.http.server.HttpTrafficHandler.channelRead(HttpTrafficHandler.java:274) ~[reactor-netty-http-1.1.5.jar:1.1.5]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.CombinedChannelDuplexHandler$DelegatingChannelHandlerContext.fireChannelRead(CombinedChannelDuplexHandler.java:436) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.handler.codec.ByteToMessageDecoder.fireChannelRead(ByteToMessageDecoder.java:346) ~[netty-codec-4.1.90.Final.jar:4.1.90.Final]
at io.netty.handler.codec.ByteToMessageDecoder.channelRead(ByteToMessageDecoder.java:318) ~[netty-codec-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.CombinedChannelDuplexHandler.channelRead(CombinedChannelDuplexHandler.java:251) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:442) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.fireChannelRead(AbstractChannelHandlerContext.java:412) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.DefaultChannelPipeline$HeadContext.channelRead(DefaultChannelPipeline.java:1410) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:440) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.AbstractChannelHandlerContext.invokeChannelRead(AbstractChannelHandlerContext.java:420) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.DefaultChannelPipeline.fireChannelRead(DefaultChannelPipeline.java:919) ~[netty-transport-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.AbstractEpollStreamChannel$EpollStreamUnsafe.epollInReady(AbstractEpollStreamChannel.java:800) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:499) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
Caused by: io.lettuce.core.RedisConnectionException: Unable to connect to localhost/<unresolved>:50989
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:78) ~[lettuce-core-6.2.3.RELEASE.jar:6.2.3.RELEASE]
at io.lettuce.core.RedisConnectionException.create(RedisConnectionException.java:56) ~[lettuce-core-6.2.3.RELEASE.jar:6.2.3.RELEASE]
at io.lettuce.core.AbstractRedisClient.getConnection(AbstractRedisClient.java:350) ~[lettuce-core-6.2.3.RELEASE.jar:6.2.3.RELEASE]
at io.lettuce.core.RedisClient.connect(RedisClient.java:216) ~[lettuce-core-6.2.3.RELEASE.jar:6.2.3.RELEASE]
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.lambda$getConnection$1(StandaloneConnectionProvider.java:111) ~[spring-data-redis-3.0.4.jar:3.0.4]
at java.base/java.util.Optional.orElseGet(Optional.java:364) ~[na:na]
at org.springframework.data.redis.connection.lettuce.StandaloneConnectionProvider.getConnection(StandaloneConnectionProvider.java:111) ~[spring-data-redis-3.0.4.jar:3.0.4]
at org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory$ExceptionTranslatingConnectionProvider.getConnection(LettuceConnectionFactory.java:1531) ~[spring-data-redis-3.0.4.jar:3.0.4]
... 69 common frames omitted
Caused by: io.netty.channel.AbstractChannel$AnnotatedConnectException: finishConnect(..) failed: Connection refused: localhost/127.0.0.1:50989
Caused by: java.net.ConnectException: finishConnect(..) failed: Connection refused
at io.netty.channel.unix.Errors.newConnectException0(Errors.java:164) ~[netty-transport-native-unix-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.unix.Errors.handleConnectErrno(Errors.java:129) ~[netty-transport-native-unix-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.unix.Socket.finishConnect(Socket.java:359) ~[netty-transport-native-unix-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.doFinishConnect(AbstractEpollChannel.java:710) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.finishConnect(AbstractEpollChannel.java:687) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.AbstractEpollChannel$AbstractEpollUnsafe.epollOutReady(AbstractEpollChannel.java:567) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.EpollEventLoop.processReady(EpollEventLoop.java:489) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.channel.epoll.EpollEventLoop.run(EpollEventLoop.java:397) ~[netty-transport-classes-epoll-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.concurrent.SingleThreadEventExecutor$4.run(SingleThreadEventExecutor.java:997) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.internal.ThreadExecutorMap$2.run(ThreadExecutorMap.java:74) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30) ~[netty-common-4.1.90.Final.jar:4.1.90.Final]
at java.base/java.lang.Thread.run(Thread.java:833) ~[na:na]
通过使用
docker compose -f {file-name} up -d
执行以下 docker-compose 脚本启动 Redis 服务器
version: "3.8"
services:
polar-redis:
image: "redis:latest"
container_name: "polar-redis"
ports:
- "127.0.0.1:50988:6379"
在application.yml中添加如下props连接redis服务器(我特意把redis的默认端口改成50988)
spring:
data:
redis:
connect-timeout: 2s
host: localhost
port: 50988
timeout: 1s
现在,重新启动您的应用程序。 SCG 应该能够连接到 redis 服务器。
谢谢