网关之SpringCloudGateway高级应用

一、熔断降级

1.1 为什么要实现熔断降级?

在分布式系统中,网关作为流量的入口,因此会有大量的请求进入网关,向其他服务发起调用,其他服务不可避免的会出现调用失败(超时、异常),失败时不能让请求堆积在网关上,需要快速失败并返回给客户端,想要实现这个要求,就必须在网关上做熔断、降级操作。

1.2 基于 hystrix 熔断降级

  1. 添加依赖
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. 配置
server:
  # 配置应用端口
  port: 8080
  
spring:
  application:
    # 配置应用名称
    name: gateway
  cloud:
    nacos:
      discovery:
        # 注册服务中心地址
        server-addr: 192.168.205.10:8848
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            # 配置 Hystrix
            - name: Hystrix
              args:
                name: fallbackCmdA
                # 降级调用 URI
                fallbackUri: forward:/fallbackA
# 设置超时时间,单位:ms
hystrix.command.fallbackCmdA.execution.isolation.thread.timeoutInMilliseconds: 5000
  1. 创建降级回调方法
@RestController
public class FallbackController {

    @GetMapping("/fallbackA")
    public String fallbackA() {
        return "服务暂时不可用";
    }
}

  1. 启动 OrderServicegateway 服务,并访问 http://localhost:8080/order/create 返回:
订单创建成功
  1. 关闭OrderService 访问 http://localhost:8080/order/create 返回:
服务暂时不可用

证明熔断降级已生效。

二、限流

2.1 为什么需要限流?

  • 防止大量的请求使服务器过载,导致服务不可用
  • 防止网络攻击

2.2 常见的限流算法

计数器算法

在指定时间内对请求数做累计,当数量大于设置的值时,后续的请求都将被拒绝。当指定时间过去后,将计数重置为0,重新开始计数。
    
弊端:如果在单位时间1s内只能允许100个请求访问,在前10ms已经通过了100个请求,那后面的990ms所有的请求都会被拒绝,这种现象称为“突刺现象”。    

漏桶算法

漏桶算法可以消除"突刺现象",漏桶算法内部有一个容器,类似生活用到的漏斗,当请求进来时,相当于水倒入漏斗,然后从下端小口慢慢匀速的流出。不管上面流量多大,下面流出的速度始终保持不变。不管服务调用方多么不稳定,通过漏桶算法进行限流,每10毫秒处理一次请求。因为处理的速度是固定的,请求进来的速度是未知的,可能突然进来很多请求,没来得及处理的请求就先放在桶里,既然是个桶,肯定是有容量上限,如果桶满了,那么新进来的请求就丢弃
    
弊端:无法应对短时间的突发流量。    

令牌桶算法

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

2.3 Gateway 限流支持

Spring Cloud Gateway 中,有 Filter 过滤器,因此可以在 pre 类型的 Filter 中自行实现上述三种过滤器。但是限流作为网关最基本的功能,Spring Cloud Gateway 官方就提供了 RequestRateLimiterGatewayFilterFactory 这个类,适用在 Redis 内的通过执行 Lua 脚本实现了令牌桶的方式。具体实现逻辑在 RequestRateLimiterGatewayFilterFactory 类中,lua 脚本在如下图所示的文件夹中:

2.4 实例

  1. 添加 redis 依赖
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>
  1. 在配置文件中配置
server:
  # 配置应用端口
  port: 8080


spring:
  application:
    # 配置应用名称
    name: gateway
  cloud:
    nacos:
      discovery:
        # 注册服务中心地址
        server-addr: 192.168.205.10:8848
    gateway:
      routes:
        - id: order-service
          uri: lb://order-service
          predicates:
            - Path=/order/**
          filters:
            - name: RequestRateLimiter
              args:
                # 用于限流的键的解析器的 Bean 对象的名字,通过 SpEL 表达式从 Spring 容器中获取 
                key-resolver: '#{@hostAddrKeyResolver}'
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 3
  redis:
    host: localhost
    port: 6379
    database: 0
  1. 自定义限流策略,通过实现 KeyResolver 接口

基于 hostAddress 限流:

public class HostAddrKeyResolver implements KeyResolver {

    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
    
}

基于 URI 限流:

public class UriKeyResolver implements KeyResolver {

    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getURI().getPath());
    }

}

基于用户 限流:

public class UserKeyResolver implements KeyResolver {

    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
    }

}

只允许一个策略生效,这里我们采用 HostAddrKeyResolver :

@Bean
public HostAddrKeyResolver hostAddrKeyResolver() {
	return new HostAddrKeyResolver();
}
  1. 启动 OrderServicegateway 服务,通过 jmeter 并发访问

可以看到请求部分成功,部分失败。

原文地址:https://www.cnblogs.com/markLogZhu/p/13627261.html