百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术资源 > 正文

Spring Cloud Zookeeper微服务集群实例之三-网关引入及熔断与限流

off999 2025-04-11 04:19 69 浏览 0 评论

在之前的文章中我们实现了服务之间的接口调用,那么集群外部的接口调用如何进行?这就必须通过网关了。网关类似其它节点一样,会将其自身注册到集群中,从而能够获取到某个服务的实例清单;然后根据我们提前配置好的规则(如按路径分发等,类似nginx),将外部过来的请求分发到对应的节点上去执行。

总的来说,网关包含有以下三大功能:

  • 路由转发:即将请求分发到合适的服务中去执行;
  • 负载均衡:类似集群内部调用负载均衡,根据配置的算法及服务的实例清单进行负载均衡处理;
  • 权限控制:可以解析登录用户信息,同时根据路径及预先配置好的规则判断用户是否有权限访问;
  • 限流:可以通过Filter过滤掉超出流量的请求,将其直接返回;
  • 熔断:调用出现异常时一段时间内减少调用对应的接口,或者全部拦截调用;

另外,我们还可以在网关层根据token信息解析出用户信息,后续集群内的接口调用都使用解析出来的用户信息进行权限方面的限制,从而使得集群内部服务能够省略权限解析的步骤,专注于核心业务逻辑的实现。

老版本的Spring Cloud中使用的是Zuul,现在我们可以使用官方的Spring Cloud Gateway来充当网关了。

1 创建网关应用

1.1 maven依赖



    4.0.0

    org.example
    gateway
    1.0-SNAPSHOT

    
        11
        11
        2.4.5
        2020.0.2
    

    
        org.springframework.boot
        spring-boot-starter-parent
        ${spring.boot.version}
         
    

    
        
            org.springframework.cloud
            spring-cloud-starter-gateway
        
        
            org.springframework.cloud
            spring-cloud-starter-zookeeper-discovery
        
    
    
        
            
                org.springframework.cloud
                spring-cloud-dependencies
                ${spring-cloud.version}
                pom
                import
            
        
    
    
        
            
                org.springframework.boot
                spring-boot-maven-plugin
            
        
    

1.2 Bootstrap配置

增加bootstrap.yml文件:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 启用后,默认会通过/{serviceName}/**这样的请求,将其分发到serviceName的实例上去处理;

server:
  port: 8000

上面enabled设置成true后,会启用注册将网关注册到注册中心去,同时有请求过来时,会取host后的第一串字符当前服务名,在注册中心查找这个服务对应的实例,然后转发到对应的节点上去(这一步也会有负载均衡的动作);注意分发过去的请求会自动将{serviceName}这一串给干掉。

举个例子,我们前面启动了一个service0的应用,上面有一个/test的接口;按网关的这个配置,如果我们访问地址:
http://localhost:8000/service0/test,那么就会调用到service0服务上的test接口去。

1.3 main方法

@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

1.4 启动与测试

启动main函数,然后我们就可以通过网关来调用service0上的test接口了,访问地址:
http://localhost:8000/service0/test,不出意外可以调用成功。

2 网关处理流程

网关包含有三个关键的属性:

  • Route:即路由,包括id、目标地址、Predicate列表及Filter列表;
  • Predicate:用于判断其所在的Route能够用于哪些请求,如根据请求参数的断定或者根据请求路径进行断定等;
  • Filter:用于在调用实际服务时增加前置或后置处理;

网关的处理过程如下图所示:

网关处理流程

客户端请求网关,网关通过Gateway Handler Mapping查找命中的Route,然后通过Web Handler进行调用,同时会在调用前后执行所配置的拦截器。

2 熔断

我们可以通过Spring Cloud Gateway来进行熔断。熔断通过网关的Filter进行。

一般会结合resilience4j来处理。

我们结合上方的示例来进行配置。

2.1 添加依赖

需要先在gateway中添加
spring-cloud-starter-circuitbreaker-reactor-resilience4j的依赖:


        
            org.springframework.cloud
            spring-cloud-starter-circuitbreaker-reactor-resilience4j
        

2.2 修改gateway配置

在bootstrap中配置:

spring:
  application:
    name: gateway
  cloud:
    gateway:
#      discovery:
#        locator:
#          enabled: true # 启用后,默认会通过/{serviceName}/**这样的请求,将其分发到serviceName的实例上去处理;但启用这种方式后,在routes中配置的就不生效了 ;
      routes:
        - id: circuitbreaker_route # 配置熔断
          uri: lb://service0
          filters:
            - name: CircuitBreaker
              args:
                name: backendA
                statusCodes:
                  - 500   # 必须配置,如果不配置的话不会进行熔断
#                fallbackUri: forward:/test # 失败后执行的请求
            - StripPrefix=1
          predicates:
            - Path=/service0/**

resilience4j.circuitbreaker:
  configs:
    default:
      slidingWindowSize: 10
      minimumNumberOfCalls: 5 # 计算错误率的最小访问次数,超过这个次数后才会计算错误率并判断是否要进行限流、熔断(但实际不会到这个值,比如配置容忍错误率为50%,配置当前值为10,那么如果前5个都失败就会直接熔断
      permittedNumberOfCallsInHalfOpenState: 3  # 半开状态允许的请求数
      automaticTransitionFromOpenToHalfOpenEnabled: true # 是否在没有请求的情况下由全开自动恢复到半开,设置成true时需要额外的线程进行监控
      waitDurationInOpenState: 2s # 由全开到半开等待的时间
      failureRateThreshold: 50  # 熔断器打开的失败阈值,也即超过指定比例时熔断器将被打开
      eventConsumerBufferSize: 10
  instances:
    backendA:
      baseConfig: default

server:
  port: 8000

management:
  endpoints:
    web:
      exposure:
        include: gateway

重启gateway;

2.3 添加测试接口

然后在service0应用的TestController中添加以下方法:

    @GetMapping("exception")
    public void testException() throws Exception {
        throw new Exception("测试异常");
    }

在这里面我们直接模拟服务调用异常,因此直接抛出了一个异常;重启Service0;

2.4 测试

通过网关来调用service0中的/test/exception接口,访问地址:
http://localhost:8000/service0/test/exception,连续三次访问的时候都会访问到service0上,但第四次返回的异常将会是[904f51ba-10] There was an unexpected error (type=Service Unavailable, status=503)了,service0未收到请求,在网关侧就已经进行了拦截。

然后等待几秒再访问,又会调用到service0上;访问几次时又会被熔断。

3 限流

限流通过RequestRateLimiter类型的拦截器进行。可以使用Redis实现;使用Redis时需要本地启动的Redis,或者在bootstrap.yml中配置Redis地址;

3.1 Maven依赖


        
            org.springframework.boot
            spring-boot-starter-data-redis-reactive
        

3.2 配置限流参数

spring:
  application:
    name: gateway
  cloud:
    gateway:
#      discovery:
#        locator:
#          enabled: true # 启用后,默认会通过/{serviceName}/**这样的请求,将其分发到serviceName的实例上去处理;但启用这种方式后,在routes中配置的就不生效了 ;
      routes:
        - id: circuitbreaker_route # 配置熔断限流
          uri: lb://service0
          predicates:
            - Path=/service0/**
          filters:
            - name: CircuitBreaker  # 熔断
              args:
                name: backendA
                statusCodes:
                  - 500   # 必须配置,如果不配置的话上述的配置不生效
#                fallbackUri: forward:/test # 失败后执行的请求
            - StripPrefix=1
            - name: RequestRateLimiter # 限流
              args:
                redis-rate-limiter.replenishRate: 1 # 令牌生成速度,每s生成多少个
                redis-rate-limiter.burstCapacity: 2 # 令牌桶容量
                redis-rate-limiter.requestedTokens: 1 # 每个请求消耗的令牌数

3.3 配置KeyResolver

需要配置KeyResolver以便进行限流,如果未配置的话,访问接口将会返回403异常。

配置方式如下:

    /**
     * 配置限流KeyResolver
     */
    @Bean
    KeyResolver userKeyResolver() {
        return exchange -> Mono.just("normal");
    }

我这里是针对所有的请求使用一个限流策略;如果需要针对不同请求限制不同策略,需要修改这个KeyResolver,如根据查询参数中的值做分组限流:

@Bean
KeyResolver userKeyResolver() {
    return exchange -> Mono.just(exchange.getRequest().getQueryParams().getFirst("user"));
}

注意如果未配置KeyResolver,限流将不会生效。

3.4 启动与测试

重启gateway,然后通过gateway调用service0的接口test:
http://localhost:8000/service0/test,根据我们的限流配置,一秒内第一次请求会成功,第二次有可能成功(如果桶中已经生成了2个令牌就会成功),第三次之后就会失败。后一秒又可以继续请求。

失败时会报:429 Too Many Request异常,成功实现限流。

4 转发规则配置

我们还可以通过routes配置一些转发规则,如:

spring:
  cloud:
    gateway:
      routes:
      - id: before_route
        uri: https://example.org
        predicates:
        - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

意思是在指定时间前的请求转发到对应的uri上;

Spring Cloud Gateway支持很多种转发规则,其中节选比较有用的列表如下:

  • 根据权重转发即根据配置的权重决定往每个节点分发的请求比例。如:
spring:
  cloud:
    gateway:
      routes:
      - id: weight_high
        uri: https://weighthigh.org
        predicates:
        - Weight=group1, 8
      - id: weight_low
        uri: https://weightlow.org
        predicates:
        - Weight=group1, 2

即往weighthigh上分发80%的流量 ,剩下的往wieghtlow上分发。

比如我们想要做灰度测试,这种配置方式就非常有用。

另外,Spring Cloud Gateway还包含以下转发规则:

  • 指定时间之后;
  • 指定时间之前;
  • 指定时间区间;
  • 根据Cookie值转发;
  • 根据Header值转发;
  • 根据Host值转发;
  • 根据Method(GET、POST)进行转发;
  • 根据Path进行转发;
  • 根据查询条件进行转发;
  • 根据远程地址进行转发;

具体可以参考官方文档,在此不再展开。

5 拦截器配置

5.1 拦截器配置

上面我们在限流与熔断中我们已经配置过拦截器了,再来看一个示例:

spring:
  cloud:
    gateway:
      routes:
      - id: add_request_header_route
        uri: https://example.org
        filters:
        - AddRequestHeader=X-Request-red, blue

上面通过AddRequestHeader拦截器,在转发的请求报文头中添加了名称为X-Request-red的头,其值为blue;

5.2 通用拦截器配置

我们也可以通过default-filters来做通用的配置:

spring:
  cloud:
    gateway:
      default-filters:
      - AddResponseHeader=X-Response-Default-Red, Default-Blue
      - PrefixPath=/httpbin

我们可以使用这种默认的配置来优化我们之前的配置文件,看优化前的:

spring:
  application:
    name: gateway
  cloud:
    gateway:
#      discovery:
#        locator:
#          enabled: true # 启用后,默认会通过/{serviceName}/**这样的请求,将其分发到serviceName的实例上去处理;但启用这种方式后,在routes中配置的就不生效了 ;
      routes:
        - id: circuitbreaker_route # 配置熔断限流
          uri: lb://service0
          predicates:
            - Path=/service0/**
          filters:
            - name: CircuitBreaker  # 熔断
              args:
                name: backendA
                statusCodes:
                  - 500   # 必须配置,如果不配置的话上述的配置不生效
#                fallbackUri: forward:/test # 失败后执行的请求
            - StripPrefix=1
            - name: RequestRateLimiter # 限流
              args:
                redis-rate-limiter.replenishRate: 1 # 令牌生成速度,每s生成多少个
                redis-rate-limiter.burstCapacity: 2 # 令牌桶容量
                redis-rate-limiter.requestedTokens: 1 # 每个请求消耗的令牌数

resilience4j.circuitbreaker:
  configs:
    default:
      slidingWindowSize: 10
      minimumNumberOfCalls: 5 # 计算错误率的最小访问次数,超过这个次数后才会计算错误率并判断是否要进行限流、熔断(但实际不会到这个值,比如配置容忍错误率为50%,配置当前值为10,那么如果前5个都失败就会直接熔断
      permittedNumberOfCallsInHalfOpenState: 3  # 半开状态允许的请求数
      automaticTransitionFromOpenToHalfOpenEnabled: true # 是否在没有请求的情况下由全开自动恢复到半开,设置成true时需要额外的线程进行监控
      waitDurationInOpenState: 2s # 由全开到半开等待的时间
      failureRateThreshold: 50  # 熔断器打开的失败阈值,也即超过指定比例时熔断器将被打开
      eventConsumerBufferSize: 10
  instances:
    backendA:
      baseConfig: default

server:
  port: 8000

management:
  endpoints:
    web:
      exposure:
        include: gateway

如果有新的service,也需要修改这个配置文件,然后重启网关;这比较蛋痛,通过default-filters改造后,配置文件如下:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 启用后,默认会通过/{serviceName}/**这样的请求,将其分发到serviceName的实例上去处理;
      default-filters: # 默认的拦截器,对所有请求都生效
        - name: CircuitBreaker  # 熔断
          args:
            name: backendA
            statusCodes:
              - 500   # 必须配置,如果不配置的话上述的配置不生效
            #                fallbackUri: forward:/test # 失败后执行的请求
        - name: RequestRateLimiter # 限流
          args:
            redis-rate-limiter.replenishRate: 1 # 令牌生成速度,每s生成多少个
            redis-rate-limiter.burstCapacity: 2 # 令牌桶容量
            redis-rate-limiter.requestedTokens: 1 # 每个请求消耗的令牌数

resilience4j.circuitbreaker:
  configs:
    default:
      slidingWindowSize: 10
      minimumNumberOfCalls: 5 # 计算错误率的最小访问次数,超过这个次数后才会计算错误率并判断是否要进行限流、熔断(但实际不会到这个值,比如配置容忍错误率为50%,配置当前值为10,那么如果前5个都失败就会直接熔断
      permittedNumberOfCallsInHalfOpenState: 3  # 半开状态允许的请求数
      automaticTransitionFromOpenToHalfOpenEnabled: true # 是否在没有请求的情况下由全开自动恢复到半开,设置成true时需要额外的线程进行监控
      waitDurationInOpenState: 2s # 由全开到半开等待的时间
      failureRateThreshold: 50  # 熔断器打开的失败阈值,也即超过指定比例时熔断器将被打开
      eventConsumerBufferSize: 10
  instances:
    backendA:
      baseConfig: default

server:
  port: 8000

management:
  endpoints:
    web:
      exposure:
        include: gateway

不需要再给每个服务进行配置了,添加服务的时候也不需要对网关做改动与重启。

5.3 内置拦截器

当前Spring Cloud Gateway内置以下拦截器:

  • AddRequestHeader:添加报文头
  • AddRequestParameter:添加请求参数
  • AddResponseHeader:添加返回报文头
  • DedupeResponseHeader:删除返回报文头
  • CircuitBreaker:熔断
  • FallbackHeaders:失败后调用fallbackUri地址时,并异常信息塞到请求头中;
  • MapRequestHeader:请求报文头中的报文名称转换
  • PrefixPath:为请求统一添加前缀
  • RequestRateLimit:限流
  • RedirectTo:转发
  • RemoveRequestHeader:删除请求报文头
  • RemoveResponseHeader:删除返回报文头
  • RemoveRequestParameter:删除请求参数
  • RewritePath:重定向
  • StripePrefix:自动移除路径前面的串,如指定为1时,请求路径为/test/call,那么处理后将会访问/call
  • Retry:重试
  • RequestSize:请求报文大小,默认为5M,如上传文件时超过5M,不修改此参数将会报错。
  • TokenReply:使用Spring Security OAuth2时,配置这个Filter会将前端访问传过来的Token转发给实际调用的服务;
  • ...

具体使用请参考官方文档。

6 自定义拦截器

在实际项目中,我们可能需要根据用户请求判断是否有权限访问对应的接口地址,用户与接口权限关系可能被后台管理动态的维护并且存储在MySql数据库中;此时我们可以在网关进行统一拦截,权限不足的请求直接返回前端401异常,实现如下,本实现省略了具体的登录用户信息获取及权限查询等步骤,仅做一个模拟,具体实现以业务需求为准。

6.1 定义拦截器

/**
 * @author LiuQi 2021/4/28-18:11
 * @version V1.0
 **/
@Component
public class PreGatewayFilterFactory extends AbstractGatewayFilterFactory
 {
    public PreGatewayFilterFactory() {
        super(Config.class);
    }

    @Override
    public GatewayFilter apply(Config config) {
        return (exchange, chain) -> {
            // 模拟无权限,直接返回401  
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            
            // 不再调用后续的拦截器
            return Mono.empty();
        };
    }

    public static class Config {
        //Put the configuration properties for your filter here
    }
}

在这个拦截器中,我们将response的状态码设置成UNAUTHORIZED,并返回一个空的Mono;返回空的Mono会使得后续的Filter不会继续执行。

6.2 配置文件引用

然后在配置文件中引用这个Factory:

spring:
  application:
    name: gateway
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 启用后,默认会通过/{serviceName}/**这样的请求,将其分发到serviceName的实例上去处理;
      default-filters: # 默认的拦截器,对所有请求都生效
        - name: Pre

注意我们在配置文件中使用的名称是Pre,Spring Cloud Gateway将会自动在后面拼上GatewayFilterFactory然后去容器中找到对应的实例使用。也就是说,我们自定义的Factory其名称必须是以GatewayFilterFactory结尾,否则使用不了。

7 部署情况

引入网关后的集群部署情况:

集群部署情况

这个时候我们的集群才真正成为了一个集群,能够真正的向外提供服务了。

但还是缺少一些关键东西,如整个集群的监控等。接下来的文章中将对这一内容进行讲解

相关推荐

安全教育登录入口平台(安全教育登录入口平台官网)

122交通安全教育怎么登录:122交通网的注册方法是首先登录网址http://www.122.cn/,接着打开网页后,点击右上角的“个人登录”;其次进入邮箱注册,然后进入到注册页面,输入相关信息即可完...

大鱼吃小鱼经典版(大鱼吃小鱼经典版(经典版)官方版)

大鱼吃小鱼小鱼吃虾是于谦跟郭麒麟的《我的棒儿呢?》郭德纲说于思洋郭麒麟作诗的相声,最后郭麒麟做了一首,师傅躺在师母身上大鱼吃小鱼小鱼吃虾虾吃水水落石出师傅压师娘师娘压床床压地地动山摇。...

谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
  • 谷歌地球下载高清卫星地图(谷歌地球地图下载器)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)
哪个软件可以免费pdf转ppt(免费的pdf转ppt软件哪个好)

要想将ppt免费转换为pdf的话,我们建议大家可以下一个那个wps,如果你是会员的话,可以注册为会员,这样的话,在wps里面的话,就可以免费将ppt呢转换为pdfpdf之后呢,我们就可以直接使用,不需要去直接不需要去另外保存,为什么格式转...

2026-02-04 09:03 off999

电信宽带测速官网入口(电信宽带测速官网入口app)

这个网站看看http://www.swok.cn/pcindex.jsp1.登录中国电信网上营业厅,宽带光纤,贴心服务,宽带测速2.下载第三方软件,如360等。进行在线测速进行宽带测速时,尽...

植物大战僵尸95版手机下载(植物大战僵尸95 版下载)

1可以在应用商店或者游戏平台上下载植物大战僵尸95版手机游戏。2下载教程:打开应用商店或者游戏平台,搜索“植物大战僵尸95版”,找到游戏后点击下载按钮,等待下载完成即可安装并开始游戏。3注意:确...

免费下载ppt成品的网站(ppt成品免费下载的网站有哪些)

1、Chuangkit(chuangkit.com)直达地址:chuangkit.com2、Woodo幻灯片(woodo.cn)直达链接:woodo.cn3、OfficePlus(officeplu...

2025世界杯赛程表(2025世界杯在哪个国家)

2022年卡塔尔世界杯赛程公布,全部比赛在卡塔尔境内8座球场举行,2022年,决赛阶段球队全部确定。揭幕战于当地时间11月20日19时进行,由东道主卡塔尔对阵厄瓜多尔,决赛于当地时间12月18日...

下载搜狐视频电视剧(搜狐电视剧下载安装)

搜狐视频APP下载好的视频想要导出到手机相册里方法如下1、打开手机搜狐视频软件,进入搜狐视频后我们点击右上角的“查找”,找到自已喜欢的视频。2、在“浏览器页面搜索”窗口中,输入要下载的视频的名称,然后...

pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
  • pubg免费下载入口(pubg下载入口官方正版)
永久免费听歌网站(丫丫音乐网)

可以到《我爱音乐网》《好听音乐网》《一听音乐网》《YYMP3音乐网》还可以到《九天音乐网》永久免费听歌软件有酷狗音乐和天猫精灵,以前要跳舞经常要下载舞曲,我从QQ上找不到舞曲下载就从酷狗音乐上找,大多...

音乐格式转换mp3软件(音乐格式转换器免费版)

有两种方法:方法一在手机上操作:1、进入手机中的文件管理。2、在其中选择“音乐”,将显示出手机中的全部音乐。3、点击“全选”,选中所有音乐文件。4、点击屏幕右下方的省略号图标,在弹出菜单中选择“...

电子书txt下载(免费的最全的小说阅读器)

1.Z-library里面收录了近千万本电子书籍,需求量大。2.苦瓜书盘没有广告,不需要账号注册,使用起来非常简单,直接搜索预览下载即可。3.鸠摩搜书整体风格简洁清晰,书籍资源丰富。4.亚马逊图书书籍...

最好免费观看高清电影(播放免费的最好看的电影)

在目前的网上选择中,IMDb(互联网电影数据库)被认为是最全的电影网站之一。这个网站提供了各种类型的电影和电视节目的海量信息,包括剧情介绍、演员表、评价、评论等。其还提供了有关电影制作背后的详细信息,...

孤单枪手2简体中文版(孤单枪手2简体中文版官方下载)

要将《孤胆枪手2》游戏的征兵秘籍切换为中文,您可以按照以下步骤进行操作:首先,打开游戏设置选项,通常可以在游戏主菜单或游戏内部找到。然后,寻找语言选项或界面选项,点击进入。在语言选项中,选择中文作为游...

取消回复欢迎 发表评论: