第五章 Gateway--服务网关

2023-11-20

接上一篇文章开始网关之旅,首先告诉大家网关是什么,Gateway简介,怎么配置,怎么入门,执行流程等等相关介绍。

第一章:微服务的架构介绍发展
第二章:微服务环境搭建
第三章:Nacos Discovery--服务治理
第四章:Sentinel--服务容错
第五章:Gateway--服务网关
第六章:Sleuth--链路追踪
第七章:Rocketmq--消息驱动

5.1网关简介

大家都都知道在微服务架构中,一个系统会被拆分为很多个微服务。那么作为客户端要如何去调用
这么多的微服务呢?如果没有网关的存在,我们只能在客户端记录每个微服务的地址,然后分别去用。

这样的架构,会存在着诸多的问题:

客户端多次请求不同的微服务,增加客户端代码或配置编写的复杂性

认证复杂,每个服务都需要独立认证。

存在跨域请求,在一定场景下处理相对复杂。

上面的这些问题可以借助API网关来解决。

所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。

添加上API网关之后,系统的架构图变成了如下所示:

我们也可以观察下,我们现在的整体架构图:

在业界比较流行的网关,有下面这些:

Ngnix+lua

使用nginx的反向代理和负载均衡可实现对api服务器的负载均衡及高可用

lua是一种脚本语言,可以来编写一些简单的逻辑, nginx支持lua脚本

Kong

基于Nginx+Lua开发,性能高,稳定,有多个可用的插件(限流、鉴权等等)可以开箱即用。问题:只支持Http协议;二次开发,自由扩展困难;提供管理API,缺乏更易用的管控、配置方式。

Zuul Netflix

开源的网关,功能丰富,使用JAVA开发,易于二次开发 问题:缺乏管控,无法动态配置;依赖组件较多;处理Http请求依赖的是Web容器,性能不如Nginx

Spring Cloud Gateway

Spring公司为了替换Zuul而开发的网关服务,将在下面具体介绍。

注意:SpringCloud Alibaba 技术栈中并没有提供自己的网关,我们可以采用Spring Cloud Gateway来做网关

5.2 Gateway 简介

Spring Cloud Gateway是Spring公司基于Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。它的目标是替代Netflix Zuul,其不仅提供统一的路由方式,并且基于 Filter 链的方式提供了网关基本的功能,例如:安全,监控和限流。

优点:

  • 性能强劲:是第一代网关Zuul的1.6倍
  • 功能强大:内置了很多实用的功能,例如转发、监控、限流等
  • 设计优雅,容易扩展
    缺点:
  • 其实现依赖Netty与WebFlux,不是传统的Servlet编程模型,学习成本高不能将其部署在Tomcat、Jetty等Servlet容器里,只能打成jar包执行
  • 需要Spring Boot 2.0及以上的版本,才支持

5.3 Gateway 快速入门

要求: 通过浏览器访问api网关,然后通过网关将请求转发到商品微服务

5.3.1 基础版

1)创建一个 api-gateway 的模块, 导入相关依赖

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <packaging>jar</packaging>
    <parent>
        <artifactId>micro-mall</artifactId>
        <groupId>com.bxs</groupId>
        <version>1.0.0</version>
    </parent>
    <artifactId>mall-gateway</artifactId>
    <name>${project.artifactId}</name>
    <description>spring cloud gateway 网关</description>

    <dependencies>
        <!-- gateway 网关 -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
    </dependencies>
</project>

2)创建主类 @SpringBootApplication

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

3)添加配置文件

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    gateway:
      routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
        - id: product_route # 当前路由的标识, 要求唯一
          uri: http://localhost:8081 # 请求要转发到的地址
          order: 1 # 路由的优先级,数字越小级别越高
          predicates: # 断言(就是路由转发要满足的条件)
            - Path=/product-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
          filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=1 # 转发之前去掉1层路径 |

4)启动项目, 并通过网关去访问微服务

 

5.3.2 增强版

1)现在在配置文件中写死了转发路径的地址, 前面我们已经分析过地址写死带来的问题, 接下来我们从注册中心获取此地址。

<dependency>
      <groupId>com.alibaba.cloud</groupId>
      <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

2)在主类上添加注解

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

3)修改配置文件

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      enabled: true
      routes: # 路由数组[路由 就是指定当请求满足什么条件的时候转到哪个微服务]
        - id: product_route # 当前路由的标识, 要求唯一
          uri: http://localhost:8081 # 请求要转发到的地址
          order: 1 # 路由的优先级,数字越小级别越高
          predicates: # 断言(就是路由转发要满足的条件)
            - Path=/product-serv/** # 当请求路径满足Path指定的规则时,才进行路由转发
          filters: # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
            - StripPrefix=1 # 转发之前去掉1层路径 |

4)测试

5.3.3 简写版

1)去掉关于路由的配置

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true

2)启动项目,并通过网关去访问微服务

 

这时候,就发现只要按照网关地址/微服务/接口的格式去访问,就可以得到成功响应。

5.4 Gateway 核心架构

5.4.1 基本概念

路由(Route)gateway 中最基本的组件之一,表示一个具体的路由信息载体。主要定义了下面的几个信息:

  • id:路由标识符,区别于其他 Route
  • uri:路由指向的目的地 uri,即客户端请求最终被转发到的微服务。
  • order:用于多个 Route 之间的排序,数值越小排序越靠前,匹配优先级越高。
  • predicate:断言的作用是进行条件判断,只有断言都返回真,才会真正的执行路由。
  • filter:过滤器用于修改请求和响应信息。

5.4.2 执行流程执行流程大体如下:

1)Gateway ClientGateway Server 发送请求
2)请求首先会被HttpWebHandlerAdapter进行提取组装成网关上下文
3)然后网关的上下文会传递到 DispatcherHandler,它负责将请求分发给 RoutePredicateHandlerMapping
4)RoutePredicateHandlerMapping负责路由查找,并根据路由断言判断路由是否可用
5)如果过断言成功,由FilteringWebHandler创建过滤器链并调用
6)请求会一次经过PreFilter微服务PostFilter的方法,最终返回响应

5.5 断言

Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由

断言就是说: 在 什么条件下 才能进行路由转发

5.5.1 内置路由断言工厂

Spring Cloud Gateway 包括许多内置的断言工厂,所有这些断言都与HTTP请求的不同属性匹配。具体如下:

基于Datetime类型的断言工厂

此类型的断言根据时间做判断,主要有三个:
1. AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
2. BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
3. BetweenRoutePredicateFactory : 接收两个日期参数,判断请求日期是否在指定时间段内

-After=2019-12-31T23:59:59.789+08:00[Asia/Shanghai]

基于远程地址的断言工厂

RemoteAddrRoutePredicateFactory: 接收一个IP地址段,判断请求主机地址是否在地址段中
-RemoteAddr=192.168.1.1/24

基于Cookie的断言工厂

CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
cookie是否具有给定名称且值与正则表达式匹配。
-Cookie=chocolate, ch.

基于Header的断言工厂

HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。
-Header=X-Request-Id, \d+

基于Host的断言工厂

HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
-Host=.testhost.org**

基于Method请求方法的断言工厂

MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
-Method=GET

基于Path请求路径的断言工厂

PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
-Path=/foo/{segment}

基于Query请求参数的断言工厂

QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
-Query=baz, ba.

基于路由权重的断言工厂

WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
routes:
-id: weight_route1 uri: host1 predicates:
-Path=/product/
-Weight=group3, 1
-id: weight_route2 uri: host2 predicates:
-Path=/product/
-Weight= group3, 9

内置路由断言工厂的使用

接下来我们验证几个内置断言的使用:

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: product_route
          uri: lb://service-product
          predicates:
            - Path=/product-serv/**
            - Before=2020-03-28T00:00:00.000+08:00 #限制请求时间在2019-03-28之前
            - Method=POST #限制请求方式为POST
          filters:
            - StripPrefix=1

5.5.2 自定义路由断言工厂

条件 :我们来设定一个场景: 假设我们的应用仅仅让age在(min,max)之间的人来访问。

1)配置 bootstrap.yml

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: product-route
          uri: lb://product-service
          predicates:
            - Path=/product-serv/**
            - Age=18,60 #
          filters:
            - StripPrefix=1

2)自定义一个断言工厂, 实现断言方法

/**
 * @Author Morse
 * @Date 2020-04-17 17:20
 *
 * 这是一个自定义的路由断言工厂类,要求有两个
 * 1. 名字必须是 配置 + RoutePredicateFactory
 * 2. 必须继承 AbstractRoutePredicateFactory<配置类>
 */
@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {

    // 构造函数
    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }

    // 读取配置文件中的参数值,给他赋值到配置类中的属性上
    @Override
    public List<String> shortcutFieldOrder() {
        // 这个位置的顺序必须跟配置文件中的值的顺序对应
        return Arrays.asList("minAge", "maxAge");
    }

    // 断言逻辑
    @Override
    public Predicate<ServerWebExchange> apply(AgeRoutePredicateFactory.Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                // 1. 接收前台传入的age参数
                String ageStr = exchange.getRequest().getQueryParams().getFirst("age");

                // 2. 先判断是否为空
                if (!StringUtils.isEmpty(ageStr)) {
                    // 3. 如果不为空,再进行路由逻辑判断
                    int age = Integer.parseInt(ageStr);
                    if (age < config.getMaxAge() && age > config.getMinAge()) {
                        return true;
                    } else {
                        return false;
                    }
                }
                return false;
            }
        };
    }

    @ApiModel(value = "配置类,用于接收配置文件中的对应参数")
    @Data
    @NoArgsConstructor
    public static class Config {
        private int minAge; //18
        private int maxAge; //60
    }
}

4)启动测试

测试发现当age在(20,60)可以访问,其它范围不能访问

http://localhost:7000/product-serv/product/1?age=30

http://localhost:7000/product-serv/product/1?age=10

5.6 过滤器

三个知识点:

1. 作用:过滤器就是在请求的传递过程中,对请求和响应做一些手脚
2. 生命周期:Pre Post
3 分类:局部过滤器(作用在某一个路由上) 全局过滤器(作用全部路由上)

在 Gateway 中, Filter 的生命周期只有两个:“pre” 和 “post”。

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

Gateway 的 Filter 从作用范围可分为两种:

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

5.6.1 局部过滤器

局部过滤器是针对单个路由的过滤器。

5.6.1.1 内置局部过滤器

在 Spring Cloud Gateway 中内置了很多不同类型的网关路由过滤器。具体如下:
相关链接

序号 过滤器工厂 作用 参数
1 AddRequestHeader 为原始请求添加Header Header的名称与值
2 AddRequestParameter 为原始请求添加请求参数 参数名称及值
3 AddResponseHeader 为原始响应添加Header Header的名称及值
4 DedupeResponseHeader 剔除响应头中重复的值 需要去重的Header名称及去重策略
5 Hystrix 为路由引入Hystrix的断路器保护 HystrixCommand的名称
6 CircuitBreaker
7 FallbackHeaders 为fallbackUrl的请求头中添加具体的异常信息 Header的名称
8 MapRequestHeader
9 PrefixPath 为原始请求路径添加前缀 前缀路径
10 PreserveHostHeader 为请求添加一个 preserveHostHeader=true 的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
11 RequestRateLimiter 用于对请求限流,限流算法为令牌桶 keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
12 RedirectTo 将原始请求重定向到指定的URL http状态码及重定向的url
13 RemoveResponseHeader 为原始请求删除某个Header Header名称
14 RemoveResponseHeader 为原始响应删除某个Header Header名称
15 RemoveRequestParameter 为原始请求删除某个Parameter Parameter名称
16 RewritePath 重写原始的请求路径 原始路径正则表达以及重写路径的正则表达式
17 RewriteLocationResponseHeader
18 RewriteResponseHeader 重写原始响应中的某个Header Header名称,值的正则表达式,重写后的值
19 SaveSession 在转发请求之前,强制执行 WebSession::save 操作
20 SecureHeaders 为原始响应添加一系列起安全作用的响应头 无,支持修改这些安全响应头的值
21 SetPath 修改原始的请求路径 修改后的路径
22 SetRequestHeader 修改原始请求中某个Header的值 Header名称,修改后的值
23 SetResponseHeader 修改原始响应中某个Header的值 Header名称,修改后的值
24 SetStatus 修改原始响应的状态码 HTTP状态码,可以是数字,也可以是字符串
25 StripPrefix 用于阶段原始请求的路径 使用数字表示要截断的路径的数量
26 Retry 针对不同的响应进行重试 retries、statuses、methods、series、exceptions、backoff
27 RequestSize 设置允许接收最大请求包的大小。如果请求包大小超过设置的值,则返回 413 Payload Too Large 请求包大小,单位为字节,默认值为5M
28 ModifyRequestBody 在转发请求之前修改原始请求体内容 修改后的请求体内容
29 ModifyResponseBody 修改原始响应体的内容 修改后的响应体内容
30 Default 为所有路由添加过滤器 过滤器工厂名称及值

内置局部过滤器的使用

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: product-route
          uri: lb://product-service
          predicates:
            - Path=/product-serv/**
            - Age=18,60 #
          filters:
            - StripPrefix=1
            - SetStatus=2000 # 修改返回状态

5.6.1.2 自定义局部过滤器

1)在配置文件中,添加一个Log的过滤器配置

server:
  port: 7000
spring:
  application:
    name: api-gateway-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: consumer
          uri: lb://consumer
          predicates:
            - Path=/consumer-serv/**
            - Age=18,60 #
          filters:
            - StripPrefix=1
            - Log=true,false # 修改返回状态

2)自定义一个过滤器工厂,实现方法

@Slf4j                                                                                                    
@Component                                                                                                
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> 
                                                                                                          
    //构造函数                                                                                                
    public LogGatewayFilterFactory() {                                                                    
        super(LogGatewayFilterFactory.Config.class);                                                      
    }                                                                                                     
                                                                                                          
    //读取配置文件中的参数 赋值到 配置类中                                                                                 
    @Override                                                                                             
    public List<String> shortcutFieldOrder() {                                                            
        return Arrays.asList("consoleLog", "cacheLog");                                                   
    }                                                                                                     
                                                                                                          
    //过滤器逻辑                                                                                               
    @Override                                                                                             
    public GatewayFilter apply(Config config) {                                                           
        return new GatewayFilter() {                                                                      
            @Override                                                                                     
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {              
                if (config.isCacheLog()) {                                                                
                    System.out.println("cacheLog已经开启了......");                                            
                }                                                                                         
                if (config.isConsoleLog()) {                                                              
                    System.out.println("consoleLog已经开启了......");                                          
                }                                                                                         
                return chain.filter(exchange);                                                            
            }                                                                                             
        };                                                                                                
    }                                                                                                     
                                                                                                          
    // 配置类 接收配置参数                                                                                         
    @Data                                                                                                 
    @NoArgsConstructor                                                                                    
    public static class Config {                                                                          
        private boolean consoleLog;                                                                       
        private boolean cacheLog;                                                                         
    }                                                                                                     
}

3)启动测试

5.6.2 全局过滤器

全局过滤器作用于所有路由, 无需配置。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。

5.6.2.1 内置全局过滤器

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

5.6.2.2 自定义全局过滤器

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

开发中的鉴权逻辑:
1)当客户端第一次请求服务时,服务端对用户进行信息认证(登录)

2)认证通过,将用户信息进行加密形成token,返回给客户端,作为登录凭证

3)以后每次请求,客户端都携带认证的token

4)服务端对token进行解密,判断是否有效。

 

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

下面的我们自定义一个GlobalFilter,去校验所有请求的请求参数中是否包含token,如何不包含请求参数token则不转发路由,否则执行正常的逻辑。

/**
 * @Author Morse
 * @Date 2020-04-18 09:51
 *
 * 自定义全局过滤器需要实现 GlobalFilter 和 Ordered 接口
 */
@Component
public class AuthGlobalFilter implements GlobalFilter, Ordered {

    // 完成判断逻辑
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (!StringUtils.equals( token, "admin")) {
            System.out.println("健全失败");
            exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
            return exchange.getResponse().setComplete();
        }
        //  调用chain.filter继续向下游执行
        return chain.filter(exchange);
    }

    // 顺序,数值越小,优先级越高
    @Override
    public int getOrder() {
        return 0;
    }
}

5.7 网关限流

网关是所有请求的公共入口,所以可以在网关进行限流,而且限流的方式也很多,我们本次采用前面学过的Sentinel组件来实现网关的限流。Sentinel支持对Spring Cloud Gateway、Zuul等主流网关进行限流。

从1.6.0版本开始,Sentinel提供了Spring Cloud Gateway的适配模块,可以提供两种资源维度的限流:
route维度:即在Spring配置文件中配置的路由条目,资源名为对应的routeId
自定义API维度:用户可以利用Sentinel提供的API来自定义一些API分组

1)导入依赖

<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>

2)编写配置类

基于 Sentinel 的 Gateway 限流是通过其提供的 Filter 来完成的,使用时只需注入对应的 SentinelGatewayFilter 实例以及 SentinelGatewayBlockExceptionHandler 实例即可。

@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;
    }

    // 初始化一个限流的过滤器
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }

    // 配置初始化的限流参数
    @PostConstruct
    public void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                // 资源名称,对应路由ID
                new GatewayFlowRule("product-route")
                        // 限流阀值
                        .setCount(1)
                        // 统计时间窗口,单位是秒,默认是1秒
                        .setIntervalSec(1)
        );
        GatewayRuleManager.loadRules(rules);
    }

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

    // 自定义限流异常页面
    @PostConstruct
    public void initBlockHandlers() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                Map map = new HashMap<>();
                map.put("code", 0);
                map.put("message", "接口被限流了");
                return ServerResponse.status(HttpStatus.OK)
                        .contentType(MediaType.APPLICATION_JSON_UTF8)
                        .body(BodyInserters.fromObject(map));
            }
        };
        GatewayCallbackManager.setBlockHandler(blockRequestHandler);
    }

}

3)测试

在一秒钟内多次访问http://localhost:7000/product-serv/product/1就可以看到限流启作用了。

4)自定义API分组

自定义API分组是一种更细粒度的限流规则定义

    // 配置初始化的限流参数
    @PostConstruct
    public void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();
        rules.add(
                // 资源名称,对应路由ID
                new GatewayFlowRule("product-route")
                        // 限流阀值
                        .setCount(1)
                        // 统计时间窗口,单位是秒,默认是1秒
                        .setIntervalSec(1)
        );
        rules.add(new GatewayFlowRule("product-api1").setCount(1) .setIntervalSec(1));
        rules.add(new GatewayFlowRule("product-api2").setCount(1) .setIntervalSec(1));
        GatewayRuleManager.loadRules(rules);
    }

    //  自定义API分组
    @PostConstruct
    private void initCustomizedApis() {
        Set<ApiDefinition> definitions = new HashSet<>();
        ApiDefinition api1 = new ApiDefinition("product-api1")
                .setPredicateItems(
                        new HashSet<ApiPredicateItem>() {{
                                // 以/product-serv/product/api1 开头的请求
                                add(
                                        new ApiPathPredicateItem()
                                                .setPattern("/product-serv/product/api1/**")
                                                .setMatchStrategy(SentinelGatewayConstants.URL_MATCH_STRATEGY_PREFIX)
                                );
                        }}
                );
        ApiDefinition api2 = new ApiDefinition("product-api2")
                .setPredicateItems(
                        new HashSet<ApiPredicateItem>(){{
                                add( new ApiPathPredicateItem().setPattern("/product-serv/product/api2/demo1"));
                        }}
                );
        definitions.add(api1);
        definitions.add(api2);
        GatewayApiDefinitionManager.loadApiDefinitions(definitions);
    }

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

第五章 Gateway--服务网关 的相关文章

  • 方法返回类型前的 是什么意思?

    下面的方法返回一个List组成T类型元素 public
  • 连接外部 Accumulo 实例和 java

    我正在尝试使用 Accumulo 连接到虚拟机 问题是 我无法将其连接到 Java 中 我可以看到 Apache 抛出的网页 但我无法让它与代码一起工作 我认为这是缺乏知识的问题而不是真正的问题 但我找不到这方面的文档 所有示例都使用 lo
  • java程序有多少种结束方式?

    我知道使用 System exit 0 可以结束一个java程序 例如 如果我有一个JFrame窗口 它会关闭并结束程序 但我想知道还有多少其他方法 可以关闭它并结束程序 包括发生错误时 程序会被关闭 JFrame也会被关闭吗 添加到其他答
  • 查看Java Agent修改的Java类的源代码

    我需要了解 Java 代理如何修改我的初始类 以便我能够理解代码的作用 build gradle configurations jar archiveName agent2 jar jar manifest attributes Prema
  • 有人用过 ServiceLoader 和 Guice 一起使用吗?

    我一直想通过我们的应用程序 构建系统进行更大规模的尝试 但更高的优先级不断将其推到次要地位 这似乎是加载 Guice 模块的好方法 并且避免了关于 硬编码配置 的常见抱怨 单个配置属性很少会自行更改 但您几乎总是会有一组配置文件 通常用于不
  • 无法使用 datastax java 驱动程序通过 UDT 密钥从 cassandra 检索

    我正在尝试使用用户定义的类型作为分区键将对象存储在 cassandra 中 我正在使用 datastax java 驱动程序进行对象映射 虽然我能够插入到数据库中 但无法检索该对象 如果我更改分区键以使用非 udt 例如文本 我就能够保存和
  • java中如何知道一条sql语句是否执行了?

    我想知道这个删除语句是否真的删除了一些东西 下面的代码总是执行 else 是否删除了某些内容 执行此操作的正确方法是什么 public Deleter String pname String pword try PreparedStatem
  • 如何使用 Java Apache POI 隐藏 Excel 工作表中以下未使用的行?

    我正在使用数据库中的数据填充模板 Excel 工作表 for Map
  • JAXB - 忽略元素

    有什么方法可以忽略 Jaxb 解析中的元素吗 我有一个很大的 XML 文件 如果我可以忽略其中一个大而复杂的元素 那么它的解析速度可能会快很多 如果它根本无法验证元素内容并解析文档的其余部分 即使该元素不正确 那就更好了 例如 这应该只生成
  • 如何使用双重调度来分析图形基元的交集?

    我正在分析图形基元 矩形 直线 圆形等 的交互并计算重叠 相对方向 合并等 这被引用为双重调度的一个主要示例 例如维基百科 http en wikipedia org wiki Double dispatch 自适应碰撞算法通常要求 不同的
  • 类更改(例如字段添加或删除)是否保持 Serialized 的向后兼容性?

    我有一个关于 Java 序列化的问题 在这种情况下 您可能需要修改可序列化类并保持向后兼容性 我有丰富的 C 经验 所以请允许我将 Java 与 NET 进行比较 在我的Java场景中 我需要使用Java的运行时序列化机制序列化一个对象 并
  • 如何使用 Jersey 将嵌套列表封送为 JSON?我得到一个空数组或一个包含数组的单元素字典数组

    我正在开发一个使用 Jersey 将对象转换为 JSON 的项目 我希望能够写出嵌套列表 如下所示 data one two three a b c 我想要转换的对象首先将数据表示为 gt gt 我认为 Jersey 会做正确的事情 以上输
  • 文本视图不显示全文

    我正在使用 TableLayout 和 TableRow 创建一个简单的布局 其中包含两个 TextView 这是代码的一部分
  • Lombok 不适用于 Eclipse Neon

    我下载了lombok jar lombok 1 16 14 jar 并将其放入我的下载中 然后我点击这个 jar 执行正确地识别了我的 MacOS 上的 Eclipse 实例 然后我选择了我想要的实例 Lombok也在pom xml中指定
  • Spring Data Rest 多对多 POST

    首先 让我解释一下我的用例 这非常简单 有一个用户实体和一个服务实体 我使用 UserService 作为连接实体 连接表 在用户和服务之间建立多对多关联最初 会有一些用户集和一些服务集 用户可以在任何时间点订阅任何服务 在这种情况下 将向
  • 在 Java 中通过 D-Bus MPRIS 访问 Clementine 实例

    我使用 Clementine 作为音乐播放器 它可以通过 D Bus 命令进行控制 在命令行上 使用 qdbus 我可以 Start Stop 暂停播放器 强制它跳过播放列表中的歌曲 检查播放列表的长度 检查播放列表中当前播放的曲目及其元数
  • Android ScrollView,检查当前是否滚动

    有没有办法检查标准 ScrollView 当前是否正在滚动 方向是向上还是向下并不重要 我只需要检查它当前是否正在滚动 ScrollView当前形式不提供用于检测滚动事件的回调 有两种解决方法可用 1 Use a ListView并实施On
  • 什么是 Java2D 处理程序线程?

    我创建了一个使用 Hibernate 的示例 java 应用程序 当我进行线程转储时 我观察到一个名为 Java2D Disposer 的奇怪线程 有人能告诉我该线程的功能吗 AWT 系统中的某些实体需要最终确定以释放资源 最突出的例子是j
  • 如何让 Firebase 与 Java 后端配合使用

    首先 如果这个问题过于抽象或不适合本网站 我想表示歉意 我真的不知道还能去哪里问 目前我已经在 iOS 和 Android 上开发了应用程序 他们将所有状态保存在 Firebase 中 因此所有内容都会立即保存到 Firebase 实时数据
  • Spring 作为 JNDI 提供者?

    我想使用 Spring 作为 JNDI 提供程序 这意味着我想在 Spring 上下文中配置一个 bean 可以通过 JNDI 访问该 bean 这看起来像这样

随机推荐

  • C++拷贝构造函数的使用:创建类Dog 成员变量name age 。完成该类的定义,该类包含构造函数,拷贝构造函数 析构函数

    创建类Dog 成员变量name age 大家完成该类的定义 该类包含构造函数 拷贝构造函数 析构函数 拷贝构造函数的作用 拷贝构造函数作用就是利用该类的一个对象是初始化另一个该类的对象 换句话说就是 拷贝构造函数的作用 通过一个对象对该类的
  • 使用ADB命令来测试Android手机App的耗电量

    1 使用WiFi连接手机 先使用USB数据线连接手机和电脑 手机和电脑连接同一个WiFi 启动端口服务 adb tcpip 5555 5555为端口号 可以自由指定 打印 restarting in TCP mode port 5555 表
  • 如何用Stata完成(shui)一篇经济学论文(十一):分组和去重

    文章目录 分组 去重 不出意外的话 这应该是stata有关数据处理的最后一篇 emmm 其实我一开始只打算写数据处理部分的stata教程 因为我觉得对于我来说 数据处理才是最头疼的部分 不过关于后面回归 还是有些东西想跟大家分享一下 开始挖
  • Halcon直线检测

    1 Halcon最常用的直线检测算子 add metrology object line measure 利用Halcon封装好的模型不仅可以检测直线 还可以检测圆 椭圆 矩形等 下面介绍下其余的直线检测的算子 需要配合 skeleton
  • CentOS7安装postgresql

    目录 1 安装postgresql 2 postgresql基本使用 常用启停命令 常用配置文件 常用postgresql命令 官网地址 https www postgresql org 1 安装postgresql 1 1 进入官网 点击
  • 两点。。。等来金蝶中间件的面试通知

    晚上笔试 说是大约一点通知 等到十二点 困了 上床 睡不着啊 1点半翻起来 打开手机 没有 打开Gmail 没有 于是抽了支烟 等到两点多一点 手机响了 接到面试通知 下午一点 石头终于落地 因为上午还有一场网易游戏的笔试 担心冲突 还好
  • Linux编辑器——vim的使用

    文章目录 1 vim的三种模式 2 vim的基本操作 3 vim的配置 前言 Linux上的编辑器有很多 比如nano vi vim等 nano是最简单的编辑器 vim编辑器常用于写代码 因为vim的功能强大 写代码快捷方便 其可以主动的用
  • 16.1 C++智能指针-new/delete探秘

    16 1 C 智能指针 new delete探秘 16 2 C 智能指针 shared ptr 16 3 C 智能指针 weak ptr 16 4 C 智能指针 shared ptr使用场景 陷阱 性能分析与使用建议 16 5 C 智能指针
  • Queue队列简介说明

    转自 Queue队列简介说明 下文笔者讲述Queue队列的简介说明 如下所示 Queue队列简介 Queue也是Java集合框架中定义的一种接口 直接继承自 Collection 接口 除了基本的Collection接口规定测操作外 Que
  • 制作一个多语言谷歌翻译 脚本

    1 修改host文件 C Windows System32 drivers etc 加入以下配置 google translate 203 208 40 66 translate google com google translate ap
  • 成功的硬件公司不仅是产品,更是一种创新文化

    目录 内容简介 客户 建立公司 而不仅仅是产品 CSDN学院 作者简介 内容简介 很多时候 你决定去做一件事 一款产品 往往都始于一个想法 一个灵感 然后 许多硬件创业者会花费数月 甚至是数年的时间来确定并分析他们的想法或者说是创意 一旦产
  • Public Private Protect Inheritance and access specifiers

    In the previous lessons on inheritance we ve been making all of our data members public in order to simplify the example
  • 腾讯云函数的Python依赖库打包注意事项

    云函数Python3 6依赖库 官网文档链接 前两天写了一个Python程序要用pycryptodome库 但是腾讯云函数并没有自带这个库 感觉还是记录一下比较好 要使用CentOS 7下的Python Python版本要是3 6的 我一开
  • VIM选择文本块/复制/粘贴

    在正常模式下 按ESC进入 按键v进入可视化模式 然后按键盘左右键或h l键即可实现文本的选择 其它相关命令 v 按字符选择 经常使用的模式 所以亲自尝试一下它 V 按行选择 这在你想拷贝或者移动很多行的文本的时候特别有用 CTRL v 按
  • QT中修改QCalendarWidget样式时,令周几(表头)与日期文字颜色不同的方法,设置文字样式冲突的问题

    太长不看版 Qt日历中使用setWeekdayTextFormat修改周末文字颜色和setHeaderTextFormat修改表头文字颜色发生冲突 通过找到每个周末日期来修改文字颜色 效果图和代码见文末 在Qt中用到了QCalendarWi
  • Prometheus Blackbox Exporter 的 HTTP 探测指标中各个阶段的时间统计信息

    在 Prometheus Blackbox Exporter 的 HTTP 探测指标中 probe http duration seconds 指标包含各个阶段的时间统计信息 这些阶段代表了 HTTP 探测的不同阶段和指标 以下是各个阶段的
  • 同步时间

    系统时间与硬件时间 一台计算机有两个时钟 一是硬件时间时钟 RTC Real Time Clock 又实时时钟 二是系统时钟 System Clock 1 概念 硬件时钟是嵌在主板上的特殊电路 平时关机后还可计算时间 系统时钟是操作系统的k
  • 安装tensorflow version `CXXABI_1.3.7' not found

    现象 ImportError usr lib64 libstdc so 6 version CXXABI 1 3 7 not found required by root anaconda3 lib python3 6 site ntern
  • mybatis+oracle+map入参,并获取主键ID的返回值

    insert id insert parameterType map useGeneratedKeys true keyProperty id gt insert into person name pswd values name pswd
  • 第五章 Gateway--服务网关

    接上一篇文章开始网关之旅 首先告诉大家网关是什么 Gateway简介 怎么配置 怎么入门 执行流程等等相关介绍 第一章 微服务的架构介绍发展第二章 微服务环境搭建第三章 Nacos Discovery 服务治理第四章 Sentinel 服务