spring-cloud-gateway

Posted on 2020-11-01 21:28  FLGB  阅读(680)  评论(0编辑  收藏  举报

Spring 官方最终还是按捺不住推出了自己的网关组件:Spring Cloud Gateway ,相比之前我们使用的 Zuul(1.x) 它有哪些优势呢?Zuul(1.x) 基于 Servlet,使用阻塞 API,它不支持任何长连接,如 WebSockets,Spring Cloud Gateway 使用非阻塞 API,支持 WebSockets,支持限流等新特性。

Spring Cloud Gateway

Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等技术开发的网关,它旨在为微服务架构提供一种简单有效的统一的 API 路由管理方式。

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

相关概念:

  • Route(路由):这是网关的基本构建块。它由一个 ID,一个目标 URI,一组断言和一组过滤器定义。如果断言为真,则路由匹配。
  • Predicate(断言):这是一个 Java 8 的 Predicate。输入类型是一个 ServerWebExchange。我们可以使用它来匹配来自 HTTP 请求的任何内容,例如 headers 或参数。
  • Filter(过滤器):这是org.springframework.cloud.gateway.filter.GatewayFilter的实例,我们可以使用它修改请求和响应。

工作流程:

客户端向 Spring Cloud Gateway 发出请求。如果 Gateway Handler Mapping 中找到与请求相匹配的路由,将其发送到 Gateway Web Handler。Handler 再通过指定的过滤器链来将请求发送到我们实际的服务执行业务逻辑,然后返回。 过滤器之间用虚线分开是因为过滤器可能会在发送代理请求之前(“pre”)或之后(“post”)执行业务逻辑。

Spring Cloud Gateway 的特征:

  • 基于 Spring Framework 5,Project Reactor 和 Spring Boot 2.0
  • 动态路由
  • Predicates 和 Filters 作用于特定路由
  • 集成 Hystrix 断路器
  • 集成 Spring Cloud DiscoveryClient
  • 易于编写的 Predicates 和 Filters
  • 限流
  • 路径重写

一、新建spring-cloud-gateway服务工程

 1.选中spring-cloud-gate包的支持,pom.xml

<?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>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>lf.liyouyou</groupId>
    <artifactId>spring-cloud-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-gateway</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

  2.配置yml中路由规则

  

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes:
     
      - id: route_configclient  # 请求 http://localhost:9005/gateway/hello转发到 http://localhost:9004/hello
        uri: http://localhost:9004
        predicates:
        - Path=/gateway/*
        filters:
        - StripPrefix=1  #前缀, 在当前路径匹配中表示去掉第一个前缀 /gateway
server:
  port: 9005

3.启动项目并测试,发现可以跳转并返回值

 二、配置Cookie路由

修改yml配置,新增Cookie路由规则

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes:
     
      - id: route_configclient  # 请求 http://localhost:9005/gateway/hello转发到 http://localhost:9004/hello
        uri: http://localhost:9004
        predicates:
        - Path=/gateway/*
        filters:
        - StripPrefix=1  #前缀, 在当前路径匹配中表示去掉第一个前缀 /gateway

      - id: cookie_route  # 请求 http://localhost:9005/gateway/hello转发到 http://localhost:9004/hello
        predicates:
        - Cookie=lf,lf.liyouyou
        uri: https://home.cnblogs.com/u/flgb/
server:
  port: 9005

 启动,用postman 进行测试,直接访问http://localhost:9005/  返回404

添加cookie,配置key=value

 再次发起请求,注意默认是get请求,返回了页面信息,断言匹配成功

 

查看请求中的cookie ,对比yml配置

 三、自定义Header路由规则

  1. 可以参照CookieRoutePredicateFactory自定义路由一个AuthRoutePredicateFactory用来判断header头的路由,其中RoutePredicateFactory是固定后缀

  AuthRoutePredicateFactory类代码如下:

package lf.liyouyou.Config;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.CookieRoutePredicateFactory;
import org.springframework.http.HttpHeaders;
import org.springframework.stereotype.Component;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.server.ServerWebExchange;

import javax.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;

@Component
public class AuthRoutePredicateFactory extends AbstractRoutePredicateFactory<AuthRoutePredicateFactory.Config> {

    public static final String NAME_KEY = "name";

    public AuthRoutePredicateFactory() {
        super(Config.class);
    }

    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        //自定义断言规则,判断header中是否包含某个值
        return (exchange->{
            HttpHeaders headers = exchange.getRequest().getHeaders();
            List<String> list = headers.get(config.getName());
            return list.size()>0;
        });
    }

    @Override
    public List<String> shortcutFieldOrder() {
        //自定义属性顺序,配置的断言时进行顺序匹配,
        // 这里就定义一个属性值,则第一个值就是config.name的值
        return Arrays.asList(NAME_KEY);
    }

    @Validated
    public static class Config {
        @NotEmpty
        private String name;

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

        public Config() {
        }

        public String getName() {
            return this.name;
        }


    }
}

 2. 配置application.yml路由策略

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes:
      - id: route_configclient  # 请求 http://localhost:9005/gateway/hello转发到 http://localhost:9004/hello
        uri: http://localhost:9004
        predicates:
        - Path=/gateway/*
        filters:
        - StripPrefix=1  #前缀, 在当前路径匹配中表示去掉第一个前缀 /gateway

      - id: cookie_route  # 请求 http://localhost:9005/gateway/hello转发到 http://localhost:9004/hello
        predicates:
        - Cookie=lf,lf.liyouyou
        uri: https://home.cnblogs.com/u/flgb/

      - id: header_route
        predicates:
        - Path=/header/**
        - Auth=Authorization
        uri: https://home.cnblogs.com/u/flgb/
        filters:
        - StripPrefix=1  #前缀, 在当前路径匹配中表示去掉第一个前缀 /header
server:
  port: 9005

 3. 启动工程,可以看到控制台输出:

 

       证明路由配置生效

 4. postman测试

   直接访问

  

 

 

 

  配置上header参数值,再访问

 四、自定义RouteFilter

  Configuration类

  

package lf.liyouyou.Config;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilter;
import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Mono;

import javax.validation.constraints.NotEmpty;
import java.util.Arrays;
import java.util.List;

//order过滤器优先级
@Component
public class LFGatewayFilterFactory extends AbstractGatewayFilterFactory<LFGatewayFilterFactory.LFConfig> {

    public static final String NAME_KEY = "name";

    Logger logger = LoggerFactory.getLogger(LFGatewayFilterFactory.class);

    public LFGatewayFilterFactory() {
        super(LFConfig.class);
    }

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

    @Override
    public GatewayFilter apply(LFConfig config) {
        //Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain);
        //Fiter pre/post
        return ((exchange,chain)->{
            //TODO
            logger.info("pre fiter request,name: {}",config.getName());
            return chain.filter(exchange).then(
                    Mono.fromRunnable(()->{
                        //TODO
                        logger.info("post :response filter!");
                    })
            );
        });
    }

    public static class LFConfig {
        @NotEmpty
        private String name;

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

        public LFConfig() {
        }

        public String getName() {
            return this.name;
        }


    }
}

yml配置filter,新增标黄地方

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      routes:
      - id: route_configclient1  #\u8BF7\u6C42 http://localhost:9005/gateway/hellolf\u4F1A\u8F6C\u53D1\u5230http://localhost:9003/hello
        uri: http://localhost:9003  #\u5728\u670D\u52A1\u6CE8\u518C\u4E2D\u5FC3\u627E\u670D\u52A1\u540D\u4E3A data-producer\u7684\u670D\u52A1
        predicates:
        - Path=/gateway/**  #\u8BBE\u7F6E\u8DEF\u7531\u65AD\u8A00,\u4EE3\u7406servicerId\u4E3Adata-service1\u7684/ data-service1 /\u8DEF\u5F84
        filters:
        - StripPrefix=1
        - LF= hello lf

重新启动,访问http://localhost:9005/gateway/hello,查看控制台日志,发现已经生效

 

五、GlobalFIlter实现负载均衡

1、spring-cloud-gateway添加注册中心的依赖

  pom.xml

<?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>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>lf.liyouyou</groupId>
    <artifactId>spring-cloud-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.yml新增标黄配置,开启注册中心支持,配置负载均衡路由

spring:
  application:
    name: gateway-service
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
      - id: route_configclient1  #\u8BF7\u6C42 http://localhost:9005/gateway/hellolf\u4F1A\u8F6C\u53D1\u5230http://localhost:9003/hello
        uri: http://localhost:9003  #\u5728\u670D\u52A1\u6CE8\u518C\u4E2D\u5FC3\u627E\u670D\u52A1\u540D\u4E3A data-producer\u7684\u670D\u52A1
        predicates:
        - Path=/gateway/**  #\u8BBE\u7F6E\u8DEF\u7531\u65AD\u8A00,\u4EE3\u7406servicerId\u4E3Adata-service1\u7684/ data-service1 /\u8DEF\u5F84
        filters:
        - StripPrefix=1
        - LF= hello lf
      - id: route_configclient  # \u8BF7\u6C42 http://localhost:9005/gateway/hello\u8F6C\u53D1\u5230 http://localhost:9004/hello
        uri: http://localhost:9004
        predicates:
        - Path=/gateway/*
        filters:
        - StripPrefix=1  #\u524D\u7F00\uFF0C \u5728\u5F53\u524D\u8DEF\u5F84\u5339\u914D\u4E2D\u8868\u793A\u53BB\u6389\u7B2C\u4E00\u4E2A\u524D\u7F00 /gateway

      - id: cookie_route  # \u8BF7\u6C42 http://localhost:9005/gateway/hello\u8F6C\u53D1\u5230 http://localhost:9004/hello
        predicates:
        - Cookie=lf,lf.liyouyou
        uri: https://home.cnblogs.com/u/flgb/

      - id: header_route
        predicates:
        - Path=/header/**
        - Auth=Authorization
        uri: https://home.cnblogs.com/u/flgb/
        filters:
        - StripPrefix=1  #\u524D\u7F00\uFF0C \u5728\u5F53\u524D\u8DEF\u5F84\u5339\u914D\u4E2D\u8868\u793A\u53BB\u6389\u7B2C\u4E00\u4E2A\u524D\u7F00 /header

      - id: lb_route
        predicates:
        - Path=/lb/**
        filters:
        - StripPrefix=1
        uri: lb://spring-cloud-config-client

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8000/eureka/
server:
  port: 9005

 

3、取消spring-cloud-config-client服务只能启动单个实例的限制

  4、修改/hellolf返回值

package lf.liyouyou.controller;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class HelloController {

    @RequestMapping("/hellolf")
    public String Hellolf(){
        return "hello lf 9999";
    }
}

 

 

 

 5、修改端口为9999,再次启动,看到注册中心上有两个spring-cloud-config-client服务

 

 

 6、测试结果如下

 

 

 

 

 

六、基于redis限流器的实现

1、新增依赖pom.xml

  

<?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>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>lf.liyouyou</groupId>
    <artifactId>spring-cloud-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
            <version>2.0.5.RELEASE</version>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

2.新增keyResolver代码

package lf.liyouyou.Config;

import org.springframework.cloud.gateway.filter.ratelimit.KeyResolver;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

@Component
public class IpAddressKeyResolver implements KeyResolver {

    @Override
    public Mono<String> resolve(ServerWebExchange exchange) {
        return Mono.just(exchange.getRequest().getRemoteAddress().getAddress().getHostAddress());
    }
}

 

3、新增yml配置

spring:
  application:
    name: gateway-service
  redis:
    host: 192.168.31.151
    port: 6379
    password:

  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
      routes:
      - id: route_configclient1  #\u8BF7\u6C42 http://localhost:9005/gateway/hellolf\u4F1A\u8F6C\u53D1\u5230http://localhost:9003/hello
        uri: http://localhost:9003  #\u5728\u670D\u52A1\u6CE8\u518C\u4E2D\u5FC3\u627E\u670D\u52A1\u540D\u4E3A data-producer\u7684\u670D\u52A1
        predicates:
        - Path=/gateway/**  #\u8BBE\u7F6E\u8DEF\u7531\u65AD\u8A00,\u4EE3\u7406servicerId\u4E3Adata-service1\u7684/ data-service1 /\u8DEF\u5F84
        filters:
        - StripPrefix=1
        - LF= hello lf
      - id: route_configclient  # \u8BF7\u6C42 http://localhost:9005/gateway/hello\u8F6C\u53D1\u5230 http://localhost:9004/hello
        uri: http://localhost:9004
        predicates:
        - Path=/gateway/*
        filters:
        - StripPrefix=1  #\u524D\u7F00\uFF0C \u5728\u5F53\u524D\u8DEF\u5F84\u5339\u914D\u4E2D\u8868\u793A\u53BB\u6389\u7B2C\u4E00\u4E2A\u524D\u7F00 /gateway

      - id: cookie_route  # \u8BF7\u6C42 http://localhost:9005/gateway/hello\u8F6C\u53D1\u5230 http://localhost:9004/hello
        predicates:
        - Cookie=lf,lf.liyouyou
        uri: https://home.cnblogs.com/u/flgb/

      - id: header_route
        predicates:
        - Path=/header/**
        - Auth=Authorization
        uri: https://home.cnblogs.com/u/flgb/
        filters:
        - StripPrefix=1  #\u524D\u7F00\uFF0C \u5728\u5F53\u524D\u8DEF\u5F84\u5339\u914D\u4E2D\u8868\u793A\u53BB\u6389\u7B2C\u4E00\u4E2A\u524D\u7F00 /header

      - id: lb_route
        predicates:
        - Path=/lb/**
        filters:
        - StripPrefix=1
        uri: lb://spring-cloud-config-client

      - id: ratelimiter_route
        predicates:
        - Path=/ratelimiter/**
        filters:
        - StripPrefix=1
        - name: RequestRateLimiter
          args:
            deny-empty-key: true
            key-resolver: "#{@ipAddressKeyResolver}" #默认类名称首字母小写
            redis-rate-limite  r.replenishRate: 1
            redis-rate-limiter.burstCapacity: 2
        uri: lb://spring-cloud-config-client

eureka:
  client:
    service-url:
      defaultZone: http://localhost:8000/eureka/
server:
  port: 9005

4.重新启动spring-cloud-gateway测试,重复刷新 http://localhost:9005/ratelimiter/hellolf,出现:429代表被限流

 

 

六、GateWay动态路由的实现

  gateway官网

     https://docs.spring.io/spring-cloud-gateway/docs/2.2.5.RELEASE/reference/html/#creating-and-deleting-a-particular-route 官网预留了actuator接口,

  可以动态创建、删除路由、路由刷新机制,不过默认是存放在内存中的,重启服务后新创建的、删除的都会消失。将其改造存储在redis中支持gateway的动态路由

  可以参考docker篇安装redis镜像。https://i.cnblogs.com/posts?cateId=1625602

  1.spring-cloud-gateway导入包,pom.xml

  

<?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>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.4.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>
    <groupId>lf.liyouyou</groupId>
    <artifactId>spring-cloud-gateway</artifactId>
    <version>0.0.1-SNAPSHOT</version>
    <name>spring-cloud-gateway</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-cloud.version>Hoxton.SR8</spring-cloud.version>
    </properties>

    <dependencies>
        <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>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
            <exclusions>
                <exclusion>
                    <groupId>org.junit.vintage</groupId>
                    <artifactId>junit-vintage-engine</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!--<dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
            <version>2.0.5.RELEASE</version>
        </dependency>-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <!-- https://mvnrepository.com/artifact/com.alibaba/fastjson -->
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.72</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-actuator</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>${spring-cloud.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

编写自定义存储类:

package lf.liyouyou.Config;

import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.gateway.route.RouteDefinition;
import org.springframework.cloud.gateway.route.RouteDefinitionRepository;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;
import reactor.core.publisher.Flux;
import reactor.core.publisher.Mono;

import java.util.ArrayList;
import java.util.HashMap;

@Component
public class RedisRouteDefinitionRepository implements RouteDefinitionRepository {

    public static final String GATEWAY_ROUTE_KEY = "gateway_route_key";

    @Autowired
    RedisTemplate redisTemplate;

    @Override
    public Flux<RouteDefinition> getRouteDefinitions() {
        ArrayList<RouteDefinition> routeDefinitions = new ArrayList<>();
        redisTemplate.opsForHash().values(GATEWAY_ROUTE_KEY).stream()
                .forEach(route->{
            routeDefinitions.add(JSON.parseObject(route.toString(),RouteDefinition.class));
        });
        return Flux.fromIterable(routeDefinitions);
    }

    @Override
    public Mono<Void> save(Mono<RouteDefinition> route) {
        return route.flatMap(routeDefinition -> {
            redisTemplate.opsForHash().put(GATEWAY_ROUTE_KEY,routeDefinition.getId(), JSON.toJSONString(routeDefinition));
            return Mono.empty();
        });
    }

    @Override
    public Mono<Void> delete(Mono<String> routeId) {
        return routeId.flatMap(id->{
            if(redisTemplate.opsForHash().hasKey(GATEWAY_ROUTE_KEY,id)){
                redisTemplate.opsForHash().delete(GATEWAY_ROUTE_KEY,id);
                return Mono.empty();
            }
            return Mono.defer(()->Mono.error(new RuntimeException("not found the RouteDefinition Exception!")));
        });
    }
}

yml添加redis和actuator配置

spring:
  application:
    name: gateway-service
  redis:
    host: 192.168.31.151
    port: 6379
    password:
management:
  endpoints:
    web:
      exposure:
        include: "*"

启动:post测试,官网资料添加路由截图如下:

 postman测试返回,1成功

报文如下:

"filters": [{
      "args":{
          "_genkey_0":1
      },
      "name": "StripPrefix"
  }],
  "uri": "https://www.baidu.com",
  "order": 0
}

 访问http://localhost:9005/actuator/gateway/routes 查看最新路由信息(基于事件机制刷新),也可以调用(http://localhost:9005/actuator/gateway/routes/refresh)进行刷新,查询baidu_route发现已存在,访问http://localhost:9005/baidu 发现已经可以跳转到百度

 

查看redis保存路由的信息:

docker exec -it 启动的redis容器名称 redis-cli 进入redis client端

 

 可以看到redis中已经保存了gateway_route_key的相关信息。

 

我们重新启动spring-cloud-gateway,再次访问http://localhost:9005/baidu 发现还是跳转到了百度。证明动态路由永久生效。

 

Copyright © 2024 FLGB
Powered by .NET 8.0 on Kubernetes