微服务

SpringCloud系列之网关gateway-14.网关层的其

2021-09-18  本文已影响0人  那钱有着落吗

网关限流三步走

这里采用令牌桶计数的方式做限流,总共分三步

准备工作

我们的Best Practice是基于Redis来实现限流,因此要保证本地启动了Redis服务。同时将下列配置加入到Gateway的配置文件中:

spring:
  application:
    name: gateway-service
redis:
    host: localhost
    port: 6379
    database: 0

这里是配置Redis连接信息的,假如你不配置的话,Gateway也会尝试用默认配置项来连接Redis。但如果你在Redis配置信息中提供了错误的IP或者Port的话,调用方法时依然会成功,不过限流功能就失效了,因为底层的Netty服务无法连接到Redis,也就无法提供限流支持。但Gateway为了保证服务可用性,限流功能的异常并不会阻碍正常的方法调用。

Key Resolver

Gateway的限流组件要求定义一个Key Resolver用来对每次路由请求生成一个Key,这个Key就是一个限流分组的标识,每个Key相当于一个令牌桶。假如我们限定了一个服务每秒只能被调用3次,这个限制会对不同的Key单独计数,我们把调用方机器的Host Name作为限流Key,那么来自同一台机器的调用将落到同一个Key下面,也就是说在这个场景下,每台机器都独立计算单位时间调用量。
创建Key Resolver的方式很简单:

@Bean
public KeyResolver remoteAddrKeyResolver() {
    return exchange -> Mono.just(
          exchange.getRequest().getRemoteAddress().getHostName());
}

上面的例子创建了基于Host Name的令牌生成器,我们可以根据自己的业务来选择合适的Key,比如说可以在接口层面做限流(使用接口的Path作为Key),还可以从Request中提取业务字段作为Key(比如用户ID等)。

配置过滤器

spring:
    cloud:
        gateway:
            routes:
                - id:  feignapi
                uri: lb://FEIGN-SERVICE-PROVIDER
predicates:
- Path=/feign-api/**
filters:
- StripPrefix=1
- name: RequestRateLimiter
args:
key-resolver: '#{@remoteAddrKeyResolver}'
redis-rate-limiter.replenishRate: 10
redis-rate-limiter.burstCapacity: 20

在上面的限流配置中,我们主要关注最后3行中的属性:
 key-resolver:这里注入的就是在上一步中我们定义的Key Resolver,它使用SpEL表达式从Spring上下文中获取指定Bean
 replenishRate:令牌桶每秒的平均填充速度
 burstCapacity:令牌桶总量

java中如何配置网关限流


@Configuration
public class RedisLimiterConfiguration {

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

    @Bean
    @Primary
    public RedisRateLimiter redisRateLimiterUser() {
        return new RedisRateLimiter(1,2);
    }


}

@Configuration
public class GatewayConfiguration {

    @Autowired
    private TimerFilter timerFilter;

    @Autowired
    private KeyResolver remoteAddrKeyResolver;

    @Autowired
    private RedisRateLimiter redisRateLimiter;

    @Bean
    @Order
    public RouteLocator customizedRoutes(RouteLocatorBuilder routeLocatorBuilder){
        return routeLocatorBuilder.routes()
                .route(r->r.path("/java/**")
                        .and().method(HttpMethod.GET)
                        .filters(f->f.stripPrefix(1)
                                .requestRateLimiter(c->{
                                    c.setKeyResolver(remoteAddrKeyResolver);
                                    c.setRateLimiter(redisRateLimiter);
                                    c.setStatusCode(HttpStatus.BAD_GATEWAY);
                                })
                        )
                        .uri("lb://feign-client")
                )
                .route(r->r.path("/seckill/**")
                        .and().after(ZonedDateTime.now().plusMinutes(1))
                        .filters(f->f.stripPrefix(1))
                        .uri("lb://feign-client")
                )
                .build();
    }

}

小细节

假如我们不想依赖Redis的话,还有其他选择方案吗?必须有,但是要靠大家自己动动手。如果大家想借助其他存储介质实现限流,可以参考RedisRateLimiter这个类的实现,通过继承AbstractRateLimiter来创建一个自定义的计数器。

上一篇 下一篇

猜你喜欢

热点阅读