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


    1、前言

    在我们开发微服务系统时,Gateway是必不可少的一个组件,我们可以通过Gateway来转发用户进行的请求,这样就可以隐藏具体系统的信息。
    在微服务系统开发中,常常是以团队的方式进行开发的,所以就需要在Gateway中进行路由的配置,Gateway为开发者提供了三种路由配置的方式:

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

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

    2、前置知识

    我们知道Gateway有两个重要的组件:PredicatesFilters

    2.1、Predicates

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

    2.1.1、Path

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

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:  
          - id: path_route  
            uri: http://127.0.0.1:8080  
            predicates:  
            - Path=/path/**,/path-server/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON格式:

    {  
        "id": "path_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "Path",  
                "args": {   
                    "_genkey_0": "/path/**",  
                    "_genkey_1": "/path-server/**"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    2.1.2、Method

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

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:  
          - id: path_route  
            uri: http://127.0.0.1:8082  
            predicates:  
            - Method=GET,POST
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON格式:

    {  
        "id": "path_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "Method",  
                "args": {   
                    "_genkey_0": "GET",  
                    "_genkey_1": "POST"
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.1.3、After

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

    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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON格式:

    {  
        "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]"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

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

    2.1.4、Before

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

    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]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    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]"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.1.5、Between

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

    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]
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    JSON格式:

    {  
        "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]"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    2.1.6、Cookie

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

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:  
          - id: path_route
            uri: https://127.0.0.1:8080
            predicates:  
            - Cookie=session, fly
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    JSON格式:

    {  
        "id": "path_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "Cookie",  
                "args": {  
                  	"name": "session",  
                  	"regexp": "fly"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    regexp表示正则的意思。

    2.1.7、Header

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

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:  
          - id: path_route
            uri: https://example.org  
            predicates:  
            - Header=X-Request-Id, \d+
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON格式:

    {  
        "id": "path_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "Header",  
                "args": {  
                    "header": "X-Request-Id",  
                    "regexp": "\\d+"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.1.8、Host

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

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:  
          - id: path_route
            uri: http://127.0.0.1:8082  
            predicates:  
            - Host=localhost:8080,localhost:8081
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON格式:

    {  
        "id": "path_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "Host",  
                "args": {  
                    "_genkey_0": "localhost:8080",
                    "_genkey_0": "localhost:8081"
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.1.9、Query

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

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:
          - id: path_route
            uri: http://127.0.0.1:8082
            predicates:  
            - Query=name, fly
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

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

    JSON格式:

    {  
        "id": "path_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "Query",  
                "args": {   
                    "param": "name",  
                    "regexp": "fly"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    2.1.10、RemoteAddr

    匹配指定来源的请求。

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:  
          - id: path_route
            uri: http://127.0.0.1:8082
            predicates:  
            - RemoteAddr=192.168.0.1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON格式:

    {  
        "id": "remoteaddr_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "RemoteAddr",  
                "args": {   
                    "_genkey_0": "192.168.0.1"  
                }  
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    2.1.11、Weight

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

    YAML格式:

    spring:  
      cloud:  
        gateway:  
          routes:  
          - id: path_route
            uri: http://127.0.0.1:8082
            predicates:  
            - Weight=group1, 8
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    JSON格式:

    {  
        "id": "path_route",  
        "uri": "http://127.0.0.1:8082",  
        "predicates":[  
            {  
                "name": "Weight",  
                "args": {   
                    "weight.group": "group1",
                    "weight.weight": "8"
                }
            }  
        ]  
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    2.2、Filters

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

    2.2.1、AddRequestHeader

    添加请求头信息。

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- AddRequestHeader = X-Request-Foo,Bar
    
    • 1
    • 2
    • 3
    • 4
    • 5

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

    JSON格式:

    {
    	"name":"AddRequestHeader",
    	"args":{
    		"_genkey_0":"X-Request-Foo",
    		"_genkey_1":"Bar"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2.2、RewritePath

    路径重写。

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- RewritePath = /path/(?>.*), /$\{segment}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSON格式:

    {
    	"name":"RewritePath",
    	"args":{
    		"_genkey_0":"/foo/(?.*)",
    		"_genkey_1":"/$\\{segment}"
    		}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2.3、AddRequestParameter

    添加请求参数。

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- AddRequestParameter = foo,bar
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSON格式:

    {
    	"name":"AddRequestParameter",
    	"args":{
    		"_genkey_0":"foo",
    		"_genkey_1":"bar"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2.4、AddResponseHeader

    添加响应参数。

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- AddResponseHeader = X-Request-Foo,Bar
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSON格式:

    {
    	"name":"AddResponseHeader",
    	"args":{
    		"_genkey_0":"X-Request-Foo",
    		"_genkey_1":"Bar"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2.5、PrefixPath

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

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- PrefixPath = /mypath
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSON格式:

    {
    	"name":"PrefixPath",
    	"args":{
    		"_genkey_0":"/mypath"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2.6、StripPrefix

    路径前缀删除。

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- StripPrefix = 2
    
    • 1
    • 2
    • 3
    • 4
    • 5

    删除前面两个前缀。

    JSON格式:

    {
    	"name":"StripPrefix",
    	"args":{
    		"_genkey_0":"2"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2.7、RedirectTo

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

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- RedirectTo = 302,https://www.baidu.com
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSON格式:

    {
    	"name":"RedirectTo",
    	"args":{
    		"_genkey_0":"302",
    		"_genkey_1":"https://www.baidu.com"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    2.2.8、RemoveRequestHeader

    删除请求头属性。

    YAML 格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- RemoveRequestHeader = X-Request-Foo
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSON格式:

    {
    	"name":"RemoveRequestHeader",
    	"args":{
    		"_genkey_0":"X-Request-Foo"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    2.2.9、RemoveResponseHeader

    删除响应头属性。

    YAML格式:

    spring:
      cloud:
    	gateway:
    	  filters:
    		- RemoveResponseHeader = X-Request-Foo
    
    • 1
    • 2
    • 3
    • 4
    • 5

    JSON格式:

    {
    	"name":"RemoveResponseHeader",
    	"args":{
    		"_genkey_0":"X-Request-Foo"
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6

    3、动态路由的实现

    动态路由的原理:将路由信息存放在某个中间层,然后在项目启动后对这个文件进行加载和监听,这个中间层可以是数据库或者文件,重要的是能在这个文件被修改后监听到这个文件的变化并重新加载。
    这里的中间层使用的是nacos的配置心中,Gateway整合了Naocs配置中心后,可以很好的监听指定的配置文件。

    3.1、Gateway中的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}
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

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

    配置如下:

    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
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    3.1、路由文件

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

    [
        {
            "id":  "feign-test",
            "uri": "http://localhost:8082",
            "predicates":[{
                "name": "Path",
                "args":{
                    "pattern": "/feign/**"
                }
            }],
            "filters":[{
                "name": "StripPrefix",
                "args":{
                    "value1": 1
                }
            }]
        },
        ## 下面还可以写其他的路由配置。
    ]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    3.2、实现ApplicationEventPublisherAware接口

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

    @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));
            }
        }
    
    
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43
    • 44
    • 45
    • 46
    • 47
    • 48
    • 49
    • 50
    • 51
    • 52
    • 53
    • 54
    • 55
    • 56
    • 57
    • 58
    • 59
    • 60
    • 61
    • 62
    • 63
    • 64
    • 65
    • 66
    • 67
  • 相关阅读:
    EDU实战-SQL注入漏洞
    NOIP 2012 普及组初赛 第28题 排列数
    【LLM】大模型幻觉问题的原因和缓解方法
    跑步运动蓝牙耳机哪个好?跑步无线运动蓝牙耳机推荐
    生成树STP中的概念
    学习python第7天
    羡慕 Excel 的高级选择与文本框颜色呈现?Pandas 也可以拥有!! ⛵
    深度学习 RNN循环神经网络原理与Pytorch正余弦值预测
    人脸识别4G执法记录仪、一体化智能AI布控球在智慧社区、智能网格中的应用
    Verilog 线型wire 种类
  • 原文地址:https://blog.csdn.net/qq_45515182/article/details/133205012