• SpringCloud案例day05


    SpringCloud Gateway网关

    案例1:环境搭建

    可以复制 Service-A9001 改成Gateway-C9009
    》1:创建工程导入依赖

    <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-gatewayartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    注意

    SpringCloud Gateway使用的web框架为webflux,和SpringMVC不兼容,需要从工程的pom.xml中删除 web框架,在需要web框架的模块中添加
    
    • 1

    》2:配置启动类

    package com.dev1;
    
    import org.springframework.boot.SpringApplication;
    import org.springframework.boot.autoconfigure.SpringBootApplication;
    
    @SpringBootApplication
    public class GatewayC9009 {
        public static void main(String[] args) {
            SpringApplication.run(GatewayC9009.class,args);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    》3:编写配置文件

    server:
      port: 9009
    spring:
      application:
        name: Gateway-C9009
      cloud:
        gateway:
          routes:
            - id: route1
              uri: http://127.0.0.1:9001
              predicates:
                - Path=/person/**
    #        - id: route2
    #          uri: http://127.0.0.1:9002
    #          predicates:
    #            - Path=/order-service/**
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    (1)id :我们自定义的路由 ID,保持唯一
    (2)uri :目标服务地址
    (3)predicates :路由条件,Predicate 接受一个输入参数,返回一个布尔值结果。该接口包含多种默
    认方法来将 Predicate 组合成其他复杂的逻辑(比如:与,或,非)。
    (4)filters :过滤规则,暂时没用

    上面这段配置的意思是,配置了一个 id 为 persson-service的路由规则,当访问网关请求地址person 开头时,会自动转发到地址: http://127.0.0.1:9001/ 。

    配置完成启动项目即可在浏览器访问进行测试,当我们访问地址 http://localhost:9009/person/find/2 时会展示页面展示如下

    在这里插入图片描述

    案例2:路由规则

    在 Spring Cloud Gateway 中 Spring 利用Predicate 的特性实现了各种路由匹配规则,有通过 Header、请求参数等不同的条件来进行作为条件
    匹配到对应的路由

    #路由断言之后匹配
    spring:
    cloud:
     gateway:
      routes:
      - id: after_route
       uri: https://xxxx.com
        #路由断言之前匹配
       predicates:
       - After=xxxxx
    #路由断言之前匹配
    spring:
    cloud:
     gateway:
      routes:
      - id: before_route
       uri: https://xxxxxx.com
       predicates:
       - Before=xxxxxxx
    #路由断言之间
    spring:
    cloud:
     gateway:
      routes:
      - id: between_route
       uri: https://xxxx.com
       predicates:
       - Between=xxxx,xxxx
    #路由断言Cookie匹配,此predicate匹配给定名称(chocolate)和正则表达式(ch.p)
    spring:
    cloud:
     gateway:
      routes:
      - id: cookie_route
       uri: https://xxxx.com
       predicates:
       - Cookie=chocolate, ch.p
    #路由断言Header匹配,header名称匹配X-Request-Id,且正则表达式匹配\d+
    spring:
    cloud:
     gateway:
      routes:
      - id: header_route
       uri: https://xxxx.com
       predicates:
       - Header=X-Request-Id, \d+
    #路由断言匹配Host匹配,匹配下面Host主机列表,**代表可变参数
    spring:
    cloud:
     gateway:
      routes:
      - id: host_route
       uri: https://xxxx.com
       predicates:
       - Host=**.somehost.org,**.anotherhost.org
    #路由断言Method匹配,匹配的是请求的HTTP方法
    spring:
    cloud:
     gateway:
      routes:
      - id: method_route
       uri: https://xxxx.com
       predicates:
       - Method=GET
    #路由断言匹配,{segment}为可变参数
    spring:
    cloud:
     gateway:
      routes:
      - id: host_route
       uri: https://xxxx.com
       predicates:
       - Path=/foo/{segment},/bar/{segment}
    #路由断言Query匹配,将请求的参数param(baz)进行匹配,也可以进行regexp正则表达式匹配 (参数包含
    foo,并且foo的值匹配ba.)
    spring:
    cloud:
     gateway:
      routes:
      - id: query_route
       uri: https://xxxx.com
       predicates:
       - Query=baz 或 Query=foo,ba.
       
    #路由断言RemoteAddr匹配,将匹配192.168.1.1~192.168.1.254之间的ip地址,其中24为子网掩码位
    数即255.255.255.0 
    spring:
    cloud:
     gateway:
      routes:
      - id: remoteaddr_route
       uri: https://example.org
       predicates:
       - RemoteAddr=192.168.1.1/24
    
    • 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
    • 72
    • 73
    • 74
    • 75
    • 76
    • 77
    • 78
    • 79
    • 80
    • 81
    • 82
    • 83
    • 84
    • 85
    • 86
    • 87
    • 88
    • 89
    • 90
    • 91
    • 92
    • 93
    • 94

    案例3:动态路由

    和zuul网关类似,在SpringCloud GateWay中也支持动态路由:即自动的从注册中心中获取服务列表并
    访问。
    (1)添加注册中心依赖
    在工程的pom文件中添加注册中心的客户端依赖(这里以Eureka为例)

     <dependency>
        <groupId>org.springframework.cloudgroupId>
        <artifactId>spring-cloud-starter-netflix-eureka-clientartifactId>
    dependency>
    
    • 1
    • 2
    • 3
    • 4

    ( 2)配置动态路由
    修改 application.yml 配置文件,添加eureka注册中心的相关配置,并修改访问映射的URL为服务名称

    server:
      port: 9009
    spring:
      application:
        name: Gateway-C9009
      cloud:
        gateway:
          routes:
          - id: person-service
            uri: lb://Service-A9001
            predicates:
            - Path=/person/**
    eureka:
      instance:
        prefer-ip-address: true #使用ip地址注册
        instance-id:  ${spring.cloud.client.ip-address}:${server.port} #向注册中心中注册服务id
      client:
        service-url:
          defaultZone: http://localhost:9000/eureka/,http://localhost:8000/eureka/
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    uri : uri以 lb: //开头(lb代表从注册中心获取服务),后面接的就是你需要转发到的服务名称

    测试

    http://localhost:9009/person/find
    
    • 1

    案例3:重写转发路径

    在这里插入图片描述

    在SpringCloud Gateway中,修改Path为/person-service/**

    # http://127.0.0.1:9009/person-service/person/find/1 转发到
    # http://127.0.0.1:9001/person-service/person/find/1
    但后者不存在,只有
    
    # http://127.0.0.1:9001/person/find/1 可以访问
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (1)修改配置文件
    修改 application.yml ,将匹配路径改为 /person-service/**

    重新启动网关,我们在浏览器访问

    http://127.0.0.1:9009/person-service/person/find/1
    
    • 1

    会抛出404。这是由于路由转发规则默认转发到

    http://127.0.0.1:9001/person-service/person/find/1
    
    • 1

    (2) 添加RewritePath重写转发路径
    修改 application.yml ,添加重写规则。

    server:
      port: 9009
    spring:
      application:
        name: Gateway-C9009
      cloud:
        gateway:
          routes:
          - id: service-1 #编号唯一
            uri: lb://Service-A9001
            predicates:
            - Path=/person-service/**
            filters:
            - RewritePath=/person-service/(?>.*), /$\{segment}
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    路径变化如下

    http://127.0.0.1:9009/person-service/person/find/1 转发到
    http://127.0.0.1:9001/person/find/1 可以访问
    
    • 1
    • 2

    案例4:微服务名称转发

    http://localhost:9009/service-a9001/person/find/1 转发到
    http://127.0.0.1:9001/person/find/1 可以访问
    
    • 1
    • 2

    配置如下

    server:
      port: 9009
    spring:
      application:
        name: Gateway-C9009
      cloud:
        gateway:
          routes:
          - id: service-1 #编号唯一
            uri: lb://Service-A9001
            predicates:
            - Path=/person-service/**
            filters:
            - RewritePath=/person-service/(?>.*), /$\{segment}
          discovery:
            locator:
              lower-case-service-id: true
              enabled: true
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    案例5:全局过滤器搭建

    》1:自定义一个全局过滤器,实现 globalfilter , ordered接口

    @Component
    public class LoginFilter implements GlobalFilter,Ordered {
    
        public Mono filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            System.out.println("执行了自定义的全局过滤器");
            return chain.filter(exchange); //继续向下执行
        }
    
        /**
         * 指定过滤器的执行顺序 , 返回值越小,执行优先级越高
         */
        public int getOrder() {
            return 0;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    》2:访问测试

    http://localhost:9009/service-a9001/person/find/1
    
    • 1

    案例6:鉴权中心

    (1)自定义全局过滤器需要实现 GlobalFilter和Ordered接口。
    (2)在 filter方法中完成过滤器的逻辑判断处理
    (3)在 getOrder方法指定此过滤器的优先级,返回值越大级别越低
    (4)ServerWebExchange 就相当于当前请求和响应的上下文,存放着重要的请求-响应属性、请求实例和响应实例等等。一个请求中的request,response都可以通过 ServerWebExchange 获取
    (5)调用 chain.filter 继续向下游执行

    /**
     * 自定义一个全局过滤器
     *      实现 globalfilter , ordered接口
     */
    @Component
    public class LoginFilter implements GlobalFilter,Ordered {
    
        /**
         * 执行过滤器中的业务逻辑
         *     对请求参数中的access-token进行判断
         *      如果存在此参数:代表已经认证成功
         *      如果不存在此参数 : 认证失败.
         *  ServerWebExchange : 相当于请求和响应的上下文(zuul中的RequestContext)
         */
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            System.out.println("执行了自定义的全局过滤器");
            //1.获取请求参数access-token
            String token = exchange.getRequest().getQueryParams().getFirst("access-token");
            //2.判断是否存在
            if(token == null) {
                //3.如果不存在 : 认证失败
                System.out.println("没有登录");
                exchange.getResponse().setStatusCode(HttpStatus.UNAUTHORIZED);
                return exchange.getResponse().setComplete(); //请求结束
            }
            //4.如果存在,继续执行
            return chain.filter(exchange); //继续向下执行
        }
    
        /**
         * 指定过滤器的执行顺序 , 返回值越小,执行优先级越高
         */
        public int getOrder() {
            return 0;
        }
    }
    
    
    • 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

    测试

    http://localhost:9009/service-a9001/person/find/1?access-token=abc
    http://localhost:9009/service-a9001/person/find/1
    
    • 1
    • 2

    案例7:网关限流(Filter)

    SpringCloudGateway官方就提供了基于令牌桶的限流支持。基于其内置的过滤器工厂RequestRateLimiterGatewayFilterFactory 实现。在过滤器工厂中是通过Redis和lua脚本结合的方式进行流量控制。

    》1:准备redis服务器
    Redis-x64-4.0.2.3.zip
    redisclient-客户端工具.zip

    在这里插入图片描述

    打开监控
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-S8bV6Ayv-1668667456838)(index_files/0cd2f043-5763-4fd2-bfd7-40042988f566.png)]
    》2:导入 redis的依赖
    首先在工程的pom文件中引入gateway的起步依赖和redis的reactive依赖,代码如下:

      
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-actuatorartifactId>
            dependency>
            
            <dependency>
                <groupId>org.springframework.bootgroupId>
                <artifactId>spring-boot-starter-data-redis-reactiveartifactId>
            dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    》3:修改application.yml配置文件

    server:
      port: 9009
    spring:
      application:
        name: Gateway-C9009
      redis:
        host: localhost
        pool: 6379
        database: 0
      cloud:
        gateway:
          routes:
          - id: service-1 #编号唯一
            uri: lb://Service-A9001
            predicates:
            - Path=/person-service/**
            filters:
            - name: RequestRateLimiter
              args:
                # 使用SpEL从容器中获取对象
                key-resolver: '#{@pathKeyResolver}'
                # 令牌桶每秒填充平均速率
                redis-rate-limiter.replenishRate: 1
                # 令牌桶的上限
                redis-rate-limiter.burstCapacity: 3
            - RewritePath=/person-service/(?>.*), /$\{segment}
    
    • 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

    在 application.yml 中添加了redis的信息,并配置了RequestRateLimiter的限流过滤器:
    burstCapacity ,令牌桶总容量。
    replenishRate ,令牌桶每秒填充平均速率。
    key-resolver ,用于限流的键的解析器的 Bean 对象的名字。它使用 SpEL 表达式根据#{@beanName}从 Spring 容器中获取 Bean 对象。

    》4: 配置KeyResolver
    为了达到不同的限流效果和规则,可以通过实现 KeyResolver 接口,定义不同请求类型的限流键。

    package com.dev1;
    
    @Configuration
    public class KeyResolverConfiguration {
    
        /**
         * 编写基于请求路径的限流规则
         * //abc
         * //基于请求ip 127.0.0.1
         * //基于参数
         */
        @Bean
        public KeyResolver pathKeyResolver() {
            //自定义的KeyResolver
            return new KeyResolver() {
                /**
                 * ServerWebExchange :
                 *      上下文参数
                 */
                public Mono<String> resolve(ServerWebExchange exchange) {
                    return Mono.just(exchange.getRequest().getPath().toString());
                }
            };
        }
    }
    
    
    • 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

    测试的时候使用以下地址不停刷新

    http://localhost:9009/person-service/person/find/1?access-token=abc
    
    • 1

    不要使用

    http://localhost:9009/service-a9001/person/find/1?access-token=abc
    
    • 1

    》5:基于请求参数或者ip限流的配置

    增加

    package com.dev1;
    
    
    @Configuration
    public class KeyResolver2Configuration {
    
        /**
         * 基于请求参数的限流
         *  请求 abc ? userId=1
         */
        @Bean
        public KeyResolver userKeyResolver() {
            return exchange -> Mono.just(
                    exchange.getRequest().getQueryParams().getFirst("userId")
                    //exchange.getRequest().getHeaders().getFirst("X-Forwarded-For") 基于请求ip的限流
            );
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    修改

    key-resolver: '#{@userKeyResolver}'
    
    • 1

    在这里插入图片描述

    大括号中就是我们的限流 Key,这边是IP,本地的就是localhost
    timestamp: 存储的是当前时间的秒数,也就是System.currentTimeMillis() / 1000或者
    Instant.now().getEpochSecond()
    tokens: 存储的是当前这秒钟的对应的可用的令牌数量
    
    • 1
    • 2
    • 3
    • 4

    Spring Cloud Gateway目前提供的限流还是相对比较简单的,在实际中我们的限流策略会有很多种情况,比如:
    对不同接口的限流
    被限流后的友好提示
    这些可以通过自定义RedisRateLimiter来实现自己的限流策略,这里我们不做讨论

  • 相关阅读:
    好用、可靠有安全的企业局域网文件传输工具
    1、3快速格式代码
    以太网帧间隙IFG详解(Interframe Gap帧间距)
    前端工程化精讲第十四课 增量构建:Webpack 中的增量构建
    2022年中职组“网络安全”赛项湖南省任务书
    程序员的注释之争:缘起与解决
    手把手教你写一个生成yapi接口代码Chrome 扩展插件
    广东新型制造类企业源代码防泄密方案
    小红书家装节期间,如何打造优质品牌内容?
    Linux下uboot添加自定义命令(详细)实例及原理解析
  • 原文地址:https://blog.csdn.net/u013621398/article/details/127903750