spring cloud gateway网关限流

本文涉及的产品
云数据库 Tair(兼容Redis),内存型 2GB
Redis 开源版,标准版 2GB
推荐场景:
搭建游戏排行榜
性能测试 PTS,5000VUM额度
简介: 一般开发高并发系统常见的限流有: 1)限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如 nginx 的 limit_conn 模块,用来限制瞬时并发连接数)、 2)限制时间窗口内的平均速率(如 Guava 的 RateLimiter、nginx 的 limit_req 模块,限制每秒的平均速率); 3)其他还有如限制远程接口调用速率、限制 MQ 的消费速率。 4)另外还可以根据网络连接数、网络流量、CPU 或内存负载等来限流。 本文讨论在gateway集成的实现
  1. 前提

    1. redis可用
    2. JMeter
  2. spring cloud gateway网关限流

    1. 限流简介

      限流就是限制流量,因为服务器能处理的请求数有限,如果请求量特别大,我们需要做限流(要么就让请求等待,要么就把请求给扔了),
      限流可以保障我们的 API 服务对所有用户的可用性,也可以防止网络攻击。在高并发的应用中,限流是一个绕不开的话题。
      
      注1:为什么要限流?见:images/00
      
    2. 常见的限流手段

      一般开发高并发系统常见的限流有:
      1)限制总并发数(比如数据库连接池、线程池)、限制瞬时并发数(如 nginx 的 limit_conn 模块,用来限制瞬时并发连接数)、
      2)限制时间窗口内的平均速率(如 Guava 的 RateLimiter、nginx 的 limit_req 模块,限制每秒的平均速率);
      3)其他还有如限制远程接口调用速率、限制 MQ 的消费速率。
      4)另外还可以根据网络连接数、网络流量、CPU 或内存负载等来限流。
      本文讨论在gateway集成的实现
      
    3. 限流算法

      1.漏桶算法(Leaky Bucket)
         略...
      
      2.令牌桶算法(Token Bucket)
         随着时间流逝,系统会按恒定 1/QPS 时间间隔(如果 QPS=100,则间隔是 10ms)往桶里加入 Token(想象和漏洞漏水相反,有个水龙头在不断的加水),
         如果桶已经满了就不再加了。新请求来临时,会各自拿走一个 Token,如果没有 Token 可拿了就阻塞或者拒绝服务。
      
         令牌桶的另外一个好处是可以方便的改变速度:一旦需要提高速率,则按需提高放入桶中的令牌的速率
         注1:令牌桶算法流程图见:images/01 
      
        1秒生成20令牌
      
        令牌桶容量100
      
    4. gateway网关限流快速实现

      Spring Cloud Gateway官方提供了RequestRateLimiterGatewayFilterFactory类,使用redis和lua脚本来实现令牌桶的方式。
      我们也可以基于Google Guava中的RateLimiter、Bucket4j、RateLimitJ来实现。但是,本文将采用官方提供的方式来实现。
      
      Gateway通过内置的RequestRateLimiter过滤器实现限流,使用令牌桶算法,借助Redis保存中间数据。用户可通过自定义KeyResolver设置限流维度,例如:
      1.对请求的目标URL进行限流
      2.对来源IP进行限流
      3.特定用户进行限流
      本案例实现对IP进行限流
      
      1. 导入redis依赖
          <!--redis依赖-->
          <dependency>
              <groupId>org.springframework.boot</groupId>
              <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
          </dependency>
      
      2. 修改application.yml文件添加redis相关配置
          #此处为单机版配置,实际开发中,应该是一个redis集群。redis集群配置见:资料/07 springboot2.x 整合redis集群
          spring:
             redis:
                 host: 192.168.217.132
                 port: 6379
                 password: 123456
                 database: 0
      
          注1:如果redis连接失败,限流功能将不能开启。因为没有redis作为容器来保存令牌,限流功能自然就失效了。切记~~~切记~~~切记~~~
          注2:可以将redis的配置信息保存到nacos中,通过添加nacos配置中心客户端的方式进行读取
      
      3. 创建限流Bean   
          详情见:src/RequestRateLimiterConfig.java
      
          注1:限流bean的名字,必须和步骤4引用时的名字一样
      
      4. 在路由配置中添加限流配置
          #filter名称必须是 RequestRateLimiter
          - name: RequestRateLimiter
            args:
              #用于限流的键的解析器的 Bean 对象的名字,使用 SpEL表达式根据#{@beanName}获取Bean对象
              key-resolver: '#{@ipAddrKeyResolver}'
              #令牌桶填充速率,允许用户每秒处理多少个请求
              redis-rate-limiter.replenishRate: 10
              #令牌桶总容量,允许在一秒钟内完成的最大请求数
              redis-rate-limiter.burstCapacity: 20
      
      5. 使用JMeter进行限流测试
          测试结果,没有抢到令牌的请求就返回429,这边的限流相当于平均request:10/s
      
      6. 在前端页面,比如:vue处理429错误,显示“服务忙请稍后再试”
          Response code:429
          Response message:Too Many Requests
      
  3. 熔断
    网关是所有请求的入口,如果部分后端服务延时严重,则可能导致大量请求堆积在网关上,拖垮网关进而瘫痪整个系统。
    这就需要对响应慢的服务做超时快速失败处理,即熔断

    在组件的选型上有两种:Hystrix与Sentinel,本章介绍的是Spring Cloud Gateway基于Hystrix实现的熔断

    1. 修改gateway-server子模块的pom,增加pom相关依赖

      <!--Gateway基于Hystrix实现的熔断-->
      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
      </dependency>
      
    2. 修改application.yml配置,在路由中添加熔断配置

      Hystrix支持两个参数:
      name:即HystrixCommand的名字
      fallbackUri:fallback对应的uri,这里的uri仅支持forward:schemed
      
        filters:
          - name: Hystrix
            args:
              name: fallback
              fallbackUri: forward:/fallback
      
      1. 创建FallBackControlle

        @RestController
        public class FallBackController {
        
            @GetMapping("/fallback")
            public String fallback() {
                return "Error:fallback";
            }
        } 
        
      2. 测试

        启动各个微服务,并访问,成功后再关闭生产者
        
        至此:熔断的简单配置实现就完成了,如需自定义熔断策略需要学习了解HystrixGatewayFilter的相关内容
        
  4. Spring Cloud 服务第一次请求超时的优化
    Spring Cloud项目启动后,首次使用 FeignClient 请求往往会消耗大量时间,并有一定概率因此导致请求超时(java.net.SocketTimeoutException: Read timed out),因而有可能会触发熔断
    这是由于在调用其他微服务接口前,会去请求该微服务的相关信息(地址、端口等),并做一些初始化操作,由于默认的懒加载特性,导致了在第一次调用时,出现超时的情况,

    解决方法主要有两种:

    1. 第一种办法是设置超时时间,具体设置成多少,因项目而异,配置如下

      #hystrix调用方法的超时时间,默认是1000毫秒
      hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000
      
      理论上这是一个治标的办法,这样处理能够解决超时的问题,但无法解决首次花费时间长的问题。同时因为需要将熔断器的超时时间设置得更长,
      等价于在一定程度上限制了熔断器的适用范围。所以可用这个方法,但不推荐这个方法
      
    2. 推荐的方式:配置ribbon立即加载,

      链路分析,服务之间的调用顺序为:gateway->消费者->生产者,   
      接下来分两部分解决这个问题,一是服务之间调用Ribbon的饥饿加载,对应上面的测试为消费者调用生产者;二是网关的饥饿加载。
       
      第一步:
      找到消费者的application.yml文件,添加如下配置:
      #此处需要注意的是,光配置立即加载是不生效的,还要配置客户端列表
      ribbon:
        eager-load:
          enabled: true                                                                  #开始饥饿模式
          clients: user-service, material-product, outer-data        #生产者的服务名,多个之间逗号分隔 
      
      重启消费者后,你会发现虽然没有发送调用请求,但日志中已经显示Feign的客户端创建成功了        
      
      第二步:网关的饥饿模式
      ribbon:
        eager-load:
          enabled: true
          clients: user-service-api
      
  5. 个人体会
    外部请求用限流、内部请求用熔断

附录一:JDK8的新特性——Lambda表达式
在JDK8之前,Java是不支持函数式编程的,所谓的函数编程,即可理解是将一个函数(也称为“行为”)作为一个参数进行传递。
通常我们提及得更多的是面向对象编程,面向对象编程是对数据的抽象(各种各样的POJO类),而函数式编程则是对行为的抽象(将行为作为一个参数进行传递)。
在JavaScript中这是很常见的一个语法特性,但在Java中将一个函数作为参数传递这却行不通,好在JDK8的出现打破了Java的这一限制。简单示例如下:

 new Thread(new Runnable() {
     @Override
     public void run() {
         System.out.println("Hello World!");
     }
 });
 使用Lambda表达式则只需要使用一句话就可代替上面使用匿名类的方式。
 new Thread(() -> System.out.println("Hello World!"));

附录二:如何使用JMeter进行并发测试

  1. 安装
    将下载得到的压缩包解压即可,这里我解压到自己电脑的路径为D:\tools\apache-jmeter-5.2.1
  2. 运行
    点击bin目录下的jmeter.bat即可启动Jmeter。
  3. 一个简单的压测实例
    现有一个http请求接口localhost:5000/usr3/hello,要使用Jmeter对其进行压测,测试步骤如下:
    1.新建一个线程组
    2.设置线程组参数
    1.Number of Threads 10 线程数量 10 个线程
    2.Ramp-Up Period 0 所有线程在多少秒内启动,设置为0表示同时启动
    3.Loop Count 1 线程重复资料
    这里配置为:10个线程,同时启动,循环一次
    3.新增http请求默认值
    在上一步创建的线程组上,新增http请求默认值,所有的请求都会使用设置的默认值,这设置协议为http,IP为localhost,端口为8080
    4.添加要压测的http请求

    协议、IP、端口不需要设置,会使用步骤c中设置的默认值,只需设置请求路径Path即可,这里填入/usr3/hello

    5.新增监听器,用于查看压测结果。这里添加三种:聚合报告、图形结果、用表格查看结果,区别在于结果展现形式不同
    6.点击运行按钮开始压测,并查看结果

附录三:@Primary注解
此注解用在类上面。它表示在需要自动注入一个单值依赖的地方,却有多个候选依赖,那么这个注解会指定一个类作为preference(偏好)选择。
可以简单理解为,我们把@Primary注解标记在任意一个类上面,在使用@Autowired注入的时候,如果不特殊指明(如何特殊指明请看@Qualifier的讲解),
那么默认就注入被@Primary标记的类。但是只可以指定一个类作为偏好类,否则依然会产生冲突。

附录四:SpringCloud服务消费者第一次调用出现超时问题的解决方案
在第一次访问服务消费者的时候(消费者去调用服务提供者服务)会出现如下异常:
com.netflix.hystrix.exception.HystrixRuntimeException: TestService#hello(String) timed-out and no fallback available
解决方案是在application.properties增加如下配置信息:

hystrix调用方法的超时时间,默认是1000毫秒

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=5000

更绝的一个方案是直接禁用hystrix:
feign.hystrix.enabled=false

相关实践学习
基于Redis实现在线游戏积分排行榜
本场景将介绍如何基于Redis数据库实现在线游戏中的游戏玩家积分排行榜功能。
云数据库 Redis 版使用教程
云数据库Redis版是兼容Redis协议标准的、提供持久化的内存数据库服务,基于高可靠双机热备架构及可无缝扩展的集群架构,满足高读写性能场景及容量需弹性变配的业务需求。 产品详情:https://www.aliyun.com/product/kvstore &nbsp; &nbsp; ------------------------------------------------------------------------- 阿里云数据库体验:数据库上云实战 开发者云会免费提供一台带自建MySQL的源数据库&nbsp;ECS 实例和一台目标数据库&nbsp;RDS实例。跟着指引,您可以一步步实现将ECS自建数据库迁移到目标数据库RDS。 点击下方链接,领取免费ECS&amp;RDS资源,30分钟完成数据库上云实战!https://developer.aliyun.com/adc/scenario/51eefbd1894e42f6bb9acacadd3f9121?spm=a2c6h.13788135.J_3257954370.9.4ba85f24utseFl
相关文章
|
11天前
|
负载均衡 Java 应用服务中间件
Gateway服务网关
Gateway服务网关
25 1
Gateway服务网关
|
2月前
|
Java 开发者 Spring
Spring Cloud Gateway 中,过滤器的分类有哪些?
Spring Cloud Gateway 中,过滤器的分类有哪些?
48 3
|
2月前
|
负载均衡 Java 网络架构
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
实现微服务网关:Zuul与Spring Cloud Gateway的比较分析
102 5
|
1月前
|
负载均衡 Java API
【Spring Cloud生态】Spring Cloud Gateway基本配置
【Spring Cloud生态】Spring Cloud Gateway基本配置
37 0
|
2月前
|
安全 Java 开发者
强大!Spring Cloud Gateway新特性及高级开发技巧
在微服务架构日益盛行的今天,网关作为微服务架构中的关键组件,承担着路由、安全、监控、限流等多重职责。Spring Cloud Gateway作为新一代的微服务网关,凭借其基于Spring Framework 5、Project Reactor和Spring Boot 2.0的强大技术栈,正逐步成为业界的主流选择。本文将深入探讨Spring Cloud Gateway的新特性及高级开发技巧,助力开发者更好地掌握这一强大的网关工具。
219 6
|
3月前
|
算法 Java UED
你的Spring Boot应用是否足够健壮?揭秘限流功能的实现秘诀
【8月更文挑战第29天】限流是保障服务稳定性的关键策略,通过限制单位时间内的请求数量防止服务过载。本文基于理论介绍,结合Spring Boot应用实例,展示了使用`@RateLimiter`注解和集成`Resilience4j`库实现限流的方法。无论采用哪种方式,都能有效控制请求速率,增强应用的健壮性和用户体验。通过这些示例,读者可以灵活选择适合自身需求的限流方案。
100 2
|
3月前
|
Java 应用服务中间件 nginx
【Azure Spring Apps】Spring App部署上云遇见 502 Bad Gateway nginx
【Azure Spring Apps】Spring App部署上云遇见 502 Bad Gateway nginx
|
Dubbo Java 应用服务中间件
深入了解Spring Cloud Alibaba Dubbo
在现代分布式系统开发中,构建高性能、可伸缩性和弹性的微服务架构变得越来越重要。Spring Cloud Alibaba Dubbo(简称Dubbo)是一个开源的分布式服务框架,可以帮助开发者构建强大的微服务架构,具备负载均衡、服务治理、远程调用等强大功能。本文将深入介绍Spring Cloud Alibaba Dubbo,帮助你理解它的核心概念、工作原理以及如何在你的项目中使用它。
|
Kubernetes Java 微服务
Spring Boot 单体应用一键升级成 Spring Cloud Alibaba(1)
Spring Boot 单体应用一键升级成 Spring Cloud Alibaba(1)
135 0
Spring Boot 单体应用一键升级成 Spring Cloud Alibaba(1)
|
6月前
|
Java 中间件 开发者
Spring Cloud Alibaba
【1月更文挑战第27天】【1月更文挑战第127篇】Spring Cloud Alibaba
140 1