Gateway网关动态路由配置(YAML格式 + JSON格式)

1、前言

在我们开发微服务系统时,Gateway是必不可少的一个组件,我们可以通过Gateway来转发用户进行的请求,这样就可以隐藏具体系统的信息。

在微服务系统开发中,常常是以团队的方式进行开发的,所以就需要在Gateway中进行路由的配置,Gateway为开发者提供了三种路由配置的方式:

  1. 自定义RouteLocator 对象,通过硬编码的方式实现路由配置。
  2. 通过YAML文件配置routes属性。
  3. 实现ApplicationEventPublisherAware接口结合Nacos,监听Naocs中的路由文件,完成动态路由配置。

其中第二种和第三种都可以结合Nacos实现动态路由,主要看开发者怎么选择。第二种就是结合Nacos的配置中心,将routes参数下的数据放入到nacos中,我这里使用第三种进行展示,用JSON的格式配置路由信息。

2、前置知识

我们知道Gateway有两个重要的组件:Predicates、Filters。

2.1、Predicates

Predicates的主要功能是进行请求的拦截和筛选。当一个请求来到Gateway时,Predicates会根据配置的规则对请求进行评估,如果请求满足某种条件,那么这个请求就会被分发到相应的路由。

2.1.1、Path

Path表示请求当前服务的路径的匹配格式。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route  
        uri: http://127.0.0.1:8080  
        predicates:  
        - Path=/path/**,/path-server/**

JSON格式:

json 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Path",  
            "args": {   
                "_genkey_0": "/path/**",  
                "_genkey_1": "/path-server/**"  
            }  
        }  
    ]  
}

_genkey_0、_genkey_1可以自定义,没有限制,表示的是Path这个参数下的值。

2.1.2、Method

进入当前服务的请求方式。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route  
        uri: http://127.0.0.1:8082  
        predicates:  
        - Method=GET,POST

JSON格式:

json 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Method",  
            "args": {   
                "_genkey_0": "GET",  
                "_genkey_1": "POST"
        }  
    ]  
}

2.1.3、After

路由在指定时间之后生效。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route
        uri: http://127.0.0.1:8082  
        predicates:
        - After=2021-08-16T07:36:00.000+08:00[Asia/Shanghai]

JSON格式:

yaml 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "After",  
            "args": {  
                "datetime": "2023-09-23T07:36:00.000+08:00[Asia/Shanghai]"  
            }  
        }  
    ]  
}

有关时间的参数,只能为datetime,此处可在源码AfterRouterPredicateFactory类中找到

2.1.4、Before

路由在指定时间之前有效。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route  
        uri: http://127.0.0.1:8082  
        predicates:  
        - Before=2023-09-23T07:36:00.000+08:00[Asia/Shanghai]

JSON格式:

json 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Before",  
            "args": {  
                "datetime": "2023-09-23T07:36:00.000+08:00[Asia/Shanghai]"  
            }  
        }  
    ]  
}

2.1.5、Between

路由在指定时间之间有效。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
        - id: path_route
          uri: http://127.0.0.1:8082  
          predicates:  
            - Between=2023-09-23T07:36:00.000+08:00[Asia/Shanghai], 2023-09-23T08:15:00.000+08:00[Asia/Shanghai]

JSON格式:

yaml 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Between",  
            "args": {  
                "datetime1": "2023-09-23T07:36:00.000+08:00[Asia/Shanghai]",  
                "datetime2": "2023-09-23T08:18:00.000+08:00[Asia/Shanghai]"  
            }  
        }  
    ]  
}

args对应值的key只能为datetime1和datetime2,此处可在源码BetweenRouterPredicateFactory类中找到。

2.1.6、Cookie

表示cookie中存在指定名称,并且对应的值符合指定正则表达式,才算匹配成功

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route
        uri: https://127.0.0.1:8080
        predicates:  
        - Cookie=session, fly

session是cookie的名称,fly是cookie的值。

JSON格式:

json 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Cookie",  
            "args": {  
              	"name": "session",  
              	"regexp": "fly"  
            }  
        }  
    ]  
}

regexp表示正则的意思。

2.1.7、Header

表示header中存在指定名称,并且对应的值符合指定正则表达式,才算匹配成功。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route
        uri: https://example.org  
        predicates:  
        - Header=X-Request-Id, \d+

JSON格式:

yaml 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Header",  
            "args": {  
                "header": "X-Request-Id",  
                "regexp": "\\d+"  
            }  
        }  
    ]  
}

2.1.8、Host

表示请求的host要和指定的字符串匹配,并且对应的值符合指定正则表达式,才算匹配成功。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route
        uri: http://127.0.0.1:8082  
        predicates:  
        - Host=localhost:8080,localhost:8081

JSON格式:

yaml 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Host",  
            "args": {  
                "_genkey_0": "localhost:8080",
                "_genkey_0": "localhost:8081"
            }  
        }  
    ]  
}

2.1.9、Query

在请求中要带有指定的参数,且该参数的值需等于配置的值或匹配正则表达式,才算匹配成功。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:
      - id: path_route
        uri: http://127.0.0.1:8082
        predicates:  
        - Query=name, fly

有一个叫做name的参数,值为fly。

JSON格式:

json 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Query",  
            "args": {   
                "param": "name",  
                "regexp": "fly"  
            }  
        }  
    ]  
}

2.1.10、RemoteAddr

匹配指定来源的请求。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route
        uri: http://127.0.0.1:8082
        predicates:  
        - RemoteAddr=192.168.0.1

JSON格式:

json 复制代码
{  
    "id": "remoteaddr_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "RemoteAddr",  
            "args": {   
                "_genkey_0": "192.168.0.1"  
            }  
        }  
    ]  
}

2.1.11、Weight

按照权重将请求分发到不同的位置。

YAML格式:

yaml 复制代码
spring:  
  cloud:  
    gateway:  
      routes:  
      - id: path_route
        uri: http://127.0.0.1:8082
        predicates:  
        - Weight=group1, 8

JSON格式:

yaml 复制代码
{  
    "id": "path_route",  
    "uri": "http://127.0.0.1:8082",  
    "predicates":[  
        {  
            "name": "Weight",  
            "args": {   
                "weight.group": "group1",
                "weight.weight": "8"
            }
        }  
    ]  
}

权重这个一般不用咯,因为在微服务中一般有loadblance负载均衡器在,所以请求的分发一般由它来负责。

2.2、Filters

与Predicates相比,Filters的功能更加全面。它不仅可以在请求到达目标之前进行拦截,还可以对响应进行修改和修饰。具体来说,Filters可以用于修改响应报文,增加或修改Header或Cookie,甚至可以修改响应的主体内容。这些功能是Predicates所不具备的。

2.2.1、AddRequestHeader

添加请求头信息。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- AddRequestHeader = X-Request-Foo,Bar

添加一个名为X-Request-Foo的请求头参数,值为Bar。

JSON格式:

json 复制代码
{
	"name":"AddRequestHeader",
	"args":{
		"_genkey_0":"X-Request-Foo",
		"_genkey_1":"Bar"
	}
}

2.2.2、RewritePath

路径重写。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- RewritePath = /path/(?<segment>.*), /$\{segment}

JSON格式:

json 复制代码
{
	"name":"RewritePath",
	"args":{
		"_genkey_0":"/foo/(?<segment>.*)",
		"_genkey_1":"/$\\{segment}"
		}
}

2.2.3、AddRequestParameter

添加请求参数。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- AddRequestParameter = foo,bar

JSON格式:

json 复制代码
{
	"name":"AddRequestParameter",
	"args":{
		"_genkey_0":"foo",
		"_genkey_1":"bar"
	}
}

2.2.4、AddResponseHeader

添加响应参数。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- AddResponseHeader = X-Request-Foo,Bar

JSON格式:

json 复制代码
{
	"name":"AddResponseHeader",
	"args":{
		"_genkey_0":"X-Request-Foo",
		"_genkey_1":"Bar"
	}
}

2.2.5、PrefixPath

路径前缀增强(添加路径)。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- PrefixPath = /mypath

JSON格式:

json 复制代码
{
	"name":"PrefixPath",
	"args":{
		"_genkey_0":"/mypath"
	}
}

2.2.6、StripPrefix

路径前缀删除。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- StripPrefix = 2

删除前面两个前缀。

JSON格式:

json 复制代码
{
	"name":"StripPrefix",
	"args":{
		"_genkey_0":"2"
	}
}

2.2.7、RedirectTo

重定向。要指定响应码和重定向路径。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- RedirectTo = 302,https://www.baidu.com

JSON格式:

json 复制代码
{
	"name":"RedirectTo",
	"args":{
		"_genkey_0":"302",
		"_genkey_1":"https://www.baidu.com"
	}
}

2.2.8、RemoveRequestHeader

删除请求头属性。

YAML 格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- RemoveRequestHeader = X-Request-Foo

JSON格式:

json 复制代码
{
	"name":"RemoveRequestHeader",
	"args":{
		"_genkey_0":"X-Request-Foo"
	}
}

2.2.9、RemoveResponseHeader

删除响应头属性。

YAML格式:

yaml 复制代码
spring:
  cloud:
	gateway:
	  filters:
		- RemoveResponseHeader = X-Request-Foo

JSON格式:

json 复制代码
{
	"name":"RemoveResponseHeader",
	"args":{
		"_genkey_0":"X-Request-Foo"
	}
}

3、动态路由的实现

动态路由的原理:将路由信息存放在某个中间层,然后在项目启动后对这个文件进行加载和监听,这个中间层可以是数据库或者文件,重要的是能在这个文件被修改后监听到这个文件的变化并重新加载。

这里的中间层使用的是nacos的配置心中,Gateway整合了Naocs配置中心后,可以很好的监听指定的配置文件。

3.1、Gateway中的YAML文件

yaml 复制代码
server:
  port: 8000
nacos_server: 127.0.0.1:8848
spring:
  application:
    name: gateway-server
  cloud:
    nacos:
      discovery:
        server-addr: ${nacos_server}
      config:
        file-extension: yaml
        server-addr: ${nacos_server}

因为在Gateway中整合了Nacos配置中心,所以默认会监听gateway-server.yaml或者gateway-server这两个文件。在项目启动的时候可以看到的。故此,我就想gateway的基本配置放在配置中心中了。

配置如下:

yaml 复制代码
spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
          lower-case-service-id: true
    sentinel:
      transport:
        dashboard: http://localhost:8080
      datasource:
        flow-control:
          nacos:
            server-addr: ${nacos_server}
            data-id: gateway_flux-control_config.json
            rule-type: gw_flow
        degrade-control:
          nacos:
            data-id: gateway_degrade-control_config.json
            server-addr: ${nacos_server}
            rule-type: degrade
      eager: true

## 自定义的配置文件信息
dynamic-routes:
  nacos-addr: ${nacos_server}
  data-id: gateway-dynamic-routes.json
  group: DEFAULT_GROUP

3.1、路由文件

我们需要结合前置知识来编写路由规则,并将文件存放在Nacos配置中心中。

yaml 复制代码
[
    {
        "id":  "feign-test",
        "uri": "http://localhost:8082",
        "predicates":[{
            "name": "Path",
            "args":{
                "pattern": "/feign/**"
            }
        }],
        "filters":[{
            "name": "StripPrefix",
            "args":{
                "value1": 1
            }
        }]
    },
    ## 下面还可以写其他的路由配置。
]

3.2、实现ApplicationEventPublisherAware接口

我们可以使用ApplicationEventPublisherAware的实现类来监听路由文件的变化并重新加载。

java 复制代码
@Component
public class NacosDynamicRouteService implements ApplicationEventPublisherAware, ApplicationRunner {

    @Value("${dynamic-routes.data-id}")
    private String dataId;
    @Value("${dynamic-routes.group}")
    private String group;
    @Value("${dynamic-routes.nacos-addr}")
    private String serverAddr;
    
    private final RouteDefinitionWriter routeDefinitionWriter;
    private ApplicationEventPublisher applicationEventPublisher;
    private static final List<String> ROUTE_LIST = new ArrayList<>();


    public NacosDynamicRouteService(RouteDefinitionWriter routeDefinitionWriter){
        this.routeDefinitionWriter = routeDefinitionWriter;
    }

    @Override
    public void setApplicationEventPublisher(ApplicationEventPublisher applicationEventPublisher) {
        this.applicationEventPublisher = applicationEventPublisher;
    }

    @Override
    public void run(ApplicationArguments args) {
        try {
        // 获取nacosService对象
            ConfigService configService = NacosFactory.createConfigService(serverAddr);
            // 获取指定的路由配置文件
            String config = configService.getConfig(dataId, group, 5000);
            // 添加对路由文件的监听器
            configService.addListener(dataId, group, new NacosDynamicRouteListener(config));
        } catch (NacosException e) {
            e.printStackTrace();
        }
    }

// 清除路由信息
    private void clearRoute() {
        for (String id : ROUTE_LIST) {
            this.routeDefinitionWriter.delete(Mono.just(id)).subscribe();
        }
        ROUTE_LIST.clear();
    }

	// 路由文件监听器
    private class NacosDynamicRouteListener extends AbstractListener{

        public NacosDynamicRouteListener(String config){
            receiveConfigInfo(config);
        }

        @Override
        public void receiveConfigInfo(String s) { // S就是路由文件
            clearRoute();
            List<RouteDefinition> gatewayRouteDefinitions = JSONObject.parseArray(s, RouteDefinition.class);
            for (RouteDefinition routeDefinition : gatewayRouteDefinitions) {
                routeDefinitionWriter.save(Mono.just(routeDefinition)).subscribe();
                ROUTE_LIST.add(routeDefinition.getId());
            }
            applicationEventPublisher.publishEvent(new RefreshRoutesEvent(routeDefinitionWriter));
        }
    }


}
相关推荐
用户908324602731 小时前
Spring AI 1.1.2 + Neo4j:用知识图谱增强 RAG 检索(上篇:图谱构建)
java·spring boot
回家路上绕了弯18 小时前
深入解析Agent Subagent架构:原理、协同逻辑与实战落地指南
分布式·后端
用户83071968408221 小时前
Spring Boot 集成 RabbitMQ :8 个最佳实践,杜绝消息丢失与队列阻塞
spring boot·后端·rabbitmq
Java水解21 小时前
Spring Boot 视图层与模板引擎
spring boot·后端
Java水解1 天前
一文搞懂 Spring Boot 默认数据库连接池 HikariCP
spring boot·后端
洋洋技术笔记1 天前
Spring Boot Web MVC配置详解
spring boot·后端
初次攀爬者2 天前
Kafka 基础介绍
spring boot·kafka·消息队列
用户8307196840822 天前
spring ai alibaba + nacos +mcp 实现mcp服务负载均衡调用实战
spring boot·spring·mcp
Java水解2 天前
SpringBoot3全栈开发实战:从入门到精通的完整指南
spring boot·后端
初次攀爬者3 天前
RocketMQ在Spring Boot上的基础使用
java·spring boot·rocketmq