Spring Cloud Alibaba 系列之 Gateway(网关)

这篇具有很好参考价值的文章主要介绍了Spring Cloud Alibaba 系列之 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 的方式提供网关的基本功能,例如说安全认证、监控、限流等等。

一、Gateway 介绍

2.1 核心概念

1.路由:网关的基本构建组成,表示一个具体的路由信息载体。它由 ID,目标 URI,谓词集合和过滤器集合定义

2.谓词/断言:Java 8 函数谓词,输入类型是 Spring Framework ServerWebExchange,可以匹配 HTTP 请求中的所有内容,例如请求头或参数

3.过滤器:使用特定工厂构造的 Spring Framework GatewayFilter 实例,可以在发送给下游请求之前或之后修改请求和响应

2.2 执行流程

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot


 执行流程大体如下:

  1. Gateway Client 向 Gateway Server 发送请求
  2. 请求首先会被 HttpWebHandlerAdapter 进行提取组装成网关上下文
  3. 然后网关的上下文会传递到 DispatcherHandler,它负责将请求分发给 RoutePredicateHandlerMapping
  4. RoutePredicateHandlerMapping 负责路由查找,并根据路由断言判断路由是否可用
  5. 如果过断言成功,由 FilteringWebHandler 创建过滤器链并调用
  6. 请求会一次经过 PreFilter -> 微服务 -> PostFilter 的方法,最终返回响应

三、环境搭建

为了更好的理解上边提到核心概念,我们现用简单的实战案例演示

项目名称 端口 描述
gateway-test - pom 项目,父工厂
user-service 9001 用户微服务,服务注册到 nacos
gateway-service 9002 网关微服务,服务注册到 nacos

注意:搭建项目启动前,必须先开启 Nacos 服务。

3.1 搭建 gateway-test 项目

该工程为 pom 项目,只需要添加如下依赖:

<parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>
    <dependencyManagement>
        <dependencies>
            <!-- spring cloud alibaba 依赖-->
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>2.2.5.RELEASE</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
            <!-- spring cloud 依赖 -->
            <dependency>
                <groupId>org.springframework.cloud</groupId>
                <artifactId>spring-cloud-dependencies</artifactId>
                <version>Hoxton.SR8</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>


        </dependencies>
    </dependencyManagement>

3.2 搭建 user-service 项目

该项目为用户微服务,模拟提供用户相关接口。

1.添加依赖:

<dependencies>
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-web</artifactId>
    </dependency>
 
    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
 
    <dependency>
        <groupId>com.alibaba.cloud</groupId>
        <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
    </dependency>
 
    <dependency>
        <groupId>org.projectlombok</groupId>
        <artifactId>lombok</artifactId>
        <optional>true</optional>
    </dependency>
</dependencies>

2. 配置文件(application.yml):


server:
  port: 9001
 
spring:
  application:
    name: user-service
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos

3.业务类:

@Data
@AllArgsConstructor
public class User {
 
    private Integer id;
 
    private String name;
}
@RestController
@RequestMapping("/user")
public class UserController {
 
    private static Map<Integer, User> userMap;
 
    static {
        userMap = new HashMap<>();
        userMap.put(1, new User(1, "张三"));
        userMap.put(2, new User(2, "李四"));
        userMap.put(3, new User(3, "王五"));
    }
 
    @RequestMapping("/findById/{id}")
    public User findById(@PathVariable("id") Integer id) {
        // 为了测试方便,用此方式模拟用户查询
        return userMap.get(id);
    }
}

4.启动类

@SpringBootApplication
@EnableDiscoveryClient
public class UserApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(UserApplication.class, args);
    }
}

启动用户微服务,浏览器输入:localhost:9001/user/findById/1,结果如下图:

   nacos添加了一个服务

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 页面如图:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 用户微服务正常

3.3 搭建 gateway-service 项目

该服务提供网关功能,核心就是配置路由规则

1.添加依赖:

<dependencies>
    <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>
</dependencies>

2.配置文件(application.yml):

server:
  port: 9090
 
spring:
  application:
    name: gateway-service
 
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
    gateway:
      discovery:
        locator:
          enabled: true # gateway 可以从 nacos 发现微服务

我们暂不配置路由规则

3.启动类:

@EnableDiscoveryClient
@SpringBootApplication
public class GatewayApplication {
 
    public static void main(String[] args) {
        SpringApplication.run(GatewayApplication.class, args);
    }
}

启动网关项目,我们试着通过网关请求用户微服务接口。

请求规则:网关地址/微服务应用名/接口

我们在浏览器输入:localhost:9090/user-service/user/findById/2,结果如下图:

nacos如下:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 页面如下:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 请求成功,网关项目搭建完成。

使用路由规则:

server:
  port: 9090
 
spring:
  application:
    name: gateway-service
 
  cloud:
    nacos:
      discovery:
        server-addr: 127.0.0.1:8848
        username: nacos
        password: nacos
    gateway:
      discovery:
        locator:
          enabled: true # gateway 可以从 nacos 发现微服务
      routes:
        - id: user_service_route  # 路由 id,确定唯一加尔肯
          uri: lb://user-service  # lb 表示从 nacos 中按照名称获取微服务,并遵循负载均衡策略,user-service 对应用户微服务应用名
          predicates:
            - Path=/user-api/**  # 使用断言
          filters:
            - StripPrefix=1       # 使用过滤器 

其中:

  • id: 路由标识符,区别于其他 Route
  • uri:路由指向的目的地 uri,即客户端请求最终被转发到的微服务
  • predicate:断言,用于条件判断,只有断言都返回真,才会真正的执行路由
  • filter:过滤器用于修改请求和响应信息

添加 routes 相关配置,重启网关项目,请求用户微服务接口。

请求规则:网关地址/断言配置的 Path 路径/接口

我们在浏览器输入:localhost:9090/user-api/user/findById/3,结果如下图:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 

路由规则生效。

简单的使用了路由规则,下文将具体介绍路由规则的使用方式。

 四、断言

Predicate(断言, 谓词) 用于进行条件判断,只有断言都返回真,才会真正的执行路由。

SpringCloud Gateway 的断言通过继承 AbstractRoutePredicateFactory 类实现,因此我们可以根据自己的需求自定义断言。

当然,开发团队已为使用者提供了一些内置断言工厂,在开发中已足够使用,请继续阅读下文。

4.1 内置断言

Spring Cloud Gateway 包括 11 种内置的断言工厂,所有这些断言都与 HTTP 请求的不同属性匹配。

补充:断言可以同时使用

  1. AfterRoutePredicateFactory:接收一个日期参数,判断请求日期是否晚于指定日期
  2. BeforeRoutePredicateFactory:接收一个日期参数,判断请求日期是否早于指定日期
  3. BetweenRoutePredicateFactory:接收两个日期参数,判断请求日期是否在指定时间段内

上边三个断言工厂都是根据时间判断。使用方式如下:

案例一(晚于指定日期)

cloud:
    gateway:
      routes:
        - id: order_route    #路由的唯一标识
          uri: lb://order-seata
          predicates:
            - After=2017-01-20T17:42:47.789-07:00[America/Denver]

案例二(早于指定日期)

cloud:
    gateway:
      routes:
        - id: order_route    #路由的唯一标识
          uri: lb://order-seata
          predicates:
            - Before=2017-01-20T17:42:47.789-07:00[America/Denver]

案例三(指定日期之间)

cloud:
    gateway:
      routes:
        - id: order_route    #路由的唯一标识
          uri: lb://order-seata
          predicates:
            - Between=2017-01-20T17:42:47.789-07:00[America/Denver], 2017-01-21T17:42:47.789-07:00[America/Denver]

4.2、基于远程地址

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

- RemoteAddr=192.168.1.1/24

其中,192.168.0.1 是 IP 地址,而 24 是子网掩码。当请求的远程地址为该值时,匹配路由。

4.3、基于Cookie

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

其中,chocolate为 cookie 名称,ch.p为 cookie 值。

- Cookie=chocolate, ch.p

 4.4、基于Header

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

其中,X-Request-Id 为 header 名称,\d+ 为正则表达式,表示数字。

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

4.5、基于Method请求方法

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

如果请求方法是 GET 或 POST,则此路由匹配

- Method=GET,POST

 4.6、基于Path匹配请求路径,

PathRoutePredicateFactory:接收一个参数,判断请求的 URI 部分是否满足路径规则。

predicates:
    - Path=/user-api/**

这个就是我们在上边配置的断言,请求是 /user-api/ 开头,则路由到用户微服务上。

4.7、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

配置多组路由规则时使用。路由会将约 80% 的流量转发至 weighthigh.org,并将约 20% 的流量转发至 weightlow.org。

4.8、QueryRoutePredicateFactory:接收两个参数,请求 param 和正则表达式, 判断请求参数是否具有给定名称且值与正则表达式匹配。

predicates:
    - Query=cardId, \d+

请求包含名称为 cardId 的参数,且参数值为数字,则匹配路由。

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

predicates:
    - Host=**.somehost.org,**.anotherhost.org

支持 URI 模板变量(例如{sub} .myhost.org),如果请求的主机标头的值为w w w.somehost.org或 beta.somehost.org 或 w w w.anotherhost.org,则此路由匹配

4.10 自定义断言

当内置的断言不满足我们的业务需求时,我们可以自定义断言工厂。

比如,我们需要判断请求 url 中传过来的 age 值在 18~60 范围才可正常路由。

1. 配置断言:

predicates:
    - Age=18, 60

2. 我们需要创建一个类继承 AbstractRoutePredicateFactory 类:

注意:自定义类名有格式要求-> 断言名称 + RoutePredicateFactory。此处断言名称为 Age,对应配置文件中的 Age。

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

@Component
public class AgeRoutePredicateFactory extends AbstractRoutePredicateFactory<AgeRoutePredicateFactory.Config> {
 
    public AgeRoutePredicateFactory() {
        super(AgeRoutePredicateFactory.Config.class);
    }
 
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("minAge", "maxAge");
    }
 
    @Override
    public Predicate<ServerWebExchange> apply(Config config) {
        return new Predicate<ServerWebExchange>() {
            @Override
            public boolean test(ServerWebExchange serverWebExchange) {
                // 判断逻辑
                String ageStr = serverWebExchange.getRequest().getQueryParams().getFirst("age");
                if (ageStr == null || ageStr.length() == 0) {
                    return false;
                }
 
                int age = Integer.parseInt(ageStr);
                return age > config.getMinAge() && age < config.getMaxAge();
            }
        };
    }
 
    @Data
    static class Config {
        private int minAge;
        private int maxAge;
    }
}

要求:

必须spring组件bean
类必须加上RoutePredicateFactory作为结尾
继承AbstractRoutePredicateFactory
必须声明静态内部类,声明属性来接受 配置文件中的信息
需要结合shortcutFieldOrder来进行绑定
通过apply进行逻辑编写,true就是匹配成功,false就是匹配失败

五、过滤器

路由过滤器允许以某种方式修改传入的 HTTP 请求或传出的 HTTP 响应。

在 Gateway 中, Filter 的生命周期只有两个: “pre” 和 “post”。

  1. PRE:这种过滤器在请求被路由之前调用。我们可利用这种过滤器实现身份验证、在集群中选择请求的微服务、记录调试信息等
  2. POST:这种过滤器在路由到微服务以后执行。这种过滤器可用来为响应添加标准的 HTTP Header、收集统计信息和指标、将响应从微服务发送给客户端等。

根据 Filter 的作用范围可以分成两种:GatewayFilter 与 GlobalFilter

  • GatewayFilter:应用到单个路由或者一个分组的路由上

  • GlobalFilter:应用到所有的路由上。

5.1 局部过滤器

局部过滤器是针对单个路由的过滤器。

Spring Cloud Gateway 也提供了 31 种局部的内置 GatewayFilter 工厂。

由于数量较多,笔者只列举部分内置局部过滤器进行展示。

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 使用方式:

spring:
    gateway:
      discovery:
        locator:
          enabled: true # gateway 可以从 nacos 发现微服务
      routes:
        - id: user_service_route  
          uri: lb://user-service  
          predicates:
            - Path=/user-api/**  # 使用断言
          filters:
            - StripPrefix=1 
            - SetStatus=2000  # 修改返回状态

同样地,当内置的局部过滤器不符合我们的业务需求时,我们也可以自定义过滤器。

比如:我们需要在调用/路由一个接口之前打印一下日志。

1. 配置局部过滤器

filters:
    - Log=true

2. 创建一个类继承 AbstractGatewayFilterFactory 类:

注意:自定义类名有格式要求-> 过滤器名称 + GatewayFilterFactory。此处过滤器名称为 Log,对应配置文件中的 Log。

@Component
public class LogGatewayFilterFactory extends AbstractGatewayFilterFactory<LogGatewayFilterFactory.Config> {
 
 
    public LogGatewayFilterFactory() {
        super(LogGatewayFilterFactory.Config.class);
    }
 
    @Override
    public List<String> shortcutFieldOrder() {
        return Arrays.asList("open");
    }
 
    @Override
    public GatewayFilter apply(Config config) {
        return new GatewayFilter() {
            @Override
            public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
                if (config.open) {
                    // 过滤器逻辑处理
                    System.out.println("====开启日志====");
                }
 
                return chain.filter(exchange);
            }
        };
    }
 
    @Data
    static class Config {
        private boolean open;
    }
}

3. 保存,重启网关项目,测试结果如下:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 5.2 全局过滤器

全局过滤器作用于所有路由, 无需配置。通过全局过滤器可以实现对权限的统一校验,安全性验证等功能。

同样地,框架也内置了一些全局过滤器,它们都实现 GlobalFilter 和 Ordered 接口。有兴趣的读者可以自行查看 GlobalFilter 的实现类或浏览下文提供的官方文档获取详细信息。

这里我们主要演示自定义全局过滤器。

比如:我们在接受请求时需要验证 token。

由于是全局过滤器,因此无需修改配置文件,需要定义类实现 GlobalFilter 和 Ordered 接口。
 

@Component
public class TokenGlobalFilter implements GlobalFilter, Ordered {
 
    @SneakyThrows
    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        String token = exchange.getRequest().getQueryParams().getFirst("token");
        if (token == null || token.length() == 0 || !token.equals("123456")) {
            System.out.println("鉴权失败");
            ServerHttpResponse response = exchange.getResponse();
 
            response.setStatusCode(HttpStatus.OK);
            response.getHeaders().add("Content-Type", "application/json;charset=UTF-8");
 
            // 鉴权失败,返回的数据结构
            Map<String, Object> map = new HashMap<>();
            map.put("code", HttpStatus.UNAUTHORIZED.value());
            map.put("message", HttpStatus.UNAUTHORIZED.getReasonPhrase());
 
            DataBuffer buffer = response.bufferFactory().wrap(new ObjectMapper().writeValueAsBytes(map));
            return response.writeWith(Flux.just(buffer));
        }
 
        return chain.filter(exchange);
    }
 
    @Override
    public int getOrder() {
        return 0;
    }
}

保存,重启网关项目,测试结果如下:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 token 验证失败,返回 401,鉴权失败的提示;token 验证成功,返回接口结果。

六、 路由失败处理

当请求路由地址不匹配或断言为 false 时,Gateway 会默认返回 Whitelabel Error Page 错误页面,这种错误提示不符合我们业务需求。

1. 我们可以自定义返回一个较为友好的错误提示,需要创建一个类继承 DefaultErrorWebExceptionHandler 类,重写其方法:
 

public class MyErrorWebExceptionHandler extends DefaultErrorWebExceptionHandler {
 
    public MyErrorWebExceptionHandler(ErrorAttributes errorAttributes,
                                      ResourceProperties resourceProperties,
                                      ErrorProperties errorProperties,
                                      ApplicationContext applicationContext) {
        super(errorAttributes, resourceProperties, errorProperties, applicationContext);
    }
 
    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }
 
    @Override
    protected Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        boolean includeStackTrace = isIncludeStackTrace(request, MediaType.ALL);
        Map<String, Object> errorMap = getErrorAttributes(request, includeStackTrace);
        int status = Integer.valueOf(errorMap.get("status").toString());
        Map<String, Object> response = this.response(status, errorMap.get("error").toString(), errorMap);
        return ServerResponse.status(status).contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(response));
    }
 
    // 我们希望返回的数据结构
    public static Map<String, Object> response(int status, String errorMessage, Map<String, Object> errorMap) {
        Map<String, Object> map = new HashMap<>();
        map.put("code", status);
        map.put("message", errorMessage);
        map.put("data", errorMap);
        return map;
    }
}

2. 配置 Bean 实例:

@Configuration
public class GatewayConfiguration {
 
    private final ServerProperties serverProperties;
 
    private final ApplicationContext applicationContext;
 
    private final ResourceProperties resourceProperties;
 
    private final List<ViewResolver> viewResolvers;
 
    private final ServerCodecConfigurer serverCodecConfigurer;
 
    public GatewayConfiguration(ServerProperties serverProperties,
                                ApplicationContext applicationContext,
                                ResourceProperties resourceProperties,
                                ObjectProvider<List<ViewResolver>> viewResolversProvider,
                                ServerCodecConfigurer serverCodecConfigurer) {
        this.serverProperties = serverProperties;
        this.applicationContext = applicationContext;
        this.resourceProperties = resourceProperties;
        this.viewResolvers = viewResolversProvider.getIfAvailable(Collections::emptyList);
        this.serverCodecConfigurer = serverCodecConfigurer;
    }
 
 
    @Bean("myErrorWebExceptionHandler")
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public ErrorWebExceptionHandler myErrorWebExceptionHandler(ErrorAttributes errorAttributes) {
 
        MyErrorWebExceptionHandler exceptionHandler = new MyErrorWebExceptionHandler(
                errorAttributes,
                this.resourceProperties,
                this.serverProperties.getError(),
                this.applicationContext);
 
        exceptionHandler.setViewResolvers(this.viewResolvers);
        exceptionHandler.setMessageWriters(this.serverCodecConfigurer.getWriters());
        exceptionHandler.setMessageReaders(this.serverCodecConfigurer.getReaders());
        return exceptionHandler;
    }
}

3. 保存后重启网关项目,请求一个错误的接口地址,结果如下:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

 七、跨域问题

针对 PC 端的页面请求,如果项目前后端分离,则请求会出现跨域请求问题。为什么呢?接着看。

URL 由协议、域名、端口和路径组成,如果两个 URL 的协议、域名和端口相同,则表示它们同源,否则反之。

浏览器提供同源策略,限制了来自不同源的 document 或脚本,对当前 document 读取或设置某些属性。其目的是为了保证用户信息的安全,防止恶意的网站窃取数据。

下面笔者演示跨域问题,编写一个简单页面:

<!DOCTYPE html>
<html lang="zh">
<head>
    <meta charset="UTF-8">
</head>
<body>
    <button id="sendBtn">发送请求</button>
    <script src="jquery.min.js"></script>
    <script type="text/javascript">
        $(function() {
            $("#sendBtn").on("click", function() {
                $.ajax({
                    type: "GET",
                    url: "http://localhost:9090/user-api/user/findById/3?token=123456",
                    success: function(resp) {
                        console.log(resp);
                    }
                })
            });
        });
    </script>
</body>
</html>

启动一个服务容器(笔者采用 sublime 的插件),分配了 10800 端口,请求结果如下:

springcloudalibaba gateway,spring cloud Alibaba,java,spring cloud,gateway,spring boot

由于请求端的端口与网关端口不一致,不是同源,因此出现跨域问题。

解决方案有两种,如下:

方式一:修改配置文件

spring:
  cloud:
    gateway:
      globalcors:
        cors-configurations:
          '[/**]':
              allowedOrigins: "*"
              allowedMethods: "*"
              allowedHeaders: "*"

方式二:配置 CorsWebFilter 过滤器

@Configuration
public class CorsConfig {
    @Bean
    public CorsWebFilter corsFilter() {
        CorsConfiguration config = new CorsConfiguration();
        config.addAllowedMethod("*");
        config.addAllowedOrigin("*");
        config.addAllowedHeader("*");
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(new PathPatternParser());
        source.registerCorsConfiguration("/**", config);
        return new CorsWebFilter(source);
    }
}

八、整合 Sentinel

网关作为微服务,我们也可以对其进行限流和降级操作

注意:配置前记得启动 Sentinel 控制台。

8.1 基础整合

1. 添加依赖:

<dependency>
    <groupId>com.alibaba.cloud</groupId>
    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
</dependency>
    
<dependency>
    <groupId>com.alibaba.csp</groupId>
    <artifactId>sentinel-spring-cloud-gateway-adapter</artifactId>
</dependency>
复制代码

2. 修改配置文件,连接 Sentinel 控制台:

spring:
  cloud:
    sentinel:
      transport:
        port: 8719
        dashboard: localhost:8081
复制代码

3. 配置 Sentinel Filter 实例

@Configuration
public class GatewayConfiguration {
 
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
}
复制代码

最后,重启网关微服务,在 Sentinel 控制台查看或配置规则即可

8.2 异常处理器

在 Sentinel 控制台配置规后,服务出现限流或降级时,我们需要服务端返回友好的异常信息,而不是一个简单的错误页面。

我们需要配置 BlockRequestHandler 实例。

@Configuration
public class GatewayConfiguration {
 
    @Bean
    @Order(-1)
    public GlobalFilter sentinelGatewayFilter() {
        return new SentinelGatewayFilter();
    }
 
    @Bean(name = "myBlockRequestHandler")
    public BlockRequestHandler myBlockRequestHandler() {
        BlockRequestHandler blockRequestHandler = new BlockRequestHandler() {
            @SneakyThrows
            @Override
            public Mono<ServerResponse> handleRequest(ServerWebExchange serverWebExchange, Throwable throwable) {
 
                Result result;
                if (throwable instanceof FlowException) {
                    result = Result.builder().code(100).msg("接口限流了").build();
 
                } else if (throwable instanceof DegradeException) {
                    result = Result.builder().code(101).msg("服务降级了").build();
 
                } else if (throwable instanceof ParamFlowException) {
                    result = Result.builder().code(102).msg("热点参数限流了").build();
 
                } else if (throwable instanceof SystemBlockException) {
                    result = Result.builder().code(103).msg("触发系统保护规则").build();
 
                } else if (throwable instanceof AuthorityException) {
                    result = Result.builder().code(104).msg("授权规则不通过").build();
                } else {
                    result = Result.builder().code(105).msg("sentinel 未知异常").build();
                }
 
                return ServerResponse.status(HttpStatus.BAD_GATEWAY)
                        .contentType(MediaType.APPLICATION_JSON)
                        .body(BodyInserters.fromValue(new ObjectMapper().writeValueAsString(result)));
            }
        };
        return blockRequestHandler;
    }
 
    @Bean
    @Order(Ordered.HIGHEST_PRECEDENCE)
    public SentinelGatewayBlockExceptionHandler sentinelGatewayBlockExceptionHandler(BlockRequestHandler myBlockRequestHandler) {
    
        //重定向bloack处理
        //GatewayCallbackManager.setBlockHandler(new RedirectBlockRequestHandler("https://www.extlight.com"));
        
        //自定义bloack处理
        GatewayCallbackManager.setBlockHandler(myBlockRequestHandler);
        return new SentinelGatewayBlockExceptionHandler(viewResolvers, serverCodecConfigurer);
    }
}
@Data
@Builder
public class Result {
 
    private int code;
 
    private String msg;
}

注意:当多个 Bean 上都配置 @Order 注解时,要多留意 order 值,否则接口请求后达不到预期效果文章来源地址https://www.toymoban.com/news/detail-695927.html

到了这里,关于Spring Cloud Alibaba 系列之 Gateway(网关)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Spring Cloud 之 Gateway 网关

    🍓 简介:java系列技术分享(👉持续更新中…🔥) 🍓 初衷:一起学习、一起进步、坚持不懈 🍓 如果文章内容有误与您的想法不一致,欢迎大家在评论区指正🙏 🍓 希望这篇文章对你有所帮助,欢迎点赞 👍 收藏 ⭐留言 📝 🍓 更多文章请点击 Gateway官网 :https://spring.io/projects/

    2024年02月16日
    浏览(35)
  • spring cloud gateway网关(一)之网关路由

    1、gateway相关介绍 在微服务架构中,系统往往由多个微服务组成,而这些服务可能部署在不同机房、不同地区、不同域名下。这种情况下,客户端(例如浏览器、手机、软件工具等)想要直接请求这些服务,就需要知道它们具体的地址信息,例如 IP 地址、端口号等。这种客户

    2024年02月08日
    浏览(41)
  • Spring cloud教程Gateway服务网关

    写在前面的话: 本笔记在参考网上视频以及博客的基础上,只做个人学习笔记,如有侵权,请联系删除,谢谢! Spring Cloud Gateway 是 Spring Cloud 的一个全新项目,该项目是基于 Spring 5.0,Spring Boot 2.0 和 Project Reactor 等响应式编程和事件流技术开发的网关,它旨在为微服务架构提

    2024年02月08日
    浏览(42)
  • Gateway网关组件(在Spring Cloud整合Gateway(idea19版本))

            Spring Cloud Gateway官网:Spring Cloud Gateway         局域网中就有网关这个概念,局域网接收数据或发送数据都要通过网关,比如使用VMware虚拟机软件搭建虚拟机集群的时候,往往我们需要选择IP段中的⼀个IP作为网关地址,网关可以对请求进行控制,提升我们系统的安

    2024年02月16日
    浏览(41)
  • Spring Cloud第二季--服务网关Gateway

    Spring Cloud Gateway是在Spring生态系统之上构建的API网关服务,基于Spring 5,Spring Boot 2和 Project Reactor等技术。 Gateway 使用的Webflux中的reactor-netty响应式编程组件,底层使用了 Netty 通讯框架。Spring Cloud Gateway能干嘛呢? Gateway是原zuul1.x版的替代。 Spring Cloud Gateway 与 Zuul的区别: Zuu

    2024年02月03日
    浏览(48)
  • spring cloud gateway网关和链路监控

    文章目录 目录 文章目录 前言 一、网关 1.1 gateway介绍 1.2 如何使用gateway  1.3 网关优化 1.4自定义断言和过滤器 1.4.1 自定义断言 二、Sleuth--链路追踪 2.1 链路追踪介绍 2.2 Sleuth介绍 2.3 使用 2.4 Zipkin的集成  2.5 使用可视化zipkin来监控微服务 总结 所谓的API网关,就是指系统的 统

    2024年02月02日
    浏览(38)
  • Spring Cloud之API网关(Gateway)

    目录 API网关 好处 解决方案 Gateway 简介 特征 核心概念 Route(路由) Predicate(断言) Filter(过滤器) 工作流程 Route(路由) 路由配置方式 1.yml配置文件路由 2.bean进行配置 3.动态路由 动态路由 Predicate(断言) 特点 常见断言 示例 Filter(过滤器) filter分类 Pre 类型 Post 类型 网关过滤器 格式

    2024年02月08日
    浏览(49)
  • Spring Cloud Gateway 网关实现白名单功能

    1 摘要 对于微服务后台而言,网关层作为所有网络请求的入口。一般基于安全考虑,会在网关层做权限认证,但是对于一些例如登录、注册等接口以及一些资源数据,这些是不需要有认证信息,因此需要在网关层设计一个白名单的功能。本文将基于 Spring Cloud Gateway 2.X 实现白

    2023年04月08日
    浏览(42)
  • Spring Cloud GateWay 网关的相关面试题

    Spring Cloud GateWay如何实现限流? 1.Spring Cloud GateWay使用令牌桶算法实现限流(Nginx使用漏桶算法实现限流 ) 2.Spring Cloud GateWay默认使用Redis 的RateLimter限流算法来实现,所以需要引入Redis依赖 3.使用的过程中,主要配置 令牌桶填充的速率,令牌桶容量,指定限流的key 4.限流的Ke

    2024年02月13日
    浏览(54)
  • Spring Cloud Gateway 监控、多网关实例路由共享 | Spring Cloud 18

    Actuator 是 Spring Boot 提供的用来对应用系统进行监控的功能模块,借助于 Actuator 开发者可以很方便地对应用系统某些监控指标进行查看、统计等。 Actuator 的核心是端点 Endpoint 。 Endpoint 可以让我们监视应用程序并与其交互。 Spring Boot 包含许多内置端点,并允许您添加自己的端

    2024年02月09日
    浏览(56)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包