• SpringCloudAlibaba系列微服务搭建笔记六_Gateway


    九、服务网关-SpirngCloudGateway

    随着微服务多个模块的部署,每个服务所在的服务器可能不同,产生多个地址,为了方便维护这些地址,需要一个网关来统一服务地址,同时也可以通过网关统一认证鉴权

    9.1 SpringCloudGateway简介

    网关挡在众多微服务前面,做路由转发、监控、限流、鉴权等功能。
    SpringCloudGateway是基于WebFlux框架实现的,而WebFlux底层使用了Netty通信框架。
    SpringCloudGateway核心的概念是路由Predicate断言、Filter过滤器

    SpringCloudGateway需要使用SpringBoot2.0以上版本,并且不能再TomcatJettyServlet容器中运行,只能是jar包运行。

    9.2 集成SpringCloudGateway

    复制一个项目,修改后项目包含三个模块,一个网关,一个用户,一个商品服务。
    在这里插入图片描述

    1. 网关模块添加依赖
    <dependency>
    	<groupId>org.springframework.cloudgroupId>
    	<artifactId>spring-cloud-starter-gatewayartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    注意:不要引入spring-boot-starter-web依赖,否则会报错

    1. application.yml配置文件配置网关路由
    server:
      port: 8051
    
    spring:
      application:
        name: gateway-server
      cloud:
        gateway:
          routes:                         # 路由,可配置多个
            - id: module_one              # 路由id,唯一即可,默认是uuid
              uri: http://localhost:8041  # 真实服务地址
              order: 1                    # 路由优先级,数值越小优先级越高,默认为 0
              predicates:                 # 断言
                - Path=/moduleone/**     # 路径匹配
            - id: module_two
              uri: http://localhost:8031
              order: 1
              predicates:
                - Path=/moduletwo/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    注意predicates:- Path=/module8031/**Path是大写开头

    如此配置效果:地址以/moduletwo开头就路由到http://localhost:8031服务,
    /moduleone开头就路由到http://localhost:8041服务。

    其实就是把协议ip端口給替换掉,去访问真实的服务

    1. 在服务模块创建对应的服务接口
    @RestController
    @RequestMapping("/moduleone")
    public class TestController {
        @Value("${server.port}")
        private String port;
    
        @RequestMapping("/test")
        public String test() {
            return "return from module test,服务端口:" + port;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    @RestController
    @RequestMapping("/moduletwo")
    public class TestController {
        @Value("${server.port}")
        private String port;
    
        @RequestMapping("/test")
        public String test() {
            return "return from module test,服务端口:" + port;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    1. 访问测试接口
      在这里插入图片描述在这里插入图片描述
      可以看到,通过8051这个端口(网关服务),通过请求路径的不同成功路由到了两个不同的服务模块。

    9.3 网关整合Nacos

    现在网关是能够正常使用了,但是路由配置都写在配置文件上,如果后续要修改或新增的话,需要修改配置文件然后重启项目,这样非常不方便。于是整合Nacos非常有必要,这样gateway服务可以直接通过应用名称拉取到服务的地址。

    9.3.1 先把服务模块注册到Nacos

    怎么注册服务到nacos参考:https://blog.csdn.net/qq_31856061/article/details/126420140

    注意:应用名不能带_,原因在下面

    9.3.2 网关服务配置nacos

    1. 添加依赖
    		
            <dependency>
                <groupId>com.alibaba.cloudgroupId>
                <artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    1. 启动类贴上注解
      @EnableDiscoveryClient
    2. 增加bootstrap.yml配置文件
      application.yml里面的配置剪切到bootstrap.yml中,再修改一下
    server:
      port: 8051
    
    spring:
      application:
        name: gateway-server
      cloud:
        nacos:
          discovery:
            server-addr: 124.221.89.209:8848 # Nacos 注册中心地址
        gateway:
          discovery:
            locator:
              enabled: true # 让网关服务可以发现 nacos 中的服务,自动拉取数据
          routes:
            - id: moduleOne
              uri: lb://moduleOne  # 从注册中心拉取 moduleOne 的地址
              order: 1
              predicates:
                - Path=/moduleone/**
            - id: moduleTwo
              uri: lb://moduleTwo  # 从注册中心拉取 moduleTwo 的地址
              order: 1
              predicates:
                - Path=/moduletwo/**
    
    • 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

    gateway中配置uri有三种方式:

    1. 服务地址: http://localhost:8031
    2. websocket:ws://localhost:8031
    3. 注册中心:lb://服务应用名称

    注意: 用lb://服务应用名称这种方式,服务名不能有_,不然会识别不了报错
    如:Invalid host: lb://module_two
    gateway的正则指定服务名字符只能是a-zA-Z

    1. 测试接口
      都可以成功调通,说明可以成功从注册中心拉取服务地址完成路由功能。
      在这里插入图片描述

    在这里插入图片描述

    9.4 Predicate断言的实现

    Predicate断言就是一个判断逻辑,返回值为布尔类型,如果为真才会往下进行路由。

    9.4.1 内置断言工厂实现

    内置断言工厂是SpringCloudGateway自带的断言工厂,可以直接通过配置文件配置。

    1. AfterRoutePredicateFactory-设定日期参数,允许在指定日期时间之后的请求通过。
      缩略配置举例:
    - After=2022-04-15T14:48.421+08:00[Asia/Shanghai]
    
    • 1
    1. BeforeRoutePredicateFactory-设定日期参数,允许在指定日期之前的请求通过
      缩略配置举例:
    - Before=2022-04-15T14:48.421+08:00[Asia/Shanghai]
    
    • 1
    1. BetweenRoutePredicateFactory-设定时间区间,只允许日期在区间内的请求通过
      缩略配置举例:
    - Between=2022-04-15T14:48.421+08:00[Asia/Shanghai],2022-04-20T14:48.421+08:00[Asia/Shanghai]
    
    • 1
    1. CookieRoutePredicateFactory-设定cookie名称和cookie值(或正则表达式),判断请求是否含有对应的cookie且其值是否匹配,匹配才会通过
      缩略配置举例:
    - Cookie=mycookie,mycookievalue
    
    • 1
    1. HeaderRoutePredicateFactory-设定请求头名称和请求头值(或正则表达式),判断请求是否含有对应的请求头且其值是否匹配,匹配才会通过
      缩略配置举例:
    - Header=Phonenum,\d+
    
    • 1
    1. HostRoutePredicateFactory-设定主机名host,允许符合的请求通过。
      缩略配置举例:
    - Host=**.echoo.com
    
    • 1
    1. MethodRoutePredicateFactory-设定请求方式,允许匹配的请求通过。
      缩略配置举例:
    - Methode=GET
    
    • 1
    1. PathRoutePredicateFactory-设定路径规则,判断请求地址是否满足设定的路径规则,满足通过
      缩略配置举例:
    - Path=/user/*
    
    • 1
    1. QueryRoutePredicateFactory-设定参数名和参数值(或正则表达式),允许符合的请求通过。
      缩略配置举例:
    - Query=name,z.
    
    • 1
    1. RemoteAddrRoutePredicateFactory-设定IP地址段,判断请求主机地址是否能匹配上。
      缩略配置举例:
    - RemoteAddr=89.220.54.25/24
    
    • 1
    1. WeightAddrRoutePredicateFactory-设定权重分组名称和权重值,同一个分组名的路由根据权重值转发
      缩略配置举例:
    	routes:
            - id: moduleOne
              uri: lb://moduleOne  # 从注册中心拉取
              order: 1
              predicates:    
                - Path=/module/**       # 服务匹配路径是一样的
                - Weight=module_group,7 # 权重分组为 module_group,权重=7
            - id: moduleTwo
              uri: lb://moduleTwo  # 从注册中心拉取
              order: 1
              predicates:
                - Path=/module/**       # 服务匹配路径是一样的 
                - Weight=module_group,3 # 权重分组为 module_group,权重=3
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13

    像上面那样配置,断言的路径是一样的都是/module/**,通过同一个权重分组module_group,不同的权重值,使得70%的请求进入moduleOne服务,30%的请求进入moduleTwo服务

    9.4.2 自定义断言工厂实现

    除了内置的断言外,还可以通过代码实现自己的断言逻辑。

    1. 修改一下两个模块接口,添加一个id参数
        @RequestMapping("/test")
        public String test(@RequestParam Integer id) {
            return "return from moduleOne test,服务端口:" + port + ",id:" + id;
        }
    
    • 1
    • 2
    • 3
    • 4
    1. 创建自定义断言工厂
      需求:只允许id在指定范围内的请求通过
    import org.apache.commons.lang.ArrayUtils;
    import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
    import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.server.ServerWebExchange;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.function.Predicate;
    
    /** 自定义断言工厂 */
    @Component
    public class CustomRoutePredicateFactory extends AbstractRoutePredicateFactory<CustomRoutePredicateFactory.Config> {
    
        public static final String[] KEY_ARRAY = {"minId", "maxId"};
    
        /** 构造器 */
        public CustomRoutePredicateFactory() {
            super(CustomRoutePredicateFactory.Config.class);
        }
    
        /** 快捷配置字段 */
        @Override
        public List<String> shortcutFieldOrder() {
            return Arrays.asList(KEY_ARRAY);
        }
    
        /** 断言逻辑 */
        @Override
        public Predicate<ServerWebExchange> apply(final CustomRoutePredicateFactory.Config config) {
            return new GatewayPredicate() {
                public boolean test(ServerWebExchange serverWebExchange) {
                    String id = serverWebExchange.getRequest().getQueryParams().getFirst("id");
                    if (null != id) {
                        int idNum = Integer.parseInt(id);
                        return config.getMinId() < idNum && config.getMaxId() > idNum; // 允许通过
                    }
                    return false;
                }
    
                @Override
                public String toString() { // 重写 toString,不通过时输出自己指定的提示
                    return String.format("minId:%d,maxId:%d", config.getMinId(), config.getMaxId());
                }
            };
        }
    
        /** 配置类 */
        @Validated
        public static class Config {
            private Integer minId;
            private Integer maxId;
    
            public Integer getMinId() {
                return minId;
            }
    
            public void setMinId(Integer minId) {
                this.minId = minId;
            }
    
            public Integer getMaxId() {
                return maxId;
            }
    
            public void setMaxId(Integer maxId) {
                this.maxId = maxId;
            }
        }
    }
    
    • 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
    • 68
    • 69
    • 70
    • 71
    1. 配置里使用自定义断言工厂
        gateway:
          discovery:
            locator:
              enabled: true # 让 SpringCloudGateway 可以发现 nacos 中的服务
          routes:
            - id: moduleOne
              uri: lb://moduleOne  # 从注册中心拉取
              order: 1
              predicates:
                - Path=/moduleone/**
            - id: moduleTwo
              uri: lb://moduleTwo  # 从注册中心拉取
              order: 1
              predicates:
                - Path=/moduletwo/**
                - Custom=90,100   # 使用自定义断言,minId=90 maxId=100
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    1. 通过内置断言可以通过规律看到,断言工厂的名称都是功能名+RoutePredicateFactory,而在配置文件里配置断言是只要指定功能名就行,所以这里是- Customer
    1. 测试
      请求127.0.0.1:8051/moduletwo/test?id=95,参数在区间内,允许通过
      在这里插入图片描述
      请求127.0.0.1:8051/moduletwo/test?id=102,参数不在区间内,请求拒绝
      在这里插入图片描述

    9.5 过滤器

    SpringCloudGateway的过滤器可以在请求和响应之间加入一些自定义的逻辑。
    其中过滤器又分为全局过滤器和局部过滤器,局部过滤器只能作用于某一个路由上。
    过滤器和断言一样除却内置过滤器外,也支持用户自定义过滤器。

    9.5.1 内置局部过滤器

    SpringCloudGateway提供的内置局部过滤器

    1. AddRequestHeader
      设定Header及其值,为原始请求添加Header,缩略配置如下
    - AddRequestHeader=Token,5461519844
    
    • 1
    1. AddRequestParameter
      设定参数及其值,为原始请求添加参数,缩略配置如下
    - AddRequestParameter=name,mary
    
    • 1
    1. AddResponseHeader
      设定Header及其值,为响应添加Header,缩略配置如下
    - AddResponseHeader=Token,5461519844
    
    • 1
    1. DedupeResponseHeader
      设定去重的Header及其值,删除响应头中的重复值,缩略配置如下
    - DedupeResponseHeader=SAMPLE-NAME SAMPLE-AGE
    
    • 1

    注意:如果指定多个Header,用空格隔开。

    内置过滤器很多,这里只列出了以上几个。因为配置方法和使用都比较简单,所以需要直接去看文档使用即可。点击打开官方文档

    9.5.2 自定义局部过滤器

    与之前的自定义断言相似,过滤器是配置名+GatewayFilterFactory的固定配合。

    1. 自定义过滤器
      基于id参数范围做一个过滤
    import org.springframework.cloud.gateway.filter.GatewayFilter;
    import org.springframework.cloud.gateway.filter.GatewayFilterChain;
    import org.springframework.cloud.gateway.filter.factory.AbstractGatewayFilterFactory;
    import org.springframework.core.io.buffer.DataBuffer;
    import org.springframework.http.HttpStatus;
    import org.springframework.validation.annotation.Validated;
    import org.springframework.web.server.ServerWebExchange;
    import reactor.core.publisher.Flux;
    import reactor.core.publisher.Mono;
    
    import java.nio.charset.StandardCharsets;
    import java.util.Arrays;
    import java.util.List;
    
    public class CustomGatewayFilterFactory extends AbstractGatewayFilterFactory<CustomGatewayFilterFactory.Config> {
    
        public static final String[] KEY_ARRAY = {"minId", "maxId"};
    
        /** 构造器 */
        public CustomGatewayFilterFactory() {
            super(CustomGatewayFilterFactory.Config.class);
        }
    
        /** 快捷配置字段 */
        @Override
        public List<String> shortcutFieldOrder() {
            return Arrays.asList(KEY_ARRAY);
        }
    
        /** 过滤逻辑 */
        @Override
        public GatewayFilter apply(Config config) {
    
            return new GatewayFilter() {
                @Override
                public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                    String id = exchange.getRequest().getQueryParams().getFirst("id");
                    if (null != id) {
                        int idNum = Integer.parseInt(id);
                        if (config.getMinId() < idNum && config.getMaxId() > idNum) {
                            return chain.filter(exchange);// 允许通过
                        }
                    }
                    // 条件不满足
                    byte[] tips = ("拒绝访问id=" + id + "的数据").getBytes(StandardCharsets.UTF_8); // 准备提示信息,并转成字节数组
                    DataBuffer wrap = exchange.getResponse().bufferFactory().wrap(tips);           // 把提示字节数组放入响应的缓冲区
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_ACCEPTABLE); // 设置响应码
                    return exchange.getResponse().writeWith(Flux.just(wrap));        // 返回提示
                }
            };
        }
    
        /** 配置类 */
        public static class Config {
            private Integer minId;
            private Integer maxId;
    
            public Integer getMinId() { return minId; }
            public void setMinId(Integer minId) { this.minId = minId; }
            public Integer getMaxId() { return maxId; }
            public void setMaxId(Integer maxId) { this.maxId = maxId; }
        }
    }
    
    
    • 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
    1. 修改配置文件
    	...
        gateway:
          discovery:
            locator:
              enabled: true # 让 SpringCloudGateway 可以发现 nacos 中的服务
          routes:
            - id: moduleOne
              uri: lb://moduleOne  # 从注册中心拉取
              order: 1
              predicates:
                - Path=/moduleone/**
              filters:
                - Custom=10,20
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    1. 测试
      在这里插入图片描述
  • 相关阅读:
    零基础Linux_5(开发工具_上)yum和vim和gcc/g++和gdb
    【基础理论】柯西分布
    网站服务器怎么部署
    【推荐一款阿里开源的低代码工具,实用性极高!】
    基于PostGIS的mvt动态矢量切片的后台地图服务和前端调用
    灵魂拷问:Mybatis中 Dao接口和XML文件的SQL如何建立关联?
    kali渗透测试_HTTPS攻击
    FDTD script command(源/监视器)
    【C语言学习笔记---指针进阶02】
    细胞膜修饰多肽/多糖/红细胞膜/仿生细胞膜载蛋白/聚乙二醇修饰细胞膜的制备
  • 原文地址:https://blog.csdn.net/qq_31856061/article/details/127420696