• SpringCloudAliBaba篇(八)之gateway ---> 手把手教你搭建服务网关


    1、什么是Spring Cloud Gateway

    • 网关作为流量的入口,常用的功能包括路由转发,权限校验,限流等
    • Spring Cloud Gateway是Spring Cloud 官方提出的第二代网关框架,定位于取代Netflix Zuul1.0,相比Zuul来说,Spring Cloud Gateway提供更优秀的性能,更强大的功能。
    • Spring Cloud Gateway 是由WebFlux + Netty + Reactor 实现的响应式的API网关,它不能在传统的servlet容器中工作,也不能构建war包
    • Spring Cloud Gateway 旨在为微服务架构提供一种简单且有效的API路由的管理方式,并基于Filter的方式提供网关的基本功能,例如说安全认证、监控、限流等等。

    1.2、Spring Cloud Gateway功能特征

    • 基于Spring Framework 5,Project Reactor和Spring Boot 2.0进行构建;
    • 动态路由: 能够匹配任何请求属性;
    • 支持路径重写
    • 集成Spring Cloud服务发现功能(Nacos,Eruka)
    • 可集成流控降级功能(Sentinel、Hystrix);
    • 可以对路由指定易于编写的Predicate(断言)和Filter(过滤器)

    1.3、核心概念

    • 路由(route)

    路由是网关中最基础的部分,路由信息包括一个ID,一个目的URL、一组断言工厂、一组Filter组成,如果断言为真,则说明请求的URL和配置的路由匹配

    • 断言(predicates)

    Java8中的断言函数,SpringCloud Gateway中的断言函数类型就是Spring5 框架中的ServerWebExchange.断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等,

    • 过滤器(Filter)

    SpringCloud Gateway中的filter分为Gateway Filter和Global Filter,Filter可以对请求和响应进行处理。

    2、Gateway初体验

    2.1、Gateway快速开始

    1、引入依赖

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

    2、application.yml

    server:
      port: 8889
    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          routes:
            - id: order_route     #路由的唯一标识,路由到order
              uri: http://localhost:8004 # 需要转发的地址
              # 断言规则 用于路由规则的匹配
              predicates:
                - Path=/order-server/**
              # 转发之前去掉第一层路径
              filters:
                - StripPrefix=1
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    浏览器进行访问http://localhost:8889/order-server/order/orderList即可转发到你的http://localhost:8004/order/orderList

    3、Gateway整合nacos

    增加一个依赖

    <!--nacos依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5

    修改yml文件

    server:
      port: 8889
    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          routes:
            - id: order_route    
              uri: lb://order-server #lb表示使用nacos本地的负载均衡策略
              predicates:
                - Path=/order-server/**
              filters:
                - StripPrefix=1
        #配置nacos        
        nacos:
          discovery:
            server-addr: 127.0.0.1:8847
            username: nacos
            password: nacos
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21

    技巧:断言和过滤路由简写方式

    yml编写方式

    server:
      port: 8889
    spring:
      application:
        name: api-gateway
      cloud:
        nacos:
          discovery:
            server-addr: 120.0.0.1:8847
            username: nacos
            password: nacos
        gateway:
          discovery:
            locator:
              enabled: true #自动识别nacos上的服务
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    运行即可,发现我们服务可以正常访问。

    4、内置断言工厂

    官网地址:

    https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#the-addrequestheader-gatewayfilter-factory

    4.1、基于Datetime类型

    此类型断言根据时间做判断,主要由三个:

    AfterRoutePredicateFactory:接受一个日期参数,判断请求日期是否晚于指定日期

    BeforeRoutePredicateFactory:接受一个日期参数,判断请求日期是否早于指定日期

    BetweenRoutePredicateFactory:接受一个日期参数,判断请求日期是否在指定时间段内

    案例一(晚于指定日期)

    spring:
      cloud:
        gateway:
          routes:
          - id: after_route
            uri: https://example.org
            predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    案例二(早于指定日期)

    spring:
      cloud:
        gateway:
          routes:
          - id: before_route
            uri: https://example.org
            predicates:
            - Before=2017-01-20T17:42:47.789-07:00[America/Denver]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    案例三(指定日期之间)

    spring:
      cloud:
        gateway:
          routes:
          - id: between_route
            uri: https://example.org
            predicates:
            - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    4.2、基于远程地址

    RemoteAddrRoutePredicateFactory: 接收一个ip地址段,判断请求主机是否在地址端中

    - RemoteAddr=192.168.1.1/24
    
    • 1

    4.3、基于Cookie

    CookieRoutePredicateFactory: 接收两个参数,cookie名字和一个正则表达式,判断请求cookie是否具有给定名称且与正则表达式匹配。

    - Cookie=chocolate, ch.p
    
    • 1

    4.4、基于Header

    HeaderRoutePredicateFactory: 接收两个参数,标题名称和一个正则表达式,判断请求Header是否具有给定名称且与正则表达式匹配。

    - Header=X-Request-Id, \d+
    
    • 1

    4.5、基于Host

    HostRoutePredicateFactory: 接收一个参数,主机名模式。判断请求的Host是否匹配规则

    - Host=**.somehost.org,**.anotherhost.org
    
    • 1

    4.6、基于Method请求方法

    MethodRoutePredicateFactory: 接收一个参数,判断请求类型是否跟指定的类型匹配

    - Method=GET,POST
    
    • 1

    4.7、基于Path匹配请求路径

    {segment}:需要携带参数

    - Path=/red/{segment},/blue/{segment}
    
    • 1

    例: /red/1 or /red/aaa or /blue/red

    4.8、基于weight权重

    spring:
      cloud:
        gateway:
          routes:
          - id: weight_high
            uri: https://weighthigh.org
            predicates:
            - Weight=group1, 8
          - id: weight_low
            uri: https://weightlow.org
            predicates:
            - Weight=group1, 2
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4.9、基于请求参数

    - Query=green
    
    • 1

    例入: ?green=121

    - Query=red, gree.
    
    • 1

    例入:?red=gree,值需要匹配后面的正则

    5、自定义断言工厂

    自定义断言工厂需要继承AbstractRoutePredicateFactory类,重写apply方法的逻辑,在apply方法中可以通过exchange.getRequest()拿到ServerHttpRequest对象,从而可以获取到请求的参数、请求方式、请求头等信息

    要求

    • 必须spring组件bean
    • 类必须加上RoutePredicateFactory作为结尾
    • 继承AbstractRoutePredicateFactory
    • 必须声明静态内部类,声明属性来接受 配置文件中的信息
    • 需要结合shortcutFieldOrder来进行绑定
    • 通过apply进行逻辑编写,true就是匹配成功,false就是匹配失败
    @Component
    public class CheckNameRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckNameRoutePredicateFactory.Config> {
    
        public CheckNameRoutePredicateFactory() {
            super(CheckNameRoutePredicateFactory.Config.class);
        }
    
        @Override
        public List<String> shortcutFieldOrder() {
            return Arrays.asList("name");
        }
    
        @Override
        public Predicate<ServerWebExchange> apply(CheckNameRoutePredicateFactory.Config config) {
            return new GatewayPredicate() {
                @Override
                public boolean test(ServerWebExchange exchange) {
                    return "YangLi".equals(config.getName());
                }
    
            };
        }
    
        /**
         * 用于接受配置文件中 断言的信息
         */
        @Validated
        public static class Config {
            private String name;
    
            public String getName() {
                return name;
            }
    
            public void setName(String name) {
                this.name = name;
            }
        }
    
    }
    
    • 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

    yml文件加上

    - CheckName=YangLi
    
    • 1

    测试即可。

    6、内置过滤器工厂(局部)

    由于内置过滤器工厂太多,我这里随便列几个,想看全部内置过滤器工厂,可去官网查看

    6.1、添加请求头

    - AddRequestHeader=X-Request-red, red
    
    • 1

    进行测试

    @RequestMapping("/header")
    public String header(@RequestHeader("X-Request-red") String color){
        return color;
    }
    
    • 1
    • 2
    • 3
    • 4

    结果

    成功返回red

    6.2、添加请求参数

    - AddRequestParameter=color, blue
    
    • 1

    进行测试

    @RequestMapping("/parameter")
    public String parameter(@RequestParam("color") String color){
        return color;
    }
    
    • 1
    • 2
    • 3
    • 4

    结果

    成功返回blue

    6.3、为匹配的路由统一添加前缀

    - PrefixPath=/mall-order #前缀,对应微服务需要配置context-path
    
    • 1

    mall-order中需要配置

    server:
      servlet:
        context-path: /mall-order
    
    • 1
    • 2
    • 3

    6.4、重定向

    - RedirectTo=302, https://www.baidu.com/ #重定向到百度
    
    • 1

    7、自定义过滤器工厂(局部)

    • 继承AbstractGatewayFilterFactory
    • 自定义的名称要以GatewayFilterFactory结尾
    • 交给spring管理

    例子:请求后面的参数name=YangLi才可以

    - CheckAuth=YangLi #自定义拦截器,必须?name=YangLi
    
    • 1
    @Component
    public class
    CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {
        public CheckAuthGatewayFilterFactory() {
            super(CheckAuthGatewayFilterFactory.Config.class);
        }
    
        @Override
        public List<String> shortcutFieldOrder() {
            return Arrays.asList("value");
        }
        @Override
        public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
            return (exchange, chain) -> {
                String name = exchange.getRequest().getQueryParams().getFirst("name");
                if (StringUtils.isNotBlank(name)){
                    if (config.getValue().equals(name)){
                        return  chain.filter(exchange);
                    }else{
                        // 返回404
                        exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                        return exchange.getResponse().setComplete();
                    }
                }
                return chain.filter(exchange);
            };
        }
        /**
         * 用于接受配置文件中 过滤器的信息
         */
        @Validated
        public static class Config {
            private String value;
    
            public String getValue() {
                return value;
            }
    
            public void setValue(String value) {
                this.value = value;
            }
        }
    
    }
    
    • 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

    8、全局过滤器

    116

    局部过滤器和全局过滤器的区别?

    局部:局部针对某个路由,需要在路由中进行配置

    全局:针对所有路由请求,一旦定义就会全局使用

    9、自定义全局过滤器

    只需要实现GlobalFilter接口即可

    @Component
    public class LogGlobalFilter implements GlobalFilter {
        Logger logger = LoggerFactory.getLogger(this.getClass());
    
        @Override
        public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
            logger.info(exchange.getRequest().getPath().value());
            return chain.filter(exchange);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11

    10、请求日志记录(Reactor Netty访问日志)

    要启用Reactor Netty访问日志,请设置-Dreactor.netty.http.server.accessLogEnabled=true

    它是java系统属性,不是Spring Boot属性

    117

    进行访问,结果会打印日志信息

    2022-07-01 15:32:12.140  INFO 15400 --- [ctor-http-nio-2] reactor.netty.http.server.AccessLog      : 0:0:0:0:0:0:0:1 - - [01/Jul/2022:15:32:11 +0800] "GET /order-server/order/parameter HTTP/1.1" 200 4 8889 942 ms
    
    • 1

    11、跨域相关配置

    前后端分离需要跨域,所以我们直接在网关这里进行配置

    yml方式配置

    spring:
      cloud:
        gateway:
          globalcors:
            cors-configurations:
              '[/**]': # 允许访问的资源
                allowedOrigins: "*" #跨域允许来源
                allowedMethods:
                - GET
                - POST
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    配置类的方式

    @Configuration
    public class CorsConfig {
        @Bean
        public CorsWebFilter corsWebFilter(){
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            // 允许的请求头参数
            corsConfiguration.addAllowedHeader("*");
            // 允许的method
            corsConfiguration.addAllowedMethod("*");
            // 允许的来源
            corsConfiguration.addAllowedOrigin("*");
            // 允许访问的资源
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
            source.registerCorsConfiguration("/**",corsConfiguration);
            return new CorsWebFilter(source);
    
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19

    12、整合sentinel流控降级

    网关作为内部系统外的一层屏障,对内起到一定的保护作用,限流便是其中之一,网关层的限流可以简单的针对不同路由进行限流,也可针对业务的接口进行限流,或者根据接口的特征分组限流。

    1、添加依赖

    <!--sentinel整合gateway的依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
    </dependency>
    <!--sentinel的依赖-->
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
    </dependency>
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    2、添加配置

        sentinel:
          transport:
            dashboard: 127.0.0.1:8010
    
    • 1
    • 2
    • 3

    3、运行并进入控制台

    118

    测试简单流控规则(一秒钟访问两次将会被限流):

    119

    在浏览器进行访问并快速刷新,触发流控规则

    120

    名词解读

    • Busrst size 宽容次数,比如设置为1,表示一秒钟访问3次以上被限流
    • 间隔 比如间隔设置为2,则表示2秒时间内访问次数大于2则被流控

    针对请求属性

    121

    Client IP 根据客户端ip地址进行流控

    • 精确 ------- 精确匹配你填的匹配串进行流控
    • 子串 --------包含匹配串
    • 正则 --------- 可以填入正则表达式

    Remote Host 根据远程域名进行流控

    Header:根据请求头名字匹配对应值进行流控

    122

    URL参数和Cookie方式与上述雷同


    新建api分组(针对API接口流控)

    123

    新增流控规则,选择刚刚新加的api分组

    124

    进行测试发现,针对这两个接口发生流控,其他接口无流控

    降级规则

    125

    当然,相应api也可以配置降级规则

    系统规则

    126

    这个与sentinel配置系统保护用法一致。

    自定义流控异常返回信息

    配置类方式:

    @Configuration
    public class GatewayConfig {
        @PostConstruct
        public void init(){
            BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
                @Override
                public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
                    HashMap<String,String> map = new HashMap<>(10);
                    map.put("code", HttpStatus.TOO_MANY_REQUESTS.toString());
                    map.put("message","限流了");
                    // 自定义异常处理
                    return ServerResponse.status(HttpStatus.OK)
                            .contentType(MediaType.APPLICATION_JSON)
                            .body(BodyInserters.fromValue(map));
                }
            };
            GatewayCallbackManager.setBlockHandler(blockRequestHandler);
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    127

    yml方式

    spring:
      application:
        name: api-gateway
      cloud:
        gateway:
          scg:
            fallback:
              mode: response
              response-body: '{"code":403,"mess":"限流了"}'
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9

    128

  • 相关阅读:
    MYSQL函数
    「网络编程」数据链路层协议_ 以太网协议学习
    七天学会C语言-第一天(C语言基本语句)
    JDK19虚拟线程初探(三)
    pdf增强插件 Enfocus PitStop Pro 2022 mac中文版功能介绍
    vue2 vuex
    如何在 Objective-C 中实现多态性,并且它与其他面向对象编程语言的多态性实现有何差异?
    List集合拆分为多个List
    ChatGPT 基本用法!ChatGPT4的prompt的使用例子!
    【读书笔记】《0day安全》第二章:栈溢出的原理和实践
  • 原文地址:https://blog.csdn.net/qijing19991210/article/details/125563491