SpringCloud系列——10Spring Cloud Gateway网关

导读:本篇文章讲解 SpringCloud系列——10Spring Cloud Gateway网关,希望对大家有帮助,欢迎收藏,转发!站点地址:www.bmabk.com

学习目标

  1. Gateway是什么?它有什么作用?

  2. Gateway中的断言使用

  3. Gateway中的过滤器使用

  4. Gateway中的路由使用

第1章 网关

1.1 网关的概念

        简单来说,网关就是一个网络连接到另外一个网络的“关口”。如下图所示,当我们所在的本地网络(局域网)要访问外部网络的数据时,需要通过路由器进行转发,而这里的路由器就充当了网关的角色。

SpringCloud系列——10Spring Cloud Gateway网关

1.2 网关的作用

        网关的作用,是可以实现不同网络之间的互联,同时,还可以使得在不同的通信协议、数据格式等系统之间实现转发。

        我们今天要讲解的Gateway,它也是网关的一种,我们称为应用网关,也称为API网关。为什么需要API网关呢? 还得从架构演变过程来说明:

        前面我们说过,在微服务架构中,每个微服务都是一个独立运行的组件,这些组件通过Rest API风格的接口给到H5、Android、IOS等客户端程序调用。(移动互联时代,为了尽快迭代)。而在一个UI界面中,通常会展示很多数据,这些数据可能来自不同的微服务,比如在一个电商系统中,执行一个下单请求,必然需要

  1. 从营销服务中查询促销信息

  2. 从会员服务中查询会员等级和会员积分等

  3. 从商品服务查询商品详情

  4. 调用订单服务进行下单。

1.3 出现的背景

        那么早期的微服务架构,面对这样的情况的处理方式,出现了如下图所示的调用方式。

SpringCloud系列——10Spring Cloud Gateway网关

在这种调用方式中,不难发现问题会比较多:

  1. 客户端和内部微服务的耦合度较高,任何一边发生变化,都会对另外一边造成影响

  2. 客户端需要维护内部微服务的地址等信息,如果微服务地址发生变化也会影响客户端的通信

  3. 客户端需要调用多次接口请求,造成服务端请求过多

        这种方式存在较多的问题,所以一般我们会在客户端与微服务之间引入BFF层(即 Backend For Frontend(服务于前端的后端)),也就是服务器设计API时会考虑前端的使用,并在服务端直接进行业务逻辑的处理,又称为用户体验适配器。

        如下图所示,BFF层为客户端提供了统一的聚合服务,我们可以在BFF层为不同的端或者不同的业务提供更加友好和统一的客户端。

SpringCloud系列——10Spring Cloud Gateway网关

 引入BFF层的好处是

  1. 客户端和内部微服务之间不存在之间耦合,使得两端的变化可以独立

  2. 当APP端有新的需求时,可以通过BFF层进行评比,减少后端团队的沟通成本,如果后端微服务设计得足够好的话,很多的需求在BFF层就可以搞定。

        但是这种方式仍然存在问题,客户端发起请求进入到BFF层时,需要考虑到安全问题,需要做鉴权、限流等,而这些功能需要在每一个BFF模块中都需要编写,增加了很多的重复代码,而且维护起来非常不灵活导致开发效率下降。

        所以,引入了API网关,整体结构如下图所示

SpringCloud系列——10Spring Cloud Gateway网关

        网关是微服务架构不可或缺的一部分,作为微服务架构的唯一入口,将所有请求转发到后端对应的微服务上去,同时又可以将各个微服务中的通用功能集中到网关去做,而不是在每个微服务都实现一遍,

  1. 身份验证和授权

  2. 限流

  3. 版本控制

  4. 缓存

  5. 统一日志

        同时,增加网关后,把各个BFF模块的横切功能剥离到网关中,BFF模块开发人员只需要关注在业务逻辑的交付上。

常见的开源网关

  1. OpenResty(Nginx+lua)

  2. Zuul,是spring cloud生态下提供的一个网关服务,性能相对来说不是很高

  3. Spring Cloud Gateway,是Spring团队开发的高性能网关

1.4 Gateway简介

        Spring Cloud Gateway 是 Spring 官方团队研发的 API 网关技术,它的目的是取代 Zuul 为微服务提供一种简单高效的 API 网关。一般来说,Spring 团队不会重复造轮子,为什么又研发出一个 Spring Cloud Gateway 呢?有几方面原因。

  • Zuul1.x 采用的是传统的 thread per connection 方式来处理请求,也就是针对每一个请求,会为这个请求专门分配一个线程来进行处理,直到这个请求完成之后才会释放线程,一旦后台服务器响应较慢,就会使得该线程被阻塞,所以它的性能不是很好。

  • Zuul 本身存在的一些性能问题不适合于高并发的场景,虽然后来 Netflix 决定开发高性能版 Zuul 2.x,但是 Zuul 2.x 的发布时间一直不确定。虽然 Zuul 2.x 后来已经发布并且开源了,但是 Spring Cloud 并没有打算集成进来。Spring Cloud Gateway 是依赖于 Spring Boot 2.0、Spring WebFlux 和 Project Reactor 等技术开发的网关,它不仅提供了统一的路由请求的方式,还基于过滤链的方式提供了网关最基本的功能。

1.5 Gateway基本概念

Spring Cloud Gateway的基本工作原理如下图所示。SpringCloud系列——10Spring Cloud Gateway网关

         客户端向Spring Cloud Gateway发出请求。然后在Gateway Handler Mapping中找到与请求相匹配的路由,将其发送到Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。

        过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

  • Filter在“pre”类型的过滤器可以做参数校验、权限校验、流量监控、日志输出、协议转换等,

  • “post”类型的过滤器中可以做响应内容、响应头的修改,日志的输出,流量监控等有着非常重要的作用。

在Spring Cloud Gateway中有三个重要的对象,分别是:

  • Route路由,它是网关的基础元素,包含ID、目标URI、断言、过滤器组成,当前请求到达网关时,会通过Gateway Handler Mapping,基于断言进行路由匹配,当断言为true时,匹配到路由进行转发

  • Predicate断言,学过java8的同学应该知道这个函数,它可以允许开发人员去匹配HTTP请求中的元素,一旦匹配为true,则表示匹配到合适的路由进行转发

  • Filter过滤器,可以在请求发出的前后进行一些业务上的处理,比如授权、埋点、限流等。

具体的工作原理如下图所示:SpringCloud系列——10Spring Cloud Gateway网关

它的整体工作原理如下。

        其中,predicate就是我们的匹配条件;而filter,就可以理解为一个无所不能的拦截器。有了这两个元素,再加上目标uri,就可以实现一个具体的路由了。

        客户端向 Spring Cloud Gateway 发出请求,如果请求与网关程序定义的路由匹配,则该请求就会被发送到网关 Web 处理程序,此时处理程序运行特定的请求过滤器链。

        过滤器之间用虚线分开的原因是过滤器可能会在发送代理请求的前后执行逻辑。所有 pre 过滤器逻辑先执行,然后执行代理请求;代理请求完成后,执行 post 过滤器逻辑。

第2章 Predicate应用

下面我们通过一些案例演示来初步了解Spring Cloug Gateway.

  1. 在上文的基础之上,整个项目拷贝过来,并改名为gateway-**

  2. 在上面的框架基础之上,修改user项目中的HelloController和UserController

    @RestController
    public class HelloController {
        @Autowired
        OrderServiceClient orderServiceClient;
    
        @Value("${server.port}")
        private int port;
    
        @GetMapping("/hello/{name}")
        public String get(@PathVariable("name") String name){
            String result = "";
            //同步
            result = new HelloCommand(name,orderServiceClient).execute();
    
            return "当前user端口为:"+port+",结果为:"+result;
        }
    }
    
    @RestController
    public class UserController {
        @Autowired
        OrderServiceClient orderServiceClient;
    
        @Value("${server.port}")
        private int port;
    
        @HystrixCommand(commandProperties = {
                @HystrixProperty(name="circuitBreaker.requestVolumeThreshold",value = "10"),
                @HystrixProperty(name="circuitBreaker.sleepWindowInMilliseconds",value="5000"),
                @HystrixProperty(name="circuitBreaker.errorThresholdPercentage",value="50"),
        },fallbackMethod = "fallback")
        @GetMapping("/get/{num}")
        public String get(@PathVariable("num") int num){
            if(num%2==0){
                return "当前user端口为:"+port+",结果为:正常访问";
            }
            return "当前user端口为:"+port+",结果为:"+orderServiceClient.orderLists(num);
        }
    
        public String fallback(int num){
            return "触发降级";
        }
    }
    

    并分别开启两个user项目,端口为8080和8081;开启两个order项目,端口分别为8088和8099

  3. 创建新的springboot项目gateway-common

  4. 配置pom

    <?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 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <parent>
            <artifactId>eclipse2019-demo</artifactId>
            <groupId>com.example</groupId>
            <version>1.0-SNAPSHOT</version>
        </parent>
        <groupId>com.example</groupId>
        <artifactId>gateway-common</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>gateway-common</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-gateway</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
            </dependency>
        </dependencies>
    
    </project>
  5. 配置yml,如果用的是nacos,一般这些配置可以写在nacos里面

    server:
      port: 9527
    
    spring:
      application:
        name: gateway
      cloud:
        gateway:
          routes:
            # 路由的ID,没有固定规则但要求唯一,建议配合服务名
            - id: getUser
              # 匹配后提供服务的路由地址
              uri: http://localhost:8080
              # 断言,路径相匹配的进行路由
              predicates:
                - Path=/get/**
            - id: sayHello
              uri: http://localhost:8081
              predicates:
                - Path=/hello/**
            - id: heihei
              uri: https://www.baidu.com/
              predicates:
                - Path=/heihei/**
              filters:
                - StripPrefix=1 #去掉地址中的第一部分
                - StripPrefix=2 #去掉地址中的第二部分
    
    eureka:
      instance:
        hostname: gateway-9527
      client:
        service-url:
          register-with-eureka: true
          fetch-registry: true
          defaultZone: http://127.0.0.1:8761/eureka
  6. 启动类

    @SpringBootApplication
    @EnableDiscoveryClient
    public class GatewayCommonApplication {
    
        public static void main(String[] args) {
            SpringApplication.run(GatewayCommonApplication.class, args);
        }
    
    }
  7. 测试

    因为是通过gateway网关走的请求,所以,hell的请求都会走8081端口,get请求都会走8080端口SpringCloud系列——10Spring Cloud Gateway网关

    SpringCloud系列——10Spring Cloud Gateway网关

2.1 Predicate规则

上述案例中,我们使用了Gateway中的 Path匹配规则,也就是根据请求的uri地址,使用前缀匹配规则完成请求地址的匹配。

Gateway内置了是多种Predicate匹配规则,具体如下图所示

SpringCloud系列——10Spring Cloud Gateway网关

2.1.1 Query断言

Query 路由断言工厂接收两个参数,一个必需的参数和一个可选的正则表达式。

spring:
  cloud:
    gateway:
      routes:
        - id: query_route
          uri: https://www.baidu.com/
          predicates:
            - Query=name, eclipse2019.*shuai

        如果请求包含一个name的参数,值是eclipse2019开头,shuaiqi结尾,则此路由将匹配。第二个参数是正则表达式。s

        测试链接:http://localhost:9527/?name=feichangshuaiqieclipse2019http://localhost:9527/?name=feichangshuaiqi

2.1.2 Method断言

Method 路由断言工厂接收一个参数,即要匹配的 HTTP 方法。

spring:
  cloud:
    gateway:
      routes:
        - id: method_route
          uri: https://www.douyu.com/
          predicates:
            - Method=GET   

2.1.3 Header断言

Header 路由断言工厂接收两个参数,分别是请求头名称和正则表达式。

spring:
  cloud:
    gateway:
      routes:
        - id: header_route
          uri: https://www.douyin.com/
          predicates:
            - Header=X-Request-Id, \d+

如果请求中带有请求头名为 X-Request-Id,其值与 \d+ 正则表达式匹配(值为一个或多个数字),则此路由匹配。

2.1.4 Cookie断言

spring:
  cloud:
    gateway:
      routes:
        - id: cookie_route
          uri: https://www.huya.com/
          predicates:
            - Cookie=name,eclipse2019

通过postman,访问http://localhost:9527 ; 并且在请求中携带cookie name=eclipse2019。 即可匹配到路由进行转发。

2.2 自定义Predicate

        除了使用到官方提供的断言工厂之外,如果我们有个性化的需求,也是可以实现自定义断言工厂的。自定义路由断言工厂需要继承 AbstractRoutePredicateFactory 类,重写 apply 方法的逻辑。在 apply 方法中可以通过 exchange.getRequest() 拿到 ServerHttpRequest 对象,从而可以获取到请求的参数、请求方式、请求头等信息。

        apply 方法的参数是自定义的配置类,在使用的时候配置参数,在 apply 方法中直接获取使用。

命名需要以 RoutePredicateFactory 结尾,比如 AuthRoutePredicateFactory,那么在使用的时候Auth 就是这个路由断言工厂的名称。代码如下所示。

  1. 自定义AuthRoutePredicateFactory

    @Component
    public class AuthRoutePredicateFactory extends AbstractRoutePredicateFactory<AuthRoutePredicateFactory.Config>{
    
        Logger logger= LoggerFactory.getLogger(AuthRoutePredicateFactory.class);
        public static final String NAME_KEY = "name";
    
        public AuthRoutePredicateFactory() {
            super(Config.class);
        }
    
        @Override
        public List<String> shortcutFieldOrder() {
            return Arrays.asList(NAME_KEY);
        }
    
        @Override
        public Predicate<ServerWebExchange> apply(Config config) {
            logger.info("AuthRoutePredicateFactory Start");
            //只要请求的header中包含yml配置的Authorization,就允许匹配路由
            return exchange -> {
                HttpHeaders headers=exchange.getRequest().getHeaders();
                List<String> header=headers.get(config.getName());
                return header.size()>0;
            };
        }
    
        public static class Config{
            private String name;
    
            public String getName() {
                return name;
            }
            public void setName(String name) {
                this.name = name;
            }
        }
    }
  2. 在配置文件中添加如下配置信息

    - id: define_route
      uri: https://www.baidu.com
      predicates:
        - Path=/define/**
        - Auth=Authorization
      filters:
        - StripPrefix=1
  3. 访问测试

    postman中访问url:http://localhost:9527/define/1

    测试: 在header中添加Authorization 以及不添加 Authorization的效果。

SpringCloud系列——10Spring Cloud Gateway网关

第3章 Filter应用

        filter是网关中的核心,它起到了请求过滤的作用。在gateway中,会对请求做pre和post的过滤,pre表示请求进来之前,post表示请求处理完成之后返回给客户端之前。

  • pred类型的过滤器可以做授权认证、流量监控、协议转化等工作

  • post过滤器可以做响应内容的修改、日志的输出等。

        下图表示的是请求和响应,经过filter的处理流程。SpringCloud系列——10Spring Cloud Gateway网关

3.1 Filter分类

在Spring Cloud Gateway中,Filter按照作用范围可以分为两类

  1. 全局过滤器,针对所有的请求都会被拦截

  2. 局部过滤器,只针对某一个指定的route有效

我们先来了解一下局部过滤器,在前面讲Predicate的时候,其实已经涉及到了Filter的使用。

在Spring Cloud Gateway中,内置了非常多的过滤器,如下图所示SpringCloud系列——10Spring Cloud Gateway网关

3.2 常用Filter

3.2.1 AddRequestParameter

针对所有匹配的请求,添加一个查询参数。

        下面这段配置,会针对所有请求增加一个tn=baiduimage&word=%E7%BE%8E%E5%A5%B3的参数

spring:
  cloud:
    gateway:
      routes:
        - id: add_request_parameter_route
          uri: https://image.baidu.com/
          predicates:
            - Path=/search/index/**
          filters:
            - AddRequestParameter=tn,baiduimage
            - AddRequestParameter=word,%E7%BE%8E%E5%A5%B3

3.2.2 RequestRateLimiter

        该过滤器会对访问到当前网关的所有请求执行限流过滤,如果被限流,默认情况下会响应HTTP 429-Too Many Requests。RequestRateLimiterGatewayFilterFactory 默认提供了 RedisRateLimiter 的限流实现,它采用令牌桶算法来实现限流功能

spring:
  cloud:
    gateway:
      routes:
        - id: request_ratelimiter_route
          uri: https://www.taobao.com/
          predicates:
            - Path=/tb/**
          filters:
            - StripPrefix=1
            - name: RequestRateLimiter
              args:
                redis-rate-limiter.replenishRate: 2
                redis-rate-limiter.burstCapacity: 5
                key-resolver: "#{@userkeyResolver}" #这个必须要配置,否则返回403

redis-rate-limiter 过滤器有两个配置属性,如果大家了解令牌桶,就很容易知道它们的含义。

  • replenishRate:令牌桶中令牌的填充速度,代表允许每秒执行的请求数。

  • burstCapacity:令牌桶的容量,也就是令牌桶最多能够容纳的令牌数。表示每秒用户最大能够执行的请求数量。

  • key-resolver:关键字标识的限流

使用redis限流的话还要做一些事:

  1. pom中引包

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
    </dependency>
  2. 设置redis的地址

    spring:
      redis:
        database: 1
        password: eclipse2019
        host: localhost
  3. 在启动类或者配置类中加如下代码

    @Bean
    KeyResolver userkeyResolver(){
        //根据请求的ip进行限流
        return exchange -> Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }

3.2.3 Retry

        Retry GatewayFilter Factory 为请求重试过滤器,当后端服务不可用时,网关会根据配置参数来发起重试请求

spring:
    cloud:
        gateway:
            routes:
            - id: retry_route
            uri: http://www.example.com
            predicates:
            - Path=/example/**
            filters:
            - name: Retry
                args:
                    retries: 3
                    status: 503
            - StripPrefix=1

RetryGatewayFilter 提供 4 个参数来控制重试请求,参数说明如下。

  • retries:请求重试次数,默认值是 3。

  • status:HTTP 请求返回的状态码,针对指定状态码进行重试,比如,在上述配置中,当服务端返回的状态码是 503 时,才会发起重试,此处可以配置多个状态码。

  • methods:指定 HTTP 请求中哪些方法类型需要进行重试,默认是 GET。

  • series:配置错误码段,表示符合某段状态码才发起重试,默认值是 SERVER_ERROR(5),表示 5xx 段的状态码都会发起重试。如果 series 配置了错误码段,但是 status 没有配置,则仍然会匹配 series 进行重试。

3.2.4 全局过滤器

        全局过滤器和GatewayFilter的作用是相同的,只是GlobalFilter针对所有的路由配置生效。Spring Cloud Gateway默认内置了一些全局过滤器

  • GatewayMetricsFilter,提供监控指标。

  • ReactiveLoadBalancerClientFilter,整合 Ribbon 针对下游服务实现负载均衡。

  • ForwardRoutingFilter,用于本地 forward,请求不转发到下游服务器。

  • NettyRoutingFilter,使用 Netty 的 HttpClient 转发 HTTP、HTTPS 请求

  • ….

        全局过滤链的执行顺序是,当 Gateway 接收到请求时,Filtering Web Handler 处理器会将所有的 GlobalFilter 实例及所有路由上所配置的 GatewayFilter 实例添加到一条过滤器链中。该过滤器链里的所有过滤器都会按照@Order 注解所指定的数字大小进行排序。

3.2.5 自定义过滤器

        虽然Spring Cloud Gateway提供了非常多的过滤器,但是在实际应用中,我们必然会涉及到和业务有关的过滤器,比如日志记录、鉴权、黑白名单等。Spring Cloud Gateway 提供了过滤器的扩展功能,开发者可以根据实际业务需求来自定义过滤器,这样我们就可以在网关层实现时鉴权、日志管理、协议转化等功能。同样,自定义过滤器也支持 GlobalFilter 和 GatewayFilter 两种。

3.2.5.1 自定义GatewayFilter

        首先创建一个自定义过滤器类MyDefineGatewayFilterFactory,继承AbstractGatewayFilterFactory。

@Component
public class MyDefineGatewayFilterFactory extends AbstractGatewayFilterFactory<MyDefineGatewayFilterFactory.MyConfig>{
    Logger logger= LoggerFactory.getLogger(MyDefineGatewayFilterFactory.class);
    public static final String NAME_KEY = "name";

    public MyDefineGatewayFilterFactory() {
        super(MyConfig.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList(NAME_KEY);
    }

    @Override
    public GatewayFilter apply(MyConfig config) {
        return ((exchange, chain) -> {
            logger.info("[Pre] Filter Request,name:"+config.getName());

//then接收一个变量,然后then前面处理的那个就结束了,后面开始处理then接收的这个变量
            return chain.filter(exchange).then(Mono.fromRunnable(()->{
                logger.info("[Post] Response Filter");
            }));
        });
    }

    public static class MyConfig{
        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}

在上述代码中,有几点需要注意:

  • 类名必须要统一以GatewayFilterFactory结尾,因为默认情况下过滤器的 name 会采用该自定义类的前缀。这里的 name=MyDefine。

  • apply方法中,同时包含PrePost过滤。在then方法中是请求执行结束之后的后置处理。

  • MyConfig 是一个配置类,该类中只有一个属性 name。这个属性可以在 yml 文件中使用。

  • 该类需要装载到 Spring IoC 容器,此处使用@Component注解实现。

接下来,修改application.yml,增加自定义过滤器配置

spring:
  cloud:
   gateway:
    routes:
      - id: define_route
      uri: http://localhost:8080
      predicates:
        - Path=/define/**
	  filters: 
	    - MyDefine=My_Eclipse2019

此时访问到这个过滤器,就会输出如下日志,说明进入到了网关拦截器。  

2020-06-02 22:08:21.838  INFO 164 --- [ioEventLoop-5-2] c.e.s.MyDefineGatewayFilterFactory       : [Pre] Filter Request,name:My_Eclipse2019
2020-06-02 22:08:21.875  INFO 164 --- [ctor-http-nio-5] c.e.s.MyDefineGatewayFilterFactory       : [Post] Response Filter

3.2.5.2 自定义GlobalFilter

        GlobalFilter 的实现比较简单,它不需要额外的配置,只需要实现 GlobalFilter 接口,自动会过滤所有的 Route

@Service
public class MyDefineFilter implements GlobalFilter,Ordered{
    Logger log= LoggerFactory.getLogger(MyDefineFilter.class);
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        log.info("[pre]-Enter MyDefineFilter");
        return chain.filter(exchange).then(Mono.fromRunnable(()->{
        	log.info("[post]-Return Result");
        }));
    }
    @Override
    public int getOrder() {
    	return 0;
    }
}

        getOrder 表示该过滤器的执行顺序,值越小,执行优先级越高。需要注意的是,我们通过 AbstractGatewayFilterFactory 实现的局部过滤器没有指定 order,它的默认值是 0,如果想要设置多个过滤器的执行顺序,可以重写 getOrder 方法。

第4章 路由

4.1 基于集群负载均衡路由

        当被路由的目标服务是一个集群节点时,就会涉及到集群路由,Spring Cloud Gateway提供了一个LoadBalancerClientFilter全局过滤器,来实现负载均衡的解析。

  1. 增加jar包依赖

    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
    </dependency>
  2. user项目也要注册到Eureka上面去

  3. 修改application.yml配置

    spring:
      application:
        name: gateway
      redis:
    	database: 1
        password: eclipse2019
        host: localhost
      cloud:
        gateway:
          routes:
            - id: getUser
              uri: lb://user  # 修改这里
              predicates:
                - Path=/get/**
          discovery: # 修改这里
            locator:
              enabled: true
              lower-case-service-id: true
    
    server:
      port: 9527
    eureka: # 修改这
      instance:
        hostname: gateway-9527
      client:
        service-url:
          register-with-eureka: true
          fetch-registry: true
          defaultZone: http://127.0.0.1:8761/eureka

增加部分的配置说明如下

  • lower-case-service-id:是否使用 service-id 的小写,默认是大写。

  • spring.cloud.gateway.discovery.locator.enabled:开启从注册中心动态创建路由的功能。

  • uri 中配置的lb://表示从注册中心获取服务,后面的user表示目标服务在注册中心上的服务名

重启gateway-common项目,访问:http://localhost/get/3接口。

4.2 动态路由的实现

        在实际应用中, 我们还会存在一种:动态配置路由的需求。也就是在运行过程中,动态增加或者修改网关路由配置,这个需求在Spring Cloud Gateway中如何实现呢?

        在Spring Cloud Gateway中,提供了GatewayControllerEndpoint这个类来实现路由的动态修改,可以通过actuator打开这些endpoint信息

  1. 添加Pom依赖

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-actuator</artifactId>
    </dependency>
  2. 修改application.yml,开发所有endpoint

    management:
      endpoints:
        web:
          exposure:
            include: *

4.2.1 检索网关中定义的路由

        通过这个地址:http://localhost:9527/actuator/gateway/routes可以获得当前网关中所有定义的路由

[
    {
        predicate: "Paths: [/get/**], match trailing slash: true",
        route_id: "getUser",
        filters: [ ],
        uri: "lb://user",
        order: 0,
    }
]

其中:

  • route_id 表示路由编号

  • route_object.predicate 表示路由的条件匹配谓词

  • route_object.filters 表示网关过滤器

  • order 路由顺序

4.2.2 查找特定的路由信息。

http://localhost:9527/actuator/gateway/routes/{route_id}

4.2.3 刷新路由缓存

{POST请求}http://localhost:9527/actuator/gateway/refresh

4.2.4 增减、修改路由

  • /gateway/routes/{id} @PostMapping 新增一个路由信息

  • /gateway/routes/{id} @DeleteMapping 删除一个路由信息

  1. 案例演示(添加路由)

    • 通过POST请求添加一个路由信息,http://localhost:9527/actuator/gateway/routes/baidu_route

      {
          "uri": "https://www.baidu.com",
          "predicates": [{
              "args": {
                  "pattern": "/baidu/**"
              },
              "name": "Path"   
          }],
          "filters": [{
              "args": {
                  "_genkey_0": 1
              },
              "name": "StripPrefix"
          }]
      }
    • 执行:{POST请求}http://localhost:9527/actuator/gateway/refresh刷新路由。

    • 通过访问:http://localhost:9527/actuator/gateway/routes 查看当前路由列表,可以发现多了一个段这样的内容。

      {
          predicate: "Paths: [/baidu/**], match trailing slash: true",
          route_id: "baidu_route",
          filters: [
              "[[StripPrefix parts = 1], order = 1]"
          ],
          uri: "https://www.baidu.com:443",
          order: 0,
      }
    • 此时我们访问: http://localhost:9527/baidu ,就会路由到百度搜索引擎这个网址。

  2. 案例演示(删除路由)

    • 通过/gateway/routes/{id} @DeleteMapping 删除一个路由信息

      通过postman调用 /gateway/routes/baidu_route (delete请求), 就可以删除路由,删除路由之后再次访问路由列表页面,此时可以返现路由信息是被删除的。

4.2.5 小结

        基于Spring Cloud Gateway默认方法实现的动态路由就讲解完了,但是通过这种形式是去更新的动态路由信息,是基于内存来实现的。一旦服务重启,新增的路由配置信息就会全部清空,所以这个时候我们可以参考GatewayControllerEndpoint这个类,来自己实现一套动态路由的方法。并且将路由信息持久化。

        在实际开发中也可以通过Nacos作为配置中心直接在Nacos上面增加。

下文预告

  1. Gateway核心源码分析

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/76716.html

(0)
小半的头像小半

相关推荐

极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!