Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)【二】【SpringBoot项目实现商品服务器端是调用】

x33g5p2x  于2021-12-30 转载在 Java  
字(18.5k)|赞(0)|评价(0)|浏览(422)
SpringCloud学习目录点击跳转对应的文章
Java之 Spring Cloud 微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务 Eureka (第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建Ribbon(第一个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Consul(第一个阶段)【四】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Hystrix (第二个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建Sentinel (第二个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建网关 nginx,Zuul(第三个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务的链路追踪 Sleuth 和 Zipkin(第三个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务的 Spring Cloud Stream(第四个阶段)【一】【SpringBoot项目实现商品服务器端调用】
Java之 Spring Cloud 微服务的 SpringCloud Config 配置中心(第四个阶段)【二】【SpringBoot项目实现商品服务器端调用】
Java之 Spring Cloud 微服务的开源配置中心Apollo(第四个阶段)【三】【SpringBoot项目实现商品服务器端调用】

一、微服务网关GateWay

Zuul 1.x 是一个基于阻塞 IO 的 API Gateway 以及 Servlet;直到 2018 年 5 月,Zuul 2.x(基于Netty,也是非阻塞的,支持长连接)才发布,但 Spring Cloud 暂时还没有整合计划。Spring CloudGateway 比 Zuul 1.x 系列的性能和功能整体要好。

1、 Gateway简介

(1) 简介

Spring Cloud Gateway 是 Spring 官方基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,旨在为微服务架构提供一种简单而有效的统一的 API 路由管理方式,统一访问接口。

SpringCloud Gateway 作为 Spring Cloud 生态系中的网关,目标是替代 Netflix ZUUL,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控/埋点,和限流等。

它是基于Nttey的响应式开发模式。

上表为Spring Cloud Gateway与Zuul的性能对比,从结果可知,Spring Cloud Gateway的RPS是Zuul的1.6倍

(2) 核心概念

  1. 路由(route) 路由是网关最基础的部分,路由信息由一个ID、一个目的URL、一组断言工厂和一组Filter组成。如果断言为真,则说明请求URL和配置的路由匹配。
  2. 断言(predicates) Java8中的断言函数,Spring Cloud Gateway中的断言函数输入类型是Spring5.0框架中的ServerWebExchange。Spring Cloud Gateway中的断言函数允许开发者去定义匹配来自Http Request中的任何信息,比如请求头和参数等。
  3. 过滤器(filter) 一个标准的Spring webFilter,Spring Cloud Gateway中的Filter分为两种类型,分别是Gateway Filter和Global Filter。过滤器Filter可以对请求和响应进行处理。

二、入门案例

1、入门案例

(1)创建工程导入依赖

在项目中添加新的模块 gateway_server ,并导入依赖

<dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
  • 注意SpringCloud Gateway使用的web框架为webflux,和SpringMVC不兼容。引入的限流组件是hystrix。redis底层不再使用jedis,而是lettuce。
(2) 配置启动类

package cn.itbleubox.gateway;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class GatewayServerApplication {
    public static void main(String[] args) {
        SpringApplication.run(GatewayServerApplication.class, args);
    }
}
(3) 编写配置文件

创建 application.yml 配置文件

server:
  port: 8080 #服务端口
spring:
  application:
    name: api-gateway-server #指定服务名
#配置SpringCloud Gateway的路由
  cloud:
    gateway:
      routes:
        #配置路由: 路由id路由到微服务的uri,断言(判断条件)
        - id: product-service
          uri: http://127.0.0.1:9001
          predicates:
            - Path=/product/**
  • id:我们自定义的路由 ID,保持唯一
  • uri:目标服务地址
  • predicates:路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
  • filters:过滤规则,暂时没用。
(4)运行测试

运行报错

SpringCloneGateWay内部是通过netty + webflux 实现的
webflux 实现和SpringMVC存在冲突
我们将父工程的web依赖移动到需要的工程当中,

移动到

重新运行

运行成功

其他的也重新运行一下

访问:http://localhost:8080/product/1

三、路由规则

Spring Cloud Gateway 的功能很强大,前面我们只是使用了 predicates 进行了简单的条件匹配,其实Spring Cloud Gataway 帮我们内置了很多 Predicates 功能。

在 Spring Cloud Gateway 中 Spring 利用Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件匹配到对应的路由。

1、动态路由(面向服务的路由)

和zuul网关类似,在SpringCloud GateWay中也支持动态路由:即自动的从注册中心中获取服务列表并访问。

(1)添加注册中心依赖

在工程的pom文件中添加注册中心的客户端依赖(这里以Eureka为例)

<dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
        </dependency>

(2)配置动态路由

server:
  port: 8080 #服务端口
spring:
  application:
    name: api-gateway-server #指定服务名
#配置SpringCloud Gateway的路由
  cloud:
    gateway:
      routes:
        #配置路由: 路由id路由到微服务的uri,断言(判断条件)
        - id: product-service
          #uri: http://127.0.0.1:9001
          uri: lb://service-product   # lb://代表的是根据微服务名称从注册中心拉取服务请求
          predicates:
            - Path=/product/**
# eureka注册中心
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册

uri : uri以 lb: //开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称

  • 运行测试

访问:http://localhost:8080/product/1

2、 过滤器

Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通过过滤器的形式来实现的。那么接下来我们一起来研究一下Gateway中的过滤器

(1)案例改造

1)重写转发路径

修改 application.yml ,将匹配路径改为 /product-service/**

predicates:
            #- Path=/product/**
          - Path=/product-service/**  #将当前的请求转发到对应的 http://127.0.0.1:9001/product/1
          filters: #配置路由过滤器 http://localhost:8080/product-service/product/1 --> http://127.0.0.1:9001/product/1
          - RewritePath=/product-service/(?<segment>.*), /$\{segment}  #路径重写的过滤器
  • 运行测试

访问:http://localhost:8080/product-service/product/1

2)微服务名称转发

discovery:
        locator:
          enabled:  true #开启根据服务名称自动转发
          lower-case-service-id: true  #微服务名称以小写形式呈现

运行测试访问

访问:http://localhost:8080/service-product/product/1

访问:http://localhost:8080/service-order/order/1

(2)过滤器概述

Spring Cloud Gateway除了具备请求路由功能之外,也支持对请求的过滤。通过Zuul网关类似,也是通过过滤器的形式来实现的。那么接下来我们一起来研究一下Gateway中的过滤器

1) 过滤器基础
01)过滤器的生命周期

Spring Cloud Gateway 的 Filter 的生命周期不像 Zuul 的那么丰富,它只有两个:“pre” 和 “post”。

  • PRE: 这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等。
  • POST:这种过滤器在路由到微服务以后执行。
    这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

02)过滤器类型

Spring Cloud Gateway 的 Filter 从作用范围可分为另外两种GatewayFilter 与 GlobalFilter。

GatewayFilter:应用到单个路由或者一个分组的路由上。
GlobalFilter:应用到所有的路由上。

2) 局部过滤器

局部过滤器(GatewayFilter),是针对单个路由的过滤器。可以对访问的URL过滤,进行切面处理。
在Spring Cloud Gateway中通过GatewayFilter的形式内置了很多不同类型的局部过滤器。
这里简单将Spring Cloud Gateway内置的所有过滤器工厂整理成了一张表格,虽然不是很详细,但能作为速览使用。

如下

每个过滤器工厂都对应一个实现类,
并且这些类的名称必须以 GatewayFilterFactory 结尾,
这是Spring Cloud Gateway的一个约定,

例如 AddRequestHeader 对应的实现类为

AddRequestHeaderGatewayFilterFactory 。
对于这些过滤器的使用方式可以参考官方文档

3) 全局过滤器

全局过滤器(GlobalFilter)作用于所有路由,Spring Cloud Gateway 定义了Global Filter接口,用户可以自定义实现自己的Global Filter。
通过全局过滤器可以实现对权限的统一校验,安全性验证等功能,并且全局过滤器也是程序员使用比较多的过滤器。

Spring Cloud Gateway内部也是通过一系列的内置全局过滤器对整个路由转发进行处理如下:

01)全局过滤器代码实现

package cn.itbleubox.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/* 自定义全局过滤器 实现 globalfilter , ordered 接口 */
@Component      //将当前过滤器交给Spring管理
public class LoginFilter implements GlobalFilter, Ordered {
    /* 执行过滤器当中的业务逻辑 */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("执行了自定义的全局过滤器");
        return chain.filter(exchange); //继续向下执行
    }
    /* 指定过滤器的执行顺序,返回值越小,执行优先级别越高 */
    @Override
    public int getOrder() {
        return 0;
    }
}
02)运行测试

访问:http://localhost:8080/service-order/order/1

控制台

4)统一鉴权

内置的过滤器已经可以完成大部分的功能,但是对于企业开发的一些业务功能处理,还是需要我们自己编写过滤器来实现的,那么我们一起通过代码的形式自定义一个过滤器,去完成统一的权限校验。

01)鉴权逻辑

开发中的鉴权逻辑:

  • 当客户端第一次请求服务时,服务端对用户进行信息认证(登录)
  • 认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证
  • 以后每次请求,客户端都携带认证的token
  • 服务端对token进行解密,判断是否有效。

如上图,对于验证用户是否已经登录鉴权的过程可以在网关层统一检验。
检验的标准就是请求中是否携带token凭证以及token的正确性。

02)代码实现

继续完善上述的LoginFilter,去校验所有请求的请求参数中是否包含“token”,
如何不包含请求参数“token”则不转发路由,否则执行正常的逻辑。

package cn.itbleubox.gateway.filter;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.core.Ordered;
import org.springframework.http.HttpStatus;
import org.springframework.http.server.reactive.ServerHttpRequest;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;
/* 自定义全局过滤器 实现 globalfilter , ordered 接口 */
@Component      //将当前过滤器交给Spring管理
public class LoginFilter implements GlobalFilter, Ordered {
    /* 执行过滤器当中的业务逻辑 对请求参数中的access-token进行判断 如果存此参数:代表以及认证成功 如果不存在次参数:认证失败 ServerWebExchange : 相当于请求和响应的上下文(Zuul当中的RequestContext) */
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        System.out.println("执行了自定义的全局过滤器");
        //1.获取请求参数access-token
        String token = exchange.getRequest().getQueryParams().getFirst("access-token");
        //2、判断是否存在
        if(token == null){
            //3、如果不存在:认证失败
            System.out.println("没有登录");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();//请求结束,--将请求拦截
        }
        //4、如果存在,继续执行
        return chain.filter(exchange); //继续向下执行
    }
    /* 指定过滤器的执行顺序,返回值越小,执行优先级别越高 */
    @Override
    public int getOrder() {
        return 0;
    }
}
  • 自定义全局过滤器需要实现GlobalFilter和Ordered接口。
  • 在filter方法中完成过滤器的逻辑判断处理
  • 在getOrder方法指定此过滤器的优先级,返回值越大级别越低
  • ServerWebExchange 就相当于当前请求和响应的上下文,存放着重要的请求-响应属性、请求实例和响应实例等等。
    一个请求中的request,response都可以通过ServerWebExchange 获取
  • 调用 chain.filter 继续向下游执行

重新启动运行测试

访问:http://localhost:8080/service-order/order/1

控制台

再次访问添加token
http://localhost:8080/service-order/order/1?access-token=123

3、网关限流

(1) 常见的限流算法

1) 计数器

计数器限流算法是最简单的一种限流实现方式。

其本质是通过维护一个单位时间内的计数器,每次请求计数器加1,当单位时间内计数器累加到大于设定的阈值,则之后的请求都被拒绝,直到单位时间已经过去,再将计数器重置为零

2) 漏桶算法

漏桶算法可以很好地限制容量池的大小,从而防止流量暴增。漏桶可以看作是一个带有常量服务时间的单服务器队列,如果漏桶(包缓存)溢出,那么数据包会被丢弃。 在网络中,漏桶算法可以控制端口的流量输出速率,平滑网络上的突发流量,实现流量整形,从而为网络提供一个稳定的流量。

3) 令牌桶算法

令牌桶算法是对漏桶算法的一种改进,桶算法能够限制请求调用的速率,而令牌桶算法能够在限制调用的平均速率的同时还允许一定程度的突发调用。

在令牌桶算法中,存在一个桶,用来存放固定数量的令牌。

算法中存在一种机制,以一定的速率往桶中放令牌。每次请求调用需要先获取令牌,只有拿到令牌,才有机会继续执行,否则选择选择等待可用的令牌、或者直接拒绝。

放令牌这个动作是持续不断的进行,如果桶中令牌数达到上限,就丢弃令牌,所以就存在这种情况,桶中一直有大量的可用令牌,这时进来的请求就可以直接拿到令牌执行,比如设置qps为100,那么限流器初始化完成一秒后,桶中就已经有100个令牌了,这时服务还没完全启动好,等启动完成对外提供服务时,该限流器可以抵挡瞬时的100个请求。

所以,只有桶中没有令牌时,请求才会进行等待,最后相当于以一定的速率执行。

(2)基于Filter的限流

SpringCloudGateway官方就提供了基于令牌桶的限流支持。基于其内置的过滤器工厂
RequestRateLimiterGatewayFilterFactory 实现。
在过滤器工厂中是通过Redis和lua脚本结合的方式进行流量控制。

1)环境搭建
  • 安装并启动redis
    启动window本地的redis

打开客户端使用monitor监控redis当中的数据

  • 导入redis的依赖
    首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖,代码如下:

<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
        </dependency>
2)修改application.yml配置文件

在application.yml配置文件中加入限流的配置,代码如下:

server:
  port: 8080 #端口
spring:
  application:
    name: api-gateway-server #服务名称
  redis:
    host: localhost
    pool: 6379
    database: 0
  cloud: #配置SpringCloudGateway的路由
    gateway:
      routes:
        - id: product-service
          uri: lb://service-product
          predicates:
            - Path=/product-service/**
          filters:
            - name: RequestRateLimiter
              args:
                # 使用SpEL从容器中获取对象
                key-resolver: '#{@pathKeyResolver}'
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 3
            - RewritePath=/product-service/(?<segment>.*), /$\{segment}
  # RequestRateLimiter : 使用限流过滤器,SpringCloud gateway提供的
  # 参数 replenishRate : 向令牌桶中填充的速率
  # burstCapacity : 令牌桶的容量
#eureka注册中心
eureka:
  client:
    service-url:
      defaultZone: http://localhost:9000/eureka/
  instance:
    prefer-ip-address: true #使用ip地址注册

application.yml 中添加了redis的信息,并配置了RequestRateLimiter的限流过滤器:

  • burstCapacity,令牌桶总容量。
  • replenishRate,令牌桶每秒填充平均速率。
  • key-resolver,用于限流的键的解析器的 Bean 对象的名字。
    它使用 SpEL 表达式根据#
    {@beanName}从 Spring 容器中获取 Bean 对象。
3)配置KeyResolver
01)基于请求IP的 127.0.0.1

为了达到不同的限流效果和规则,可以通过实现 KeyResolver 接口,定义不同请求类型的限流键。

package cn.itbleubox.gateway;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Configuration
public class KeyResolverConfiguration {
    /* 编写基于请求路径的限流规则 //abc //基于请求IP的 127.0.0.1 //基于参数的 */
    @Bean
    public KeyResolver pathKeyResolver() {
        //自定义的KeyResolver
        return new KeyResolver() {
            /* ServerWebExchange : 上下文参数 */
            @Override
            public Mono<String> resolve(ServerWebExchange exchange) {
                return Mono.just(exchange.getRequest().getPath().toString());
            }
        };
    }

}

运行测试

方便测试先将Token的内容注释掉

访问:http://localhost:8080/product-service/product/1

现在我们疯狂刷新,在之前我们设置了1秒内只能访问3次
点击F5疯狂刷新

查看redis的监控信息

02) 基于请求参数的限流(规定访问路径的参数)

/* 基于请求参数的限流 请求 abc ? userId = 1 */
    @Bean
    public KeyResolver userKeyResolver() {
        return exchange -> Mono.just(
                exchange.getRequest().getQueryParams().getFirst("userId")
        );
    }

修改application.yml配置文件

key-resolver: '#{@userKeyResolver}'

运行测试

在这之前将之前的基于请求IP的限流注释掉,否则会报错

访问:http://localhost:8080/product-service/product/1

抛出错误

设置请求参数
http://localhost:8080/product-service/product/1?userId=1

现在我们疯狂刷新,
在之前我们设置了1秒内只能访问3次,
点击F5疯狂刷新

(3) 基于Sentinel的限流

Sentinel 支持对 Spring Cloud Gateway、Zuul 等主流的 API Gateway 进行限流。

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源

维度的限流:route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的

  • routeId自定义 API 维度:用户可以利用 Sentinel 提供的 API 来
  • 自定义一些 API 分组Sentinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,

此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:

  • GatewayFlowRule :网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
  • ApiDefinition :用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api ,请求 path 模式为 /foo/**/baz/** 的都归到 my_api 这个 API分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。
1)环境搭建

导入Sentinel 的响应依赖

2) 编写配置类

GatewayConfiguration

/* sentinel 限流的配置 */

@Configuration
public class GatewayConfiguration {

    private final List<ViewResolver> viewResolvers;

    private final ServerCodecConfigurer serverCodecConfigurer;

    public GatewayConfiguration(ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }

    /** * 配置限流的异常处理器:SentinelGatewayBlockExceptionHandler */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler() {
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }

    /** * 配置限流过滤器 */
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    /** * 配置初始化的限流参数 * 用于指定资源的限流规则, * 1.资源名称(路由ID) * 2、配置统计时间 * 3、配置限流阀值 */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();

        rules.add(new GatewayFlowRule("product-service")
                .setCount(1)
                .setIntervalSec(1)
        );
        GatewayRuleManager.loadRules(rules);
    }
/* @PostConstruct public void initBlockHandlers() { BlockRequestHandler blockRequestHandler = new BlockRequestHandler() { public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) { Map map = new HashMap<>(); map.put("code", 001); map.put("message", "对不起,接口限流了"); return ServerResponse.status(HttpStatus.OK). contentType(MediaType.APPLICATION_JSON_UTF8). body(BodyInserters.fromObject(map)); } }; GatewayCallbackManager.setBlockHandler(blockRequestHandler); } @PostConstruct private void initCustomizedApis() { Set<ApiDefinition> definitions = new HashSet<>(); ApiDefinition api1 = new ApiDefinition("product_api") .setPredicateItems(new HashSet<ApiPredicateItem>() {{ add(new ApiPathPredicateItem().setPattern("/product-service/product/**"). setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)); }}); ApiDefinition api2 = new ApiDefinition("order_api") .setPredicateItems(new HashSet<ApiPredicateItem>() {{ add(new ApiPathPredicateItem().setPattern("/order-service/order")); }}); definitions.add(api1); definitions.add(api2); GatewayApiDefinitionManager.loadApiDefinitions(definitions); } */
}
3) 网关配置

设置配置文件,只需要注释令牌桶的配置就行

运行测试

访问:http://localhost:8080/product-service/product/1

现在我们疯狂刷新,
在之前我们设置了1秒内只能访问1次,
点击F5疯狂刷新

4) 自定义异常提示

当触发限流后页面显示的是Blocked by Sentinel: FlowException。为了展示更加友好的限流提示,Sentinel支持自定义异常处理。

您可以在 GatewayCallbackManager 注册回调进行定制:

  • setBlockHandler :注册函数用于实现自定义的逻辑处理被限流的请求,对应接口为
    BlockRequestHandler 。默认实现为
    DefaultBlockRequestHandler ,当被限流时会返回类似
    于下面的错误信息: Blocked by Sentinel: FlowException

/* 自定义限流处理器 */
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 001);
                map.put("message", "对不起,接口限流了");
                return ServerResponse.status(HttpStatus.OK).
                        contentType(MediaType.APPLICATION_JSON_UTF8).
                        body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

重新运行并测试

访问:http://localhost:8080/product-service/product/1

5)自定义API限流分组

/** * 配置初始化的限流参数 * 用于指定资源的限流规则, * 1.资源名称(路由ID) * 2、配置统计时间 * 3、配置限流阀值 */
    @PostConstruct
    public void initGatewayRules() {
        Set<GatewayFlowRule> rules = new HashSet<>();

        rules.add(new GatewayFlowRule("product-service")
                .setCount(1)
                .setIntervalSec(1)
        );
        rules.add(new GatewayFlowRule("product_api")
                .setCount(1)
                .setIntervalSec(1)

        );

        GatewayRuleManager.loadRules(rules);
    }

    /* 自定义API限流分组 1、定义分组 2、对小组配置限流规则 */
    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("product_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    //已/product-service/product/**开头所有URL
                    add(new ApiPathPredicateItem().setPattern("/product-service/product/**").
                            setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX));
                }});
        ApiDefinition api2 = new ApiDefinition("order_api")
                .setPredicateItems(new HashSet<ApiPredicateItem>() {{
                    //完全匹配/order-service/order 的 url
                    add(new ApiPathPredicateItem().setPattern("/order-service/order"));
                }});
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

运行测试

http://localhost:8080/product-service/product/1

四、 网关高可用

高可用HA(High Availability)是分布式系统架构设计中必须考虑的因素之一,

它通常是指,通过设计减少系统不能提供服务的时间。

我们都知道,单点是系统高可用的大敌,单点往往是系统高可用最大的风险和敌人,应该尽量在系统设计的过程中避免单点。

方法论上,高可用保证的原则是“集群化”,或者叫“冗余”:只有一个单点,挂了服务会受影响;如果有冗余备份,挂了还有其他backup能够顶上。

我们实际使用 Spring Cloud Gateway 的方式如上图,不同的客户端使用不同的负载将请求分发到后端的 Gateway,Gateway 再通过HTTP调用后端服务,最后对外输出。因此为了保证 Gateway 的高可用性,前端可以同时启动多个 Gateway 实例进行负载,在 Gateway 的前端使用 Nginx 或者 F5 进行负载转发以达到高可用性。

1、 准备多个GateWay工程

将限流的一些东西注释掉

  • 第一个网关8080

  • 第二个网关8081

修改端口号8081

启动第二个端口号

访问测试http://localhost:8081/product-service/product/1

2、配置ngnix

#配置多台服务器(这里只在一台服务器上的不同端口)
    upstream gateway {
       server 127.0.0.1:8081;
       server 127.0.0.1:8080;
    }
        location / {
             proxy_pass http://gateway;
        }

启动nginx
在浏览器上通过访问http://localhost/order-service/order/buy/1请求的效果和之前是一样的。这次关闭一台网关服务器,还是可以支持部分请求的访问。

访问:http://localhost/product-service/product/1

这个时候我们将8081关闭

http://localhost:8081/product-service/product/1

访问:http://localhost/product-service/product/1

SpringCloud学习目录点击跳转对应的文章
Java之 Spring Cloud 微服务搭建(第一个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务 Eureka (第一个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建Ribbon(第一个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Consul(第一个阶段)【四】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Feign组件(第二个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建 Hystrix (第二个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建Sentinel (第二个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建网关 nginx,Zuul(第三个阶段)【一】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务搭建网关SpringCloud Gateway微服务网关GateWay(第三个阶段)【二】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务的链路追踪 Sleuth 和 Zipkin(第三个阶段)【三】【SpringBoot项目实现商品服务器端是调用】
Java之 Spring Cloud 微服务的 Spring Cloud Stream(第四个阶段)【一】【SpringBoot项目实现商品服务器端调用】
Java之 Spring Cloud 微服务的 SpringCloud Config 配置中心(第四个阶段)【二】【SpringBoot项目实现商品服务器端调用】
Java之 Spring Cloud 微服务的开源配置中心Apollo(第四个阶段)【三】【SpringBoot项目实现商品服务器端调用】

相关文章

最新文章

更多