SpringCloud之Gateway网关熔断、限流

1、重试

SpringCloud之Gateway网关熔断、限流_第1张图片

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      routes:
        - id: gate_hi
          uri: lb://gateway-hi
          predicates:
            - Path=/retry/*
          filters:
            - StripPrefix=1
            - name: Retry
              args:
                retries: 3
                series: SERVER_ERROR
                methods: GET,POST

retries:重试次数
series: SERVER_ERROR,下游服务报5XX系列的错误触发重试机制
methods:重试的HTTP方法

hi服务hello接口

    @GetMapping("hello")
    public String hello(@RequestParam String name) {
        int i = 1 / 0;
        return "hi," + name + " ! " + "访问端口号:" + port;
    }

启动eureka、hi、limit服务
SpringCloud之Gateway网关熔断、限流_第2张图片

2、熔断Hystrix

添加 hystrix 依赖

        <!-- hystrix -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
        </dependency>

熔断配置

server:
  port: 8026

eureka:
  client:
    serviceUrl:
      defaultZone: http://localhost:8088/eureka/

spring:
  application:
    name: gateway-limit
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      routes:
        - id: gateway_hi
          uri: lb://gateway-hi
          predicates:
            - Path=/hi/*
          filters:
            - StripPrefix=1
            #熔断
            - name: Hystrix
              args:
                name: localfallback
                fallbackUri: forward:/fallback

回调方法在网关服务

@RestController
public class IndexController {

    @RequestMapping("/fallback")
    public String fallback() {
        System.out.println("fallback");
        return "local fallback";
    }
}

启动服务 eureka、hi、gateway-limit
正常访问:
http://localhost:8026/hi/hello?name=yzm
SpringCloud之Gateway网关熔断、限流_第3张图片

关闭hi服务,再次访问
SpringCloud之Gateway网关熔断、限流_第4张图片
服务hi不可达,网关回调fallback()方法

修改:
将回调方法部署到ha服务上

    @RequestMapping("/fallback")
    public String fallback() {
        System.out.println("fallback");
        return "ha fallback";
    }

注释gateway服务上的fallback()方法

@RestController
public class IndexController {

//    @RequestMapping("/fallback")
    public String fallback() {
        System.out.println("fallback");
        return "local fallback";
    }

}
spring:
  application:
    name: gateway-limit
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      routes:
        - id: gateway_hi
          uri: lb://gateway-hi
          predicates:
            - Path=/hi/*
          filters:
            - StripPrefix=1
            #熔断
            - name: Hystrix
              args:
                name: remotefallback
                fallbackUri: forward:/fallback
        - id: gateway_ha
          uri: lb://gateway-ha
          predicates:
            - Path=/fallback

启动服务 eureka、ha、gateway-limit,不用启动hi
访问 http://localhost:8026/hi/hello?name=yzm
SpringCloud之Gateway网关熔断、限流_第5张图片

3、Redis实现限流

添加依赖 redis-reactive

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>

限流配置

spring:
  application:
    name: gateway-limit
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lowerCaseServiceId: true
      routes:
        - id: gateway_hi
          uri: lb://gateway-hi
          predicates:
            - Path=/hi/limit/*
          filters:
            - StripPrefix=2
            #限流
            - name: RequestRateLimiter
              args:
                key-resolver: '#{@remoteAddrKeyResolver}'
                redis-rate-limiter.replenishRate: 1 #令牌桶每秒填充平均速率
                redis-rate-limiter.burstCapacity: 3 #令牌桶容量
  # redis连接配置
  redis:
    host: 192.168.8.128
    port: 6379
    password: 1234
    database: 0

key-resolver:用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

键解析器

/**
 * 用于限流的键的解析器的 Bean 对象名字。
 * 它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。
 * 默认情况下,使用PrincipalNameKeyResolver,以请求认证的java.security.Principal作为限流键。
 */
@Component
public class RemoteAddrKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        // 根据IP地址限流
        String hostAddress = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
        System.out.println("hostAddress = " + hostAddress);

        // 根据请求地址限流
        String uriPath = exchange.getRequest().getURI().getPath();
        System.out.println("uriPath = " + uriPath);

        // 根据用户ID
        //String userId = exchange.getRequest().getQueryParams().getFirst("userId");
        return Mono.just(hostAddress);
    }
}

正常访问
SpringCloud之Gateway网关熔断、限流_第6张图片
快速刷新
SpringCloud之Gateway网关熔断、限流_第7张图片

4、使用过滤器实现令牌桶限流

引入依赖

        <!-- 令牌桶 -->
        <dependency>
            <groupId>com.github.vladimir-bukhtoyarov</groupId>
            <artifactId>bucket4j-core</artifactId>
            <version>4.0.0</version>
        </dependency>

定义过滤器

/**
 * 根据IP Address限流
 */
@Data
@Slf4j
@NoArgsConstructor
@AllArgsConstructor
public class RateLimitByIpGatewayFilter implements GatewayFilter, Ordered {

    //桶的最大容量,即能装载 Token 的最大数量
    int capacity;
    //每次 Token 补充量
    int refillTokens;
    //补充 Token 的时间间隔
    Duration refillDuration;

    private static final Map<String, Bucket> CACHE = new ConcurrentHashMap<>();

    private Bucket createNewBucket() {
        Refill refill = Refill.of(refillTokens, refillDuration);
        Bandwidth limit = Bandwidth.classic(capacity, refill);
        return Bucket4j.builder().addLimit(limit).build();
    }

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String ip = exchange.getRequest().getRemoteAddress().getAddress().getHostAddress();
        Bucket bucket = CACHE.computeIfAbsent(ip, b -> createNewBucket());

        log.info("IP: " + ip + ",TokenBucket Available Tokens: " + bucket.getAvailableTokens());
        if (bucket.tryConsume(1)) {
            return chain.filter(exchange);
        } else {
            exchange.getResponse().setStatusCode(HttpStatus.TOO_MANY_REQUESTS);
            return exchange.getResponse().setComplete();
        }
    }

    @Override
    public int getOrder() {
        return -1000;
    }
}

配置路由

package com.yzm.gatewaylimit.config;

import org.springframework.cloud.gateway.route.RouteLocator;
import org.springframework.cloud.gateway.route.builder.RouteLocatorBuilder;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.time.Duration;

@Configuration
public class RouteConfig {

    @Bean
    public RouteLocator customerRouteLocator(RouteLocatorBuilder builder) {
        return builder.routes()
                // 根据令牌限流
                .route(p -> p
                        .path("/ip/**")
                        .filters(f -> f
                                .stripPrefix(1)
                                .filter(new RateLimitByIpGatewayFilter(3,1, Duration.ofSeconds(1))))
                        .uri("http://httpbin.org:80"))
                .build();
    }
}

桶最大容量3个,每秒产出1个token

快速刷新
SpringCloud之Gateway网关熔断、限流_第8张图片

相关链接

首页
上一篇:Gateway过滤器

你可能感兴趣的:(spring,cloud,spring,cloud)