Spring Cloud Alibaba-----整合Gateway

1.Gateway

1.1 网关简介

大家都知道在微服务架构中,一个系统会拆分为多个微服务。那么作为客户端要如何去调用这么多的微服务,如果没有网关存在,我们只能在客户端记录每个微服务的地址,然后去分别用。
在这里插入图片描述
这样的架构,会存在着诸多的问题:

  • 每个业务都会需要鉴权、限流、权限校验、跨域等逻辑,如果每个业务都各自为战,自己造轮子实现一遍,会很蛋疼,完全可以抽出来,放到一个统一的地方去做。
  • 如果业务量比较简单的话,这种方式前期不会有什么问题,但随着业务越来越复杂,比如淘宝、亚马逊打开一个页面可能会涉及到数百个微服务协同工作,如果每一个微服务都分配一个域名的话,一方面客户端代码会很难维护,涉及到数百个域名,另一方面是连接数的瓶颈,想象一下你打开一个APP,通过抓包发现涉及到了数百个远程调用,这在移动端下会显得非常低效。
  • 后期如果需要对微服务进行重构的话,也会变的非常麻烦,需要客户端配合你一起进行改造,比如商品服务,随着业务变的越来越复杂,后期需要进行拆分成多个微服务,这个时候对外提供的服务也需要拆分成多个,同时需要客户端配合你进行改造,非常蛋疼。

上面的这些问题可以借助API网关来解决。
在这里插入图片描述
所谓的API网关,就是指系统的统一入口,它封装了应用程序的内部结构,为客户端提供统一服务,一些与业务本身功能无关的公共逻辑可以在这里实现,诸如认证、鉴权、监控、路由转发等等。
添加上API网关之后,系统的架构图变成了如下所示:
在这里插入图片描述
我们也可以观察下,我们现在的整体架构图:
在这里插入图片描述

1.2 什么是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.1 其他的网关组件:

在SpringCloud微服务体系中,有个很重要的组件就是网关,在1.x版本中都是采用的Zuul网关;但在2.x版本中,zuul的升级一直跳票,SpringCloud最后自己研发了一个网关替代Zuul,那就是SpringCloud Gateway,网上很多地方都说Zuul是阻塞的,Gateway是非阻塞的,这么说是不严谨的,准确的讲Zuul1.x是阻塞的,而在2.x的版本中,Zuul也是基于Netty,也是非阻塞的,如果一定要说性能,其实这个真没多大差距。

而官方出过一个测试项目,创建了一个benchmark的测试项目:spring-cloud-gateway-bench,其中对比了:
在这里插入图片描述
性能强劲:是第一代网关Zuul的1.6倍
官网文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

1.2.2 Spring Cloud Gateway 功能特征

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

1.3 Spring Cloud Gateway核心概念和原理

1.3.1 核心概念

  • 路由(route)
    路由是网关中最基础的部分,路由信息包括一个ID、一个目的URI、一组断言工厂、一组Filter组成。如果断言为真,则说明请求的URL和配置的路由匹配。
  • 断言(predicates)
    Java8中的断言函数,SpringCloud Gateway中的断言函数类型是Spring5.0框架中的ServerWebExchange。断言函数允许开发者去定义匹配Http request中的任何信息,比如请求头和参数等。
  • 过滤器(Filter)
    SpringCloud Gateway中的filter分为Gateway FilIer和Global Filter。Filter可以对请求和响应进行处理。

1.3.2 工作原理

在这里插入图片描述

2.Gateway的使用

2.1 环境搭建

2.1.1 引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

2.1.2 编写yml配置文件

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: http://localhost:8020  # 需要转发的地址
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add

2.1.3 集成Nacos

引入依赖

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
</dependency>

编写yml配置文件

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: lb://order-server  # 需要转发的地址  lb:使用nacos中本地的负载均衡策略 order-server 服务名
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add
    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

简写: 去掉关于路由的配置,自动寻找服务

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      discovery:
        locator:
          enabled: true # 是否启动我们的自动识别nacos服务  可以直接用服务名作为前缀访问,断言也是根据服务名做断言路由到我们的真实服务地址
                        # http://localhost:8088/order-server/order/add

    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

测试(这时候,就发现只要按照网关地址/微服务/接口的格式去访问,就可以得到成功响应)
在这里插入图片描述

2.2 路由断言工厂(Route Predicate Factories)配置(局部,只针对某一个路由)

作用: 当请求gateway的时候, 使用断言对请求进行匹配, 如果匹配成功就路由转发, 如果匹配失败就返回404
类型:内置,自定义

2.2.1 内置断言工厂

官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gateway-request-predicates-factories

  • 基于Datetime类型的断言工厂
    此类型的断言根据时间做判断,主要有三个:
    。 AfterRoutePredicateFactory: 接收一个日期参数,判断请求日期是否晚于指定日期
    。BeforeRoutePredicateFactory: 接收一个日期参数,判断请求日期是否早于指定日期
    。BetweenRoutePredicateFactory: 接收两个日期参数,判断请求日期是否在指定时间段内
    ZonedDateTime.now()
spring:
 cloud:
   gateway:
     routes:
     - id: after_route
       uri: https://example.org
       predicates:
       - After=2017-01-20T17:42:47.789-07:00[America/Denver]
  • 基于远程地址的断言工厂
    RemoteAddrRoutePredicateFactory:接收一个IP地址段,判断请求主机地址是否在地址段中
    ‐ RemoteAddr=192.168.1.1/24
  • 基于Cookie的断言工厂
    CookieRoutePredicateFactory:接收两个参数,cookie 名字和一个正则表达式。 判断请求
    cookie是否具有给定名称且值与正则表达式匹配。
    ‐ Cookie=chocolate, ch.
  • 基于Header的断言工厂
    HeaderRoutePredicateFactory:接收两个参数,标题名称和正则表达式。 判断请求Header是否具有给定名称且值与正则表达式匹配。
    ‐ Header=X‐Request‐Id, \d+
  • 基于Host的断言工厂
    HostRoutePredicateFactory:接收一个参数,主机名模式。判断请求的Host是否满足匹配规则。
    ‐ Host=**.testhost.org
  • 基于Method请求方法的断言工厂
    MethodRoutePredicateFactory:接收一个参数,判断请求类型是否跟指定的类型匹配。
    ‐ Method=GET
  • 基于Path请求路径的断言工厂
    PathRoutePredicateFactory:接收一个参数,判断请求的URI部分是否满足路径规则。
    ‐ Path=/foo/{segment}
  • 基于Query请求参数的断言工厂
    QueryRoutePredicateFactory :接收两个参数,请求param和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。
    -Query=baz, ba.
  • 基于路由权重的断言工厂
    WeightRoutePredicateFactory:接收一个[组名,权重], 然后对于同一个组内的路由按照权重转发
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

2.2.2 自定义断言工厂

  1. 必须spring组件 注入bean(@Component)
  2. 类必须加上RoutePredicateFactory作为结尾
  3. 必须继承AbstractRoutePredicateFactory
  4. 必须声明静态内部类 声明属性来接收 配置文件中对应的断言的信息
  5. 需要结合shortcutFieldOrder进行绑定
  6. 通过apply进行逻辑判断 true就是匹配成功 false匹配失败

注意: 命名需要以 RoutePredicateFactory 结尾,必须注入到spring中

package com.xiexie.gateway.predicate;

import org.springframework.cloud.gateway.handler.predicate.AbstractRoutePredicateFactory;
import org.springframework.cloud.gateway.handler.predicate.GatewayPredicate;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
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;

/**
 * @Description 自定义断言工厂
 * @Date 2022-04-19 13:49
 * @Author xie
 */
@Component
public class CheckAuthRoutePredicateFactory extends AbstractRoutePredicateFactory<CheckAuthRoutePredicateFactory.Config> {

    public CheckAuthRoutePredicateFactory() {
        super(CheckAuthRoutePredicateFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        // 必须跟config中的属性名进行绑定
        return Arrays.asList("name");
    }

    @Override
    public Predicate<ServerWebExchange> apply(CheckAuthRoutePredicateFactory.Config config) {
        return new GatewayPredicate() {
            @Override
            public boolean test(ServerWebExchange exchange) {
                if (StringUtils.isEmpty(config.getName()) || !"xiexie".equals(config.getName())) {
                    return false;
                }
                return true;
            }

            @Override
            public String toString() {
                return String.format("CheckAuth: name=%s", config.getName());
            }
        };
    }

    /**
     * 用于接收配置文件中 断言的信息
     */
    @Validated
    public static class Config {

        private String name;

        public String getName() {
            return name;
        }

        public void setName(String name) {
            this.name = name;
        }
    }
}
server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: lb://order-server  # 需要转发的地址  lb:使用nacos中本地的负载均衡策略 order-server 服务名
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add
            - Path=/order-serv/**
            # 自定义CheckAuth断言工厂
            - CheckAuth=xiexie
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add
    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956

2.3 过滤器工厂( GatewayFilter Factories)配置(局部,只针对某一个路由)

Gateway 内置了很多的过滤器工厂,我们通过一些过滤器工厂可以进行一些业务逻辑处理器,比如添加剔除响应头,添加去除参数等
在这里插入图片描述
官方文档:https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#gatewayfilter-factories

2.3.1 内置过滤器工厂

过滤器工厂作用参数
AddRequestHeader为原始请求添加HeaderHeader的名称及值
AddRequestParameter为原始请求添加请求参数参数名称及值
AddResponseHeader为原始响应添加HeaderHeader的名称及值
DedupeResponseHeader剔除响应头中重复的值需要去重的Header名称及去重策略
Hystrix为路由引入Hystrix的断路器保护HystrixCommand的名称
FallbackHeaders为fallbackUri的请求头中添加具体的异常信息Header的名称
PrefixPath为原始请求路径添加前缀前缀路径
PreserveHostHeader为请求添加一个preserveHostHeader=true 的属性,路由过滤器会检查该属性以决定是否要发送原始的Host
RequestRateLimiter用于对请求限流,限流算法为令牌桶keyResolver、rateLimiter、statusCode、denyEmptyKey、emptyKeyStatus
RedirectTo将原始请求重定向到指定的URLhttp状态码及重定向的url
RemoveHopByHopHeadersFilter为原始请求删除IETF组织规定的一系列Header默认就会启用,可以通过配置指定仅删除哪些Header
RemoveRequestHeader为原始请求删除某个HeaderHeader名称
RemoveResponseHeader为原始响应删除某个HeaderHeader名称
RewritePath重写原始的请求路径原始路径正则表达式以及重写后路径的正则表达式
RewriteResponseHeader重写原始响应中的某个HeaderHeader名称、值的正则表达式,重写后的值
SaveSession在转发请求之前,强制执行WebSession::save操作
secureHeaders为原始响应添加一系列起安全作用的响应头无,支持修改这些安全响应头的值
SetPath修改原始的请求路径修改后的路径
SetResponseHeader修改原始响应中某个Header的值Header名称,修改后的值
SetStatus修改原始响应的状态码HTTP 状态码,可以是数字,也可以是字符串
StripPrefix用于截断原始请求的路径使用数字表示要截断的路径的数量
Retry针对不同的响应进行重试retries、statuses、methods、series
RequestSize设置允许接收最大请求包的大 小。如果请求包大小超过设置的值,则返回 413 Payload Too Large请求包大小,单位为字节,默认值为5M
ModifyRequestBody在转发请求之前修改原始请求体内容修改后的请求体内容
ModifyResponseBody修改原始响应体的内容修改后的响应体内容
2.3.1.1 添加请求头(上下文参考-全量配置文件)
server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 路由配置
      routes:
        - id: order_route  # 路由的唯一标识,路由到order
          uri: lb://order-server  # 需要转发的地址  lb:使用nacos中本地的负载均衡策略(loadbalancer) order-server 服务名
          # 断言规则,用于路由规则的匹配
          predicates:
            # 因为是字符串所以可以这样写,自动映射
            # http://localhost:8088/order-serv/order/add 路由到↓
            # http://localhost:8020/order-serv/order/add   http://order-server/order-serv/order/add
            - Path=/order-serv/**
            # 自定义CheckAuth断言工厂
            - CheckAuth=xiexie # 数据放在自定义断言工厂的config中,根据config映射到的数据值进行判断是否匹配到
          # 过滤器,请求在传递过程中可以通过过滤器对其进行一定的修改
          filters:
            - StripPrefix=1 # 转发之前去掉1层路径(内置的一种过滤器) 变成http://localhost:8020/order/add
            # 其他的内置过滤器
            - AddRequestHeader=X-Request-color, red # 添加请求头参数
            # - AddRequestParameter=color, blue # 添加请求参数
            # - PrefixPath=/mall‐order # 添加前缀 对应微服务需要配置context‐path: /mall‐order
            # - RedirectTo=302, https://www.baidu.com/ # 重定向到百度
            # - CheckAuth=name, xiexie # 数据放在自定义的过滤器中,根据config映射到的数据进行判断是否放行
    # 注册到nacos中
    nacos:
      server-addr: 127.0.0.1:8848
      username: nacos
      password: nacos
      discovery:
        namespace: 6325e312-7f95-48d7-8d06-810047ed3956


@RequestMapping("/header")
public String header(@RequestHeader("X-Request-color") String color) {
    System.out.println("gateWay获取请求头X‐Request‐color:" + color);
    return color;
}
2.3.1.2 添加请求参数(上下文参考上文全量配置文件)
- AddRequestParameter=color, blue # 添加请求参数
@RequestMapping("/parameter")
public String parameter(@RequestParam("color") String color) {
    System.out.println("gateWay获取参数color:" + color);
    return color;
}
2.3.1.3 为匹配的路由统一添加前缀(上下文参考上文全量配置文件)
- PrefixPath=/mall‐order # 添加前缀 对应微服务需要配置context‐path: /mall‐order
server:
  port: 8020
  servlet:
    context-path: /mall‐order
2.3.1.4 重定向操作(上下文参考上文全量配置文件)
- RedirectTo=302, https://www.baidu.com/ # 重定向到百度




还有很多,自行参照上面列表测试

2.3.2 自定义过滤器工厂

继承AbstractNameValueGatewayFilterFactory且我们的自定义名称必须要以GatewayFilterFactory结尾并交给spring管理。

跟自定义的断言工厂相似

实现过滤工厂

package com.xiexie.gateway.filter;

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.cloud.gateway.filter.factory.RedirectToGatewayFilterFactory;
import org.springframework.http.HttpStatus;
import org.springframework.stereotype.Component;
import org.springframework.util.MultiValueMap;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.Arrays;
import java.util.List;
import java.util.Set;

/**
 * @Description
 * @Date 2022-04-19 15:37
 * @Author xie
 */
@Component
public class CheckAuthGatewayFilterFactory extends AbstractGatewayFilterFactory<CheckAuthGatewayFilterFactory.Config> {

    public CheckAuthGatewayFilterFactory() {
        super(CheckAuthGatewayFilterFactory.Config.class);
    }

    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("param", "value");
    }

    @Override
    public GatewayFilter apply(CheckAuthGatewayFilterFactory.Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                MultiValueMap<String, String> queryParams = exchange.getRequest().getQueryParams();
                Set<String> stringSet = queryParams.keySet();
                if (stringSet.contains(config.getParam()) && config.getValue().equals(queryParams.get(config.getParam()).get(0))) {
                    // 正常请求
                    return chain.filter(exchange);
                } else {
                    // 返回404并结束
                    exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);
                    return exchange.getResponse().setComplete();
                }
            }
        };
    }

    public static class Config {

        private String param;

        private String value;

        public String getParam() {
            return param;
        }

        public void setParam(String param) {
            this.param = param;
        }

        public String getValue() {
            return value;
        }

        public void setValue(String value) {
            this.value = value;
        }
    }
}

配置文件中配置(上下文参考上文全量配置文件)

- CheckAuth=name, xiexie # 数据放在自定义的过滤器中,根据config映射到的数据进行判断是否放行

2.4 全局过滤器(Global Filters)配置

在这里插入图片描述
局部过滤器和全局过滤器区别:

  • 局部:局部针对某个路由, 需要在路由中进行配置
  • 全局:针对所有路由请求, 一旦定义就会投入使用
    GlobalFilter 接口和 GatewayFilter 有一样的接口定义,只不过, GlobalFilter 会作用于所有路由

2.4.1 自定义全局过滤器

package com.xiexie.gateway.filter.global;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.cloud.gateway.filter.GatewayFilterChain;
import org.springframework.cloud.gateway.filter.GlobalFilter;
import org.springframework.stereotype.Component;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

/**
 * @Description 自定义全局过滤器
 * @Date 2022-04-19 16:30
 * @Author xie
 */
@Component
public class GlobalLogFilter implements GlobalFilter {

    Logger log = LoggerFactory.getLogger(GlobalLogFilter.class);

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {

        log.info(exchange.getRequest().getPath().value());
        return chain.filter(exchange);
    }
}

2.4.2 Reactor Netty 访问日志(gateway自动定义的日志收集)

https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#reactor-netty-access-logs
要启用 Reactor Netty 访问日志,请设置 -Dreactor.netty.http.server.accessLogEnabled=true

它必须是 Java 系统属性(设置JVM环境变量的),而不是 Spring Boot 属性。 java -jar
xxxGatewat.jar -Dreactor.netty.http.server.accessLogEnabled=true

可以将日志记录系统配置为具有单独的访问日志文件。(比如配置在Logback)

2.5 Gateway跨域配置(CORS Configuration)

在这里插入图片描述
通过yml配置的方式
https://docs.spring.io/spring-cloud-gateway/docs/current/reference/html/#cors-configuration

2.5.1 通过yml配置的方式

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 网关gateway配置
    gateway:
      # 跨域配置
      globalcors:
        cors-configurations:
          '[/**]':  # 允许跨域访问的资源
            allowedOrigins: "*"  # 跨域允许的来源
            allowedMethods:  # 跨域允许的method
              - GET
              - POST
              - DELETE
              - PUT
              - OPTION

2.5.1 通过java代码方式

package com.xiexie.gateway.config;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.reactive.CorsWebFilter;
import org.springframework.web.cors.reactive.UrlBasedCorsConfigurationSource;
import org.springframework.web.util.pattern.PathPatternParser;

/**
 * @Description 跨域配置
 * @Date 2022-04-19 17:06
 * @Author xie
 */
@Configuration
public class CorsConfig {

    @Bean
    public CorsWebFilter corsWebFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*"); // 允许跨域的method
        config.addAllowedOrigin("*"); // 允许跨域的来源
        config.addAllowedHeader("*"); // 允许跨域的header头

        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config); // 允许跨域访问的资源
        return new CorsWebFilter(source);
    }
}

2.6 gateway整合sentinel流控降级

网关作为内部系统外的一层屏障, 对内起到一定的保护作用, 限流便是其中之一. 网关层的限流可以简单地针对不同路由进行限流, 也可针对业务的接口进行限流,或者根据接口的特征分组限流。
官方文档:https://github.com/alibaba/Sentinel/wiki/网关限流

在这里插入图片描述
Sentinel 1.6.3 引入了网关流控控制台的支持,用户可以直接在 Sentinel 控制台上查看 API Gateway 实时的 route 和自定义 API 分组监控,管理网关规则和 API 分组配置。

从 1.6.0 版本开始,Sentinel 提供了 Spring Cloud Gateway 的适配模块,可以提供两种资源维度的限流:

  • route 维度:即在 Spring 配置文件中配置的路由条目,资源名为对应的 routeId
  • 自定义 API 维度:用户可以利用 Sentinel 提供的 API 来自定义一些 API 分组

entinel 1.6.0 引入了 Sentinel API Gateway Adapter Common 模块,此模块中包含网关限流的规则和自定义 API 的实体和管理逻辑:

  • GatewayFlowRule:网关限流规则,针对 API Gateway 的场景定制的限流规则,可以针对不同 route 或自定义的 API 分组进行限流,支持针对请求中的参数、Header、来源 IP 等进行定制化的限流。
  • ApiDefinition:用户自定义的 API 定义分组,可以看做是一些 URL 匹配的组合。比如我们可以定义一个 API 叫 my_api,请求 path 模式为 /foo/** 和 /baz/** 的都归到 my_api 这个 API 分组下面。限流的时候可以针对这个自定义的 API 分组维度进行限流。

2.6.1 添加依赖

<!--gateway 整合sentinel-->
<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-alibaba-sentinel-gateway</artifactId>
</dependency>

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>

2.6.2 添加配置

server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 整合sentinel
    sentinel:
      transport:
        # 添加sentinel控制台
        dashboard: 127.0.0.1:8858

在这里插入图片描述

具体操作可参考sentinel章节操作详解

2.6.3 自定义异常

2.6.3.1 通过配置完成
server:
  port: 8088

spring:
  application:
    name: api-gateway
  cloud:
    # 整合sentinel
    sentinel:
      transport:
        # 添加sentinel控制台
        dashboard: 127.0.0.1:8858
      # 自定义异常
      scg:
        fallback:
          mode: response
          response-body: '{"code":"403", "msg":"限流了"}'
2.6.3.2 通过GatewayCallbackManager(代码实现)
package com.xiexie.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.Map;

/**
 * @Description 自定义限流异常
 * @Date 2022-04-20 9:19
 * @Author xie
 */
@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void init() {

        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            // 自定义异常处理
            Map<String, String> result = new HashMap<>();

            if (throwable instanceof FlowException) {
                // 限流

            } else if (throwable instanceof DegradeException) {
                // 降级

            } // .......

            result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
            result.put("code", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());

            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(result));
        };
    }
}

2.6.4 代码实现限流降级操作

用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则 GatewayConfiguration中添加:

package com.xiexie.gateway.config;

import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayFlowRule;
import com.alibaba.csp.sentinel.adapter.gateway.common.rule.GatewayRuleManager;
import com.alibaba.csp.sentinel.adapter.gateway.sc.callback.BlockRequestHandler;
import com.alibaba.csp.sentinel.slots.block.degrade.DegradeException;
import com.alibaba.csp.sentinel.slots.block.flow.FlowException;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.web.reactive.function.BodyInserters;
import org.springframework.web.reactive.function.server.ServerResponse;

import javax.annotation.PostConstruct;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
 * @Description 自定义网关限流降级和异常
 * @Date 2022-04-20 9:19
 * @Author xie
 */
@Configuration
public class GatewaySentinelConfig {

    @PostConstruct
    public void init() {

        // 自定义异常信息
        initBlockRequestHandler();

        // 自定义网关限流规则
        initGatewayRules();
    }

    private void initBlockRequestHandler() {
        BlockRequestHandler blockRequestHandler = (serverWebExchange, throwable) -> {

            // 自定义异常处理
            Map<String, String> result = new HashMap<>();

            if (throwable instanceof FlowException) {
                // 限流

            } else if (throwable instanceof DegradeException) {
                // 降级

            } // .......

            result.put("code", String.valueOf(HttpStatus.TOO_MANY_REQUESTS.value()));
            result.put("code", HttpStatus.TOO_MANY_REQUESTS.getReasonPhrase());

            return ServerResponse.status(HttpStatus.OK)
                    .contentType(MediaType.APPLICATION_JSON)
                    .body(BodyInserters.fromValue(result));
        };
    }

    private void initGatewayRules() {

        Set<GatewayFlowRule> rules = new HashSet<>();
        //resource:资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
        //count:限流阈值
        //intervalSec:统计时间窗口,单位是秒,默认是 1 秒。
        rules.add(new GatewayFlowRule("order_route").setCount(2).setIntervalSec(1));
        rules.add(new GatewayFlowRule("user_server_api").setCount(2).setIntervalSec(1));

        // 加载网关规则
        GatewayRuleManager.loadRules(rules);
    }
}

其中网关限流规则 GatewayFlowRule 的字段解释如下:

字段注释
resource资源名称,可以是网关中的 route 名称或者用户自定义的 API 分组名称。
resourceMode规则是针对 API Gateway 的 route(RESOURCE_MODE_ROUTE_ID)还是用户在 Sentinel 中定义的 API 分组(RESOURCE_MODE_CUSTOM_API_NAME),默认是 route。
grade限流指标维度,同限流规则的 grade 字段。
count限流阈值
intervalSec统计时间窗口,单位是秒,默认是 1 秒。
controlBehavior流量整形的控制效果,同限流规则的 controlBehavior 字段,目前支持快速失败和匀速排队两种模式,默认是快速失败。
burst应对突发请求时额外允许的请求数目。
maxQueueingTimeoutMs匀速排队模式下的最长排队时间,单位是毫秒,仅在匀速排队模式下生效。
paramItem参数限流配置。若不提供,则代表不针对参数进行限流,该网关规则将会被转换成普通流控规则;否则会转换成热点规则。
paramItem - parseStrategy从请求中提取参数的策略,目前支持提取来源 IP(PARAM_PARSE_STRATEGY_CLIENT_IP)、Host(PARAM_PARSE_STRATEGY_HOST)、任意 Header(PARAM_PARSE_STRATEGY_HEADER)和任意 URL 参数(PARAM_PARSE_STRATEGY_URL_PARAM)四种模式。
paramItem - fieldName若提取策略选择 Header 模式或 URL 参数模式,则需要指定对应的 header 名称或 URL 参数名称。
paramItem - pattern参数值的匹配模式,只有匹配该模式的请求属性值会纳入统计和流控;若为空则统计该请求属性的所有值。(1.6.2 版本开始支持)
paramItem - matchStrategy参数值的匹配策略,目前支持精确匹配(PARAM_MATCH_STRATEGY_EXACT)、子串匹配(PARAM_MATCH_STRATEGY_CONTAINS)和正则匹配(PARAM_MATCH_STRATEGY_REGEX)。(1.6.2 版本开始支持)

用户可以通过 GatewayRuleManager.loadRules(rules) 手动加载网关规则,或通过 GatewayRuleManager.register2Property(property) 注册动态规则源动态推送(推荐方式)。

2.7 网关高可用

为了保证 Gateway 的高可用性,可以同时启动多个 Gateway 实例进行负载,在 Gateway 的上游使用 Nginx 或者 F5 进行负载转发以达到高可用。
在这里插入图片描述
在这里插入图片描述

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值