Spring Cloud Gateway2之路由详解

news2025/1/10 17:35:25

Spring Cloud Gateway路由

文章目录

  • 1. 前言
  • 2. Gateway路由的基本概念
  • 3. 三种路由
    • 1. 静态路由
    • 2. 动态路由
      • 1. 利用外部存储
      • 2. API动态路由
    • 3. 服务发现路由(自动路由)
      • 3.1. 配置方式
      • 3.2 自动路由(服务发现)原理
        • 核心源码
          • GatewayDiscoveryClientAutoConfiguration
          • `DiscoveryClientRouteDefinitionLocator`核心方法`getRouteDefinitions`
  • 4. Gateway路由的核心组件
    • 4.1 路由断言
      • 组合并生效相当于并且的关系
      • 如果要或的关系需要分配多个路由
    • 4.2 路由过滤器
      • 路由过滤器概念
      • 路由过滤器工厂
      • 内置路由过滤器
      • 自定义路由过滤器
  • 5. 路由的动态刷新
    • 5.1. 添加依赖
    • 5.2. 配置application.yml
    • 5.3. 创建Config Server
    • 5.4. 在远程Git仓库中添加配置文件
    • 5.5. 启动Gateway服务
    • 5.6. 动态刷新路由信息
  • 6. 处理路由失败和异常
      • 6.1 处理请求失败和异常
      • 6.2 配置回退和重试策略
  • 7. 高级路由功能
  • 8. 参考文档

1. 前言

其实Spring Cloud Gateway2.x 的官网文档写的已经很全面了,如果想要了解,可以访问 《Spring Cloud Gateway》

在这里插入图片描述

Spring Cloud Gateway是一个基于Spring Boot构建的API网关,主要用于微服务架构中。它提供了一种简单而高效的方式来对请求进行路由、过滤和转发,从而实现对服务的统一访问入口和流量管理。Spring Cloud Gateway通过一个高度可配置的路由规则集,支持各种复杂的请求处理场景。

与传统的API网关(如Zuul)相比,Spring Cloud Gateway提供了更好的性能和丰富的功能。它基于响应式编程模型(Reactive)和Spring WebFlux框架构建,能够提供非阻塞的异步I/O处理,并且能够很好地与其他Spring Cloud组件进行集成。此外,Spring Cloud Gateway还提供了很多现成的功能,如负载均衡、熔断、限流等,可以帮助开发者快速搭建一个完善的API网关。
在这里插入图片描述
图片来自Spring Cloud Gateway2 官网 https://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html

2. Gateway路由的基本概念

  • 2.1 路由的定义和作用
    路由是一个HTTP请求的处理过程。在Spring Cloud Gateway中,每个请求都会经过一系列的过滤器,最后转发到目标URI。

路由配置由三部分组成:ID、目标URI、一系列的断言和过滤器。ID是路由的唯一标识,目标URI是请求的最终目的地,断言是用来匹配HTTP请求的规则,过滤器是用来处理HTTP请求的组件。

在Spring Cloud Gateway中配置路由有两种方式:通过配置文件和通过代码。

  1. 通过配置文件配置路由:

在application.yml文件中添加如下配置:

spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8080
        predicates:
        - Path=/user/**

以上面的配置为例,id为user_route的路由会匹配所有路径为/user/的请求,然后将其转发到http://localhost:8080。

  1. 通过代码配置路由:

在配置类中添加如下配置:

@Bean
public RouteLocator customRouteLocator(RouteLocatorBuilder builder) {
    return builder.routes()
        .route("user_route", r -> r.path("/user/**")
            .uri("http://localhost:8080"))
        .build();
}

以上面的配置为例,id为user_route的路由会匹配所有路径为/user/的请求,然后将其转发到http://localhost:8080。

  • 2.2 如何在Spring Cloud Gateway中配置路由

3. 三种路由

根据路由的创建方式和使用场景,可以将路由分类为以下三种:

1. 静态路由

在Spring Cloud Gateway启动时,通过配置文件或Java代码定义的路由规则。这些路由规则在运行时是不可修改的。
可以通过配置文件定义静态路由:这段配置会将所有以/user开始的请求转发到http://localhost:8080。

spring:
  cloud:
    gateway:
      routes:
      - id: user_route
        uri: http://localhost:8080
        predicates:
        - Path=/user/**

Spring Cloud Gateway的静态路由实现的原理主要通过Reactive模式下的Netty处理方式,以及Project Reactor中的Flux和Mono模型来处理并发请求。此外,它还使用了Spring 5的核心Webflux框架进行路由分发。

具体来说,当请求到来时,Spring Cloud Gateway会根据配置文件中的静态路由信息,将请求转发到相应的目标地址。

核心的代码主要集中在RoutePredicateHandlerMapping和FilteringWebHandler类中。

// RoutePredicateHandlerMapping用于处理请求的映射关系
public class RoutePredicateHandlerMapping extends AbstractHandlerMapping {

    public RoutePredicateHandlerMapping(GatewayProperties properties, 
            RouteLocator routeLocator, ProxyExchangeArgumentResolver 
            proxyExchangeArgumentResolver, ServerCodecConfigurer 
            serverCodecConfigurer, Environment environment) {
        ...
    }
    
    ...
}

// FilteringWebHandler用于处理各种过滤器
public class FilteringWebHandler implements WebHandler {
    private final List<GlobalFilter> globalFilters;

    public FilteringWebHandler(List<GlobalFilter> globalFilters) {
        this.globalFilters = globalFilters;
    }

    ...
}

实际上,配置静态路由的主要工作量在于编写配置文件和理解路由断言,Spring Cloud Gateway已经为处理了复杂的路由转发和过滤器操作。

具体的路由匹配和转发过程则由Spring Cloud Gateway框架自动处理。在处理请求时,Gateway会依次经过负载均衡器、过滤器链和路由断言的处理,并最终将请求转发到配置的目标地址。

2. 动态路由

动态路由通常可以理解为两种方式,一种通过外部配置存储动态更新网关路由,另一种是通过API 的方式动态新增修改删除网关路由。

1. 利用外部存储

动态路由与静态路由的主要区别在于,动态路由允许在运行时更新路由配置,而不需要重启应用。Spring Cloud Gateway支持通过配置中心(如Spring Cloud Config)。

实现动态路由的原理主要是通过监听配置中心的变化,当配置发生变化时,使用Event机制触发路由信息的更新。Spring Cloud Gateway会自动处理新的路由配置并更新其内部的路由表。

核心代码主要集中在以下几个类中:
当配置中心发生更改时,RouteRefreshListener会监听到相关事件,触发RefreshRoutesEvent事件,从而使CachingRouteLocator更新路由信息。这样,Spring Cloud Gateway就可以在运行时动态地处理新的路由配置。

  1. CachingRouteLocator:它负责缓存和管理路由信息,同时也会处理路由信息的更新。
public abstract class CachingRouteLocator implements RouteLocator, 
        ApplicationListener<RefreshRoutesEvent> {

    public abstract Flux<Route> fetch();

    @Override
    public void onApplicationEvent(RefreshRoutesEvent event) {
        // 当收到一个RefreshRoutesEvent事件时,将会触发路由信息的更新
        this.resetRoutes().subscribe();
    }

    ...
}
  1. RouteDefinitionRouteLocator:它继承自CachingRouteLocator,并负责从RouteDefinitionLocator中获取路由定义,然后根据路由定义创建路由。
public class RouteDefinitionRouteLocator extends CachingRouteLocator {

    public RouteDefinitionRouteLocator(RouteDefinitionLocator routeDefinitionLocator, 
            List<GatewayFilterFactory> gatewayFilters, List<RoutePredicateFactory> 
            routePredicates, RouteDefinitionHandlerFilter routeDefinitionHandlerFilter, 
            ConfigurationService configurationService, GatewayProperties properties) {
        ...
    }

    ...
}
  1. RouteRefreshListener:它负责监听配置中心的变化,并在检测到更改时发布一个RefreshRoutesEvent事件。
@Configuration
public class RouteRefreshListener {
    @Bean
    public ApplicationListener<?> routeChangeListener(RouteDefinitionRouteLocator 
            routeDefinitionRouteLocator) {
        return new ApplicationListener<RefreshRoutesResultEvent>() {
            @Override
            public void onApplicationEvent(RefreshRoutesResultEvent event) {
                routeDefinitionRouteLocator.onApplicationEvent(new RefreshRoutesEvent(this));
            }
        };
    }
}

要实现动态路由,还需要在配置文件中配置相应的配置中心,例如使用Spring Cloud Config:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: dynamic_route
          uri: lb://service-id
          predicates:
            - Path=/dynamic_path/**
      config:
        uri: http://config-server-uri
        label: master
        name: gateway

2. API动态路由

在Spring Cloud Gateway运行时,通过调用API动态创建和修改的路由规则。这种路由规则可以根据需要进行实时的修改。动态路由通常需要通过编程方式创建。

在Spring Cloud Gateway中,可以通过调用Gateway API来动态地创建和修改路由规则。这些路由规则可以在运行时进行实时修改。要实现这一功能,需要使用RouteDefinitionWriter接口,它提供了添加、删除和更新路由定义的方法。

Spring Cloud Gateway中动态地创建和修改路由规则

依赖

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

创建一个RouteController来处理动态路由的创建、更新和删除操作

@RestController
@RequestMapping("/route")
public class RouteController {

    @Autowired
    private RouteDefinitionWriter routeDefinitionWriter;

    @Autowired
    private RouteDefinitionLocator routeDefinitionLocator;

    @PostMapping
    public Mono<Void> add(@RequestBody RouteDefinition routeDefinition) {
        return routeDefinitionWriter.save(Mono.just(routeDefinition)).then();
    }

    @PutMapping
    public Mono<Void> update(@RequestBody RouteDefinition routeDefinition) {
        return routeDefinitionWriter.delete(Mono.just(routeDefinition.getId()))
                .then(routeDefinitionWriter.save(Mono.just(routeDefinition)))
                .then();
    }

    @DeleteMapping("/{id}")
    public Mono<Void> delete(@PathVariable String id) {
        return routeDefinitionWriter.delete(Mono.just(id));
    }

    @GetMapping
    public Flux<RouteDefinition> getRoutes() {
        return routeDefinitionLocator.getRouteDefinitions();
    }
}

在这个例子中,使用RouteDefinitionWriter保存、更新和删除路由定义。当定义新的路由规则或者更新现有的路由规则时,会自动触发路由表的刷新。

现在就可以使用HTTP API来动态地创建、修改和删除路由规则了:

  1. 添加一个新的路由规则:

    POST /route
    {
        "id": "my_route",
        "uri": "http://example.com",
        "predicates": [
            {
                "name": "Path",
                "args": {
                    "pattern": "/mypath/**"
                }
            }
        ],
        "filters": [
            {
                "name": "RewritePath",
                "args": {
                    "regexp": "/mypath/(?<segment>.*)",
                    "replacement": "/${segment}"
                }
            }
        ]
    }
    
  2. 更新现有的路由规则:

    PUT /route
    {
        "id": "my_route",
        "uri": "http://example.com",
        "predicates": [
            {
                "name": "Path",
                "args": {
                    "pattern": "/mypath_v2/**"
                }
            }
        ],
        "filters": [
            {
                "name": "RewritePath",
                "args": {
                    "regexp": "/mypath_v2/(?<segment>.*)",
                    "replacement": "/${segment}"
                }
            }
        ]
    }
    
  3. 删除现有的路由规则:

    DELETE /route/my_route
    

通过这种方法,可以在Spring Cloud Gateway运行时动态地创建和修改路由规则,以满足不断变化的业务需求。

3. 服务发现路由(自动路由)

3.1. 配置方式

这是一种特殊的动态路由,其路由规则是根据服务发现机制来自动创建的。
在微服务架构中,服务发现是一种特别重要的机制,它让微服务能够自动地发现网络中的其他服务,并知道如何与它们进行交互。Spring Cloud Gateway通过与服务注册中心(如Eureka、Consul等)集成,实现了自动的服务发现路由功能。

当一个新的服务实例被注册到服务注册中心时,Spring Cloud Gateway会自动发现它,并创建一个新的路由规则,将请求转发到这个服务。同样,当一个服务实例下线或被注销时,对应的路由规则也会被自动删除,这就是所谓的服务发现路由或者自动路由。

这种机制可以极大地简化服务间的交互,因为不需要手动地为每一个服务定义路由规则。

在Spring Cloud Gateway中使用服务发现的一个简单示例

添加 Spring Cloud Gateway和 Eureka的依赖:

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

application.yml文件中配置Eureka和Gateway:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true # 启用服务发现路由
  application:
    name: gateway-service
eureka:
  client:
    service-url:
      defaultZone: http://localhost:8761/eureka/

启动Eureka Server和的服务,然后启动Spring Cloud Gateway,Gateway就会自动发现注册在Eureka中的服务,并为它们创建路由规则。例如有一个名为user-service的服务,那么就可以通过http://localhost:8080/user-service来访问。

3.2 自动路由(服务发现)原理

在Spring Cloud Gateway中实现服务发现路由的核心类是DiscoveryClientRouteDefinitionLocator。这个类负责从注册中心获取服务实例信息,并基于这些信息创建路由定义。当服务实例发生变化时,DiscoveryClientRouteDefinitionLocator会自动更新路由定义。

服务发现路由的底层原理主要包括以下几个方面:

  1. 集成服务发现组件:Spring Cloud Gateway可以与多种服务注册中心(如Eureka、Consul等)集成。这些集成是通过实现DiscoveryClient接口来完成的。DiscoveryClient提供了获取服务实例信息的方法,例如getInstances(String serviceId)。这使得Gateway可以获取到注册中心中所有服务的实例信息。

  2. 创建路由定义:DiscoveryClientRouteDefinitionLocator会将从DiscoveryClient获取到的服务实例信息转换为RouteDefinition对象。这些RouteDefinition对象包含了路由的基本信息,如ID、URI、断言和过滤器等。通过这些信息,Gateway可以知道如何将请求路由到特定的服务实例。

  3. 路由规则更新:当注册中心中的服务实例发生变化时,DiscoveryClientRouteDefinitionLocator会自动更新路由规则。这是通过监听服务实例变化事件来实现的。当接收到服务实例变化事件后,DiscoveryClientRouteDefinitionLocator会重新获取服务实例信息,并更新RouteDefinition对象。这样,Gateway就能实时地感知到服务实例的变化,并相应地调整路由规则。

  4. 请求转发:当Gateway接收到一个请求时,它会根据DiscoveryClientRouteDefinitionLocator提供的路由定义匹配相应的路由规则。然后,Gateway会将请求转发到匹配的服务实例。这个转发过程是通过NettyRoutingFilter完成的,它会根据RouteDefinition中的URI信息转发请求。

核心源码
GatewayDiscoveryClientAutoConfiguration

我们可以看到 当 DiscoveryClient 存在并且 spring.cloud.gateway.discovery.locator.enabledtrue 时,创建一个 DiscoveryClientRouteDefinitionLocator Bean,允许 Spring Cloud Gateway 动态地从服务注册中心发现路由定义。
在这里插入图片描述

DiscoveryClientRouteDefinitionLocator核心方法getRouteDefinitions

getRouteDefinitions方法的主要目的是从 DiscoveryClient 获取服务实例信息并将其转换为路由定义。首先解析 includeExpressionurlExpression,然后根据 includeExpression 创建一个用于判断服务实例是否需要被包含在路由定义中的 Predicate。接着从 DiscoveryClient 获取所有服务实例信息,并转换为 RouteDefinition。转换过程中会生成 PredicateDefinitionFilterDefinition。最后返回一个包含所有路由定义的 Flux 对象。

@Override
public Flux<RouteDefinition> getRouteDefinitions() {
    // 创建一个 SpelExpressionParser 用于解析表达式
    SpelExpressionParser parser = new SpelExpressionParser();
    // 解析 includeExpression,用于判断服务实例是否需要被包含在路由定义中
    Expression includeExpr = parser.parseExpression(properties.getIncludeExpression());
    // 解析 urlExpression,用于生成服务实例对应的 URI
    Expression urlExpr = parser.parseExpression(properties.getUrlExpression());

    // 根据 includeExpression 创建一个 Predicate,用于判断服务实例是否需要被包含在路由定义中
    Predicate<ServiceInstance> includePredicate;
    if (properties.getIncludeExpression() == null || "true".equalsIgnoreCase(properties.getIncludeExpression())) {
        includePredicate = instance -> true;
    } else {
        includePredicate = instance -> {
            Boolean include = includeExpr.getValue(evalCtxt, instance, Boolean.class);
            if (include == null) {
                return false;
            }
            return include;
        };
    }

    // 从 DiscoveryClient 获取所有服务实例信息并转换为路由定义
    return Flux.fromIterable(discoveryClient.getServices())
            .map(discoveryClient::getInstances) // 获取每个服务的所有实例
            .filter(instances -> !instances.isEmpty()) // 过滤掉没有实例的服务
            .map(instances -> instances.get(0)) // 获取每个服务的第一个实例
            .filter(includePredicate) // 过滤掉不需要被包含在路由定义中的服务实例
            .map(instance -> { // 将服务实例转换为 RouteDefinition
                String serviceId = instance.getServiceId();

                RouteDefinition routeDefinition = new RouteDefinition();
                routeDefinition.setId(this.routeIdPrefix + serviceId);
                // 获取服务实例对应的 URI
                String uri = urlExpr.getValue(evalCtxt, instance, String.class);
                routeDefinition.setUri(URI.create(uri));

                final ServiceInstance instanceForEval = new DelegatingServiceInstance(instance, properties);

                // 生成 PredicateDefinition
                for (PredicateDefinition original : this.properties.getPredicates()) {
                    PredicateDefinition predicate = new PredicateDefinition();
                    predicate.setName(original.getName());
                    for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
                        String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
                        predicate.addArg(entry.getKey(), value);
                    }
                    routeDefinition.getPredicates().add(predicate);
                }

                // 生成 FilterDefinition
                for (FilterDefinition original : this.properties.getFilters()) {
                    FilterDefinition filter = new FilterDefinition();
                    filter.setName(original.getName());
                    for (Map.Entry<String, String> entry : original.getArgs().entrySet()) {
                        String value = getValueFromExpr(evalCtxt, parser, instanceForEval, entry);
                        filter.addArg(entry.getKey(), value);
                    }
                    routeDefinition.getFilters().add(filter);
                }

                return routeDefinition;
            });
}

4. Gateway路由的核心组件

4.1 路由断言

路由断言(Route Predicates)是Spring Cloud Gateway中的一个核心概念,它负责匹配HTTP请求的属性,并决定是否将请求路由到特定的服务。

使用断言,可以基于许多不同的请求属性定义路由规则,包括:

  • 请求路径:比如请求的URL
  • 请求方法:GET、POST等
  • 请求头:可以匹配特定的请求头字段和值
  • 查询参数:可以匹配URL的查询参数
  • Cookie:可以匹配特定的cookie

断言的定义通常在Spring配置文件(如application.yaml)中,作为路由定义的一部分。每个断言都是一种谓词(Predicate),这是一个返回true或false的函数。

路由断言的示例:

spring:
  cloud:
    gateway:
      routes:
      - id: user_service
        uri: http://localhost:8080/user
        predicates:
        - Path=/user/**
        - Method=GET
        - Header=X-Request-User, \d+

在这个例子中,定义了一个名为"user_service"的路由,其目标URI为"http://localhost:8080/user"。定义了三个断言:

  1. Path断言:只有当请求路径以"/user/"开头时,才会触发该路由。
  2. Method断言:只有当HTTP请求方法为GET时,才会触发该路由。
  3. Header断言:只有当请求头中存在名为"X-Request-User"的字段,并且其值为数字时,才会触发该路由。

只有当所有的断言都返回true时,该请求才会被路由到目标服务。否则,Spring Cloud Gateway将返回404错误。

组合并生效相当于并且的关系

在这个示例中,只有满足以下所有条件的请求才会路由到http://localhost:8080/:

  • 请求路径以/api/开头
  • 请求方法为GET
  • 请求头中包含X-Request-Id,且其值为数字
  • 查询参数中包含debug,并且其值为true
  • Cookie中包含sessionId,并且其值不为空
spring:
  cloud:
    gateway:
      routes:
      - id: host_route
        uri: http://localhost:8080/
        predicates:
        - Path=/api/**
        - Method=GET
        - Header=X-Request-Id, \d+
        - Query=debug, true
        - Cookie=sessionId, .+

如果要或的关系需要分配多个路由

如果想要以"或"的关系生效,需要定义多个路由。考虑到Spring Cloud Gateway的工作方式,一个请求只能被一个路由处理。这意味着谓词是以"与"的方式工作的,而不是"或"。如果想要一个请求被多个谓词处理,需要创建多个路由。每个路由有一个谓词,这样一个请求可以匹配多个路由。

在以下的配置中,如果请求路径以/api/开头, 或请求方法为GET, 或请求头中包含X-Request-Id且其值为数字, 或查询参数中包含debug且其值为true, 或Cookie中包含sessionId且其值不为空,任何一个条件满足,请求都会路由到http://localhost:8080/。

spring:
  cloud:
    gateway:
      routes:
      - id: host_route1
        uri: http://localhost:8080/
        predicates:
        - Path=/api/**
      - id: host_route2
        uri: http://localhost:8080/
        predicates:
        - Method=GET
      - id: host_route3
        uri: http://localhost:8080/
        predicates:
        - Header=X-Request-Id, \d+
      - id: host_route4
        uri: http://localhost:8080/
        predicates:
        - Query=debug, true
      - id: host_route5
        uri: http://localhost:8080/
        predicates:
        - Cookie=sessionId, .+

4.2 路由过滤器

Spring Cloud Gateway作为一个API网关,提供了很多功能,如路由转发、断路器、限流等。其中,路由过滤器是其核心功能之一,它允许我们在请求被路由之前或之后对请求进行处理。本文将详细介绍Spring Cloud Gateway的路由过滤器。

路由过滤器概念

路由过滤器是一个Java类,用于修改进入或退出网关的HTTP请求和响应。它包含两种类型的过滤器:

  1. Global Filter:全局过滤器,对所有路由请求生效。
  2. GatewayFilter:网关过滤器,只对特定路由请求生效。

Spring Cloud Gateway中的过滤器基于WebFilter接口实现,并且实现了Ordered接口来控制过滤器的执行顺序。过滤器可以在请求被路由之前(Pre过滤器)或之后(Post过滤器)进行处理。

路由过滤器工厂

Spring Cloud Gateway的过滤器是通过工厂创建的。这些工厂实现了GatewayFilterFactory接口,这个接口包含两个方法:

  • apply(T config):将配置信息传递给过滤器,并创建过滤器实例。
  • getConfigClass():返回过滤器所使用的配置类。

内置路由过滤器

Spring Cloud Gateway提供了许多内置过滤器,可以覆盖大部分基本功能。以下是一些常用的内置过滤器:

  1. AddRequestHeader:添加请求头。
  2. AddRequestParameter:添加请求参数。
  3. AddResponseHeader:添加响应头。
  4. PrefixPath:添加前缀路径。
  5. RewritePath:重写请求路径。
  6. Retry:重试。
  7. SetPath:设置请求路径。
  8. SetResponseHeader:设置响应头。
  9. StripPrefix:去掉前缀。
  10. Hystrix:使用Hystrix断路器。

自定义路由过滤器

除了内置过滤器以外,我们还可以自定义过滤器。自定义过滤器需要实现GatewayFilter接口,并重写filter(ServerWebExchange exchange, GatewayFilterChain chain)方法。下面是一个简单的自定义过滤器示例:
创建了一个名为 CustomGatewayFilterFactory 的类,实现了 GatewayFilterFactory<GatewayFilterFactory.Args> 接口,并重写了 applyname 方法。在 apply 方法中,我们返回一个新的 CustomGatewayFilter 实例。在 name 方法中,我们返回过滤器的名称,这个名称需要与配置文件中的 filters 部分所引用的名称相匹配。最后,我们将 CustomGatewayFilterFactory 注册为一个 Bean。

public class CustomGatewayFilter implements GatewayFilter {

    @Override
    public Mono<Void> filter(ServerWebExchange exchange, GatewayFilterChain chain) {
        // 在请求被路由之前执行的逻辑
        System.out.println("Custom Gateway Filter Pre");

        return chain.filter(exchange).then(Mono.fromRunnable(() -> {
            // 在请求被路由之后执行的逻辑
            System.out.println("Custom Gateway Filter Post");
        }));
    }
}

要在配置文件中为特定路由配置 CustomGatewayFilter,需要在 Spring Cloud Gateway 的配置文件(如 application.ymlapplication.properties)中定义路由规则,并在 filters 部分引用的 CustomGatewayFilter。以下是一个在 application.yml 中配置 CustomGatewayFilter 的示例:

spring:
  cloud:
    gateway:
      routes:
        - id: my_route
          uri: http://example.com # 目标服务的地址
          predicates:
            - Path=/my_path/** # 路由条件,例如路径匹配
          filters:
            - name: CustomFilter # 在这里引用的自定义过滤器

需要先将 CustomGatewayFilter 注册为一个全局过滤器。为了达到这个目的,需要创建一个 GatewayFilterFactory,并将其注册为一个 Bean。请参考以下示例:

@Configuration
public class CustomGatewayFilterConfiguration {

    @Bean
    public CustomGatewayFilterFactory customGatewayFilterFactory() {
        return new CustomGatewayFilterFactory();
    }

    public static class CustomGatewayFilterFactory implements GatewayFilterFactory<GatewayFilterFactory.Args> {

        @Override
        public GatewayFilter apply(GatewayFilterFactory.Args args) {
            return new CustomGatewayFilter();
        }

        @Override
        public String name() {
            return "CustomFilter";
        }
    }
}

5. 路由的动态刷新

我们写一个简化的教程来看下如何在Spring Cloud Gateway中实现动态路由刷新。

5.1. 添加依赖

在Gateway服务的pom.xml文件中添加以下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-config</artifactId>
</dependency>
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-bus-amqp</artifactId>
</dependency>

这里我们使用RabbitMQ作为消息代理,如果想使用其他消息代理,请替换相应的依赖。

5.2. 配置application.yml

在Gateway服务的src/main/resources/application.yml中添加以下配置:

spring:
  cloud:
    gateway:
      discovery:
        locator:
          enabled: true
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user-service/**
  config:
    uri: http://localhost:8888
  rabbitmq:
    host: localhost
    port: 5672
    username: guest
    password: guest
  bus:
    id: ${spring.application.name}:${server.port}

这里我们配置了一个名为user-service的路由,路由的配置信息将从Config Server(地址为http://localhost:8888)获取。

5.3. 创建Config Server

创建一个新的Spring Boot项目,名为config-server。在pom.xml文件中添加以下依赖:

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-config-server</artifactId>
</dependency>

src/main/resources/application.yml中添加以下配置:

server:
  port: 8888
spring:
  cloud:
    config:
      server:
        git:
          uri: https://github.com/your-username/your-repo.git

这里配置了Config Server从远程Git仓库获取配置信息。

src/main/java/com/example/configserver/ConfigServerApplication.java中添加@EnableConfigServer注解以启动Config Server:

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

启动Config Server。

5.4. 在远程Git仓库中添加配置文件

在的远程Git仓库中添加一个名为gateway-service.yml的文件,内容如下:

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user-service/**

这里配置了一个名为user-service的路由。

5.5. 启动Gateway服务

现在启动的Gateway服务,它将从Config Server获取最新的路由信息。可以通过访问http://localhost:8080/user-service/some-api来测试路由是否生效。

5.6. 动态刷新路由信息

gateway-service.yml文件发生变化并推送到远程Git仓库后,可以发送一个POST请求到http://localhost:8080/actuator/refresh端点以刷新Gateway的路由信息。

也可以在config-server项目中添加Spring Cloud Bus的依赖,并添加相应的配置,以实现自动广播路由变更消息。

6. 处理路由失败和异常

如何处理路由失败和异常也是大家在写一个高可用,健壮性良好的网关服务的基本要求。那么在本教程中,我们来了解一下Spring Cloud Gateway中如何处理路由失败和异常,以及如何配置回退和重试策略。

6.1 处理请求失败和异常

当路由转发过程中发生异常,例如目标服务不可用,可以使用Gateway的全局异常处理器来捕获异常并返回一个友好的错误响应。默认情况下,Gateway会使用org.springframework.cloud.gateway.handler.GlobalErrorWebExceptionHandler作为全局异常处理器。可以通过实现ErrorWebExceptionHandler接口并注册为Spring Bean来自定义全局异常处理器。
写一个简单的例子可供参考
自定义CustomGlobalExceptionHandler继承了AbstractErrorWebExceptionHandler并覆盖了getRoutingFunction方法,以自定义错误响应。

@Component
public class CustomGlobalExceptionHandler extends AbstractErrorWebExceptionHandler {

    public CustomGlobalExceptionHandler(ErrorAttributes errorAttributes,
                                        WebProperties.Resources resources,
                                        ApplicationContext applicationContext,
                                        ServerCodecConfigurer serverCodecConfigurer) {
        super(errorAttributes, resources, applicationContext);
        setMessageWriters(serverCodecConfigurer.getWriters());
        setMessageReaders(serverCodecConfigurer.getReaders());
    }

    @Override
    protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
        return RouterFunctions.route(RequestPredicates.all(), this::renderErrorResponse);
    }

    private Mono<ServerResponse> renderErrorResponse(ServerRequest request) {
        Throwable error = getError(request);
        // 自定义响应体
        Map<String, Object> errorAttributes = new HashMap<>();
        errorAttributes.put("message", error.getMessage());
        errorAttributes.put("status", HttpStatus.INTERNAL_SERVER_ERROR.value());
        errorAttributes.put("timestamp", LocalDateTime.now());
        return ServerResponse.status(HttpStatus.INTERNAL_SERVER_ERROR)
                .contentType(MediaType.APPLICATION_JSON)
                .body(BodyInserters.fromValue(errorAttributes));
    }
}

默认实现是DefaultErrorWebExceptionHandler
在这里插入图片描述

6.2 配置回退和重试策略

使用Spring Cloud Gateway,可以为路由配置回退和重试策略。回退策略允许在转发请求失败时返回一个预定义的响应。重试策略允许在请求失败时尝试重新发送请求。

为了配置回退策略,可以在路由配置中添加fallbackUri属性。

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user-service/**
          filters:
            - name: Hystrix
              args:
                name: user-service
                fallbackUri: forward:/fallback

user-service路由配置了一个回退URI,它会将请求转发到/fallback端点。需要在的Gateway服务中实现这个端点,并返回一个预定义的响应。

为了配置重试策略,可以在路由配置中添加Retry过滤器。
user-service路由配置了一个重试策略。当请求方法为GET且服务器返回500 Internal Server Error状态时,Gateway会尝试重新发送请求,最多重试3次。

spring:
  cloud:
    gateway:
      routes:
        - id: user-service
          uri: lb://user-service
          predicates:
            - Path=/user-service/**
          filters:
            - name: Retry
              args:
                retries: 3
                statuses: INTERNAL_SERVER_ERROR
                methods: GET

7. 高级路由功能

写不动了,路由详解就到这儿,后面有时间补充一下高级路由功能。

  • 7.1 如何配置负载均衡
  • 7.2 如何配置熔断器
  • 7.3 如何配置速率限制

8. 参考文档

Spring Cloud Gateway官方文档 https://cloud.spring.io/spring-cloud-gateway/2.0.x/single/spring-cloud-gateway.html
在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1068169.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【轻松玩转MacOS】系统设置篇

引言 作为一个MacOS新用户&#xff0c;你是否对系统设置感到迷茫&#xff1f;是否想要定制出一个完全属于自己的MacBook&#xff1f;别担心&#xff0c;本文将带你一步步走进系统设置的世界&#xff0c;让你轻松定制出一个独一无二的MacBook。让我们开始吧&#xff01;今天&am…

开发做前端好还是后端好?这是个问题!

前言 随着互联网的快速发展&#xff0c;越来越多的人选择从事Web开发行业&#xff0c;而Web开发涉及到前端和后端两个方面&#xff0c;相信许多人都曾经对这两个方面进行过探究。而且编程世界就像一座大城市&#xff0c;前端开发和后端开发就像城市的两个不同街区。作为初学者&…

【C语言初阶】分支语句和循环语句

目录 一、什么是语句 二、分支语句 2.1 if 语句 2.1.1 悬空else 2.1.2 if 书写形式的对比 2.2 switch语句 2.2.1 在switch语句中的 break 2.2.2 default子句 2.2.3 练习 三、循环语句 3.1 while循环 3.1.1 while语句中的break 3.1.2 while语句中的continue 3.2 fo…

AndroidStudio添加一个虚拟设备

虚拟设备管理器 这个是AndroidStudio的启动界面 虚拟设备管理界面 点击加号&#xff0c;新增 选择手机型号 选择系统版本 虚拟设备名 完成

应用安全系列之三十九:JWT 相关安全问题以及最佳实践

JWT 简介 JWT是JSON Web Token 的简称,根据https://www.rfc-editor.org/rfc/rfc7519的定义如下: A string representing a set of claims as a JSON object that is encoded in a JWS or JWE, enabling the claims to be digitally signed or MACed and/or encrypted. 翻译…

滴滴发布十一大数据:延边出行需求上涨280% 西部省份成旅游热点

今年十一假期适逢中秋佳节&#xff0c;在亲友团聚和长假出游的多重期盼下&#xff0c;超级黄金周展现强劲内需&#xff0c;带动多样化的消费趋势&#xff0c;出行热情也随之高涨。滴滴出行数据显示&#xff0c;打车需求相比去年同期上涨80%&#xff0c;高峰时段每分钟呼叫突破1…

[论文工具] LaTeX论文SVG和EPS矢量图转换方法详解

祝大家中秋国庆双节快乐&#xff01; 回过头来&#xff0c;我们在编程过程中&#xff0c;经常会遇到各种各样的问题。然而&#xff0c;很多问题都无法解决&#xff0c;网上夹杂着各种冗余的回答&#xff0c;也缺乏系统的实战技巧归纳。为更好地从事科学研究和编程学习&#xff…

C语言中文网 - Shell脚本 - 1

Shell 既是一个连接用户和 Linux 内核的程序&#xff0c;又是一门管理 Linux 系统的脚本语言。Shell 脚本虽然没有 C、Python、Java、C# 等编程语言强大&#xff0c;但也支持了基本的编程元素。 第1章 Shell基础&#xff08;开胃菜&#xff09; 欢迎来到 Linux Shell 的世界&am…

吃鸡攻略大揭秘!提升战斗力,分享干货!

大家好&#xff01;我是你们的吃鸡玩家小编。今天我要和大家分享一些关于提高游戏战斗力和分享顶级游戏干货的干货&#xff01; 首先&#xff0c;我们要提到的是绝地求生作图工具推荐。作为一名吃鸡玩家&#xff0c;你一定想要在游戏中获得更多的优势。绝地求生作图工具是你必备…

HarmonyOS学习路之方舟开发框架—学习ArkTS语言(状态管理 八)

其他状态管理概述 除了前面章节提到的组件状态管理和应用状态管理&#xff0c;ArkTS还提供了Watch和$$来为开发者提供更多功能&#xff1a; Watch用于监听状态变量的变化。$$运算符&#xff1a;给内置组件提供TS变量的引用&#xff0c;使得TS变量和内置组件的内部状态保持同步…

WSL 安装 NVIDIA显卡驱动

文章目录 WSL 安装 NVIDIA显卡驱动本机显卡信息验证安装 WSL 版 Ubuntu 22.04在 WSL 中安装 NVIDIA显卡驱动WSL 安装 NVIDIA显卡驱动 最近在研究一些 AIGC 工具,由于 Windows 加入了 WSL 之后的各种特性,本文记录一下如何在 WSL 的 Linux发行版 中安装 NVIDIA 显卡驱动的步骤,…

力扣第110题 平衡二叉数 c++ 树 深度优先搜索 二叉树

题目 110. 平衡二叉树 简单 给定一个二叉树&#xff0c;判断它是否是高度平衡的二叉树。 本题中&#xff0c;一棵高度平衡二叉树定义为&#xff1a; 一个二叉树每个节点 的左右两个子树的高度差的绝对值不超过 1 。 示例 1&#xff1a; 输入&#xff1a;root [3,9,20,null…

车险计算器微信小程序源码 带流量主功能

车险计算器微信小程序源码带流量主功能&#xff0c;可以精准的算出车险的书目&#xff0c;是一个非常实用的微信小程序源码。 简单的计算让你得知车险价值 另外也支持流量主&#xff0c;具体小编也就不多说了&#xff0c;大家自己搭建研究吧。 源码下载&#xff1a;https://d…

nsoftware Cloud SMS 2022 .NET 22.0.8 Crack

nsoftware Cloud SMS 能够通过各种流行的消息服务&#xff08;包括 Twilio、Sinch、SMSGlobal、SMS.to、Vonage、Clickatell 等&#xff09;发送、接收和安排 SMS 消息&#xff0c;从而提供了一种简化且高效的消息服务方法。 Cloud SMS 提供单个 SMS 组件&#xff0c;允许通过…

spring源码解析——IOC之bean创建

正文 在 Spring 中存在着不同的 scope&#xff0c;默认是 singleton &#xff0c;还有 prototype、request 等等其他的 scope&#xff0c;他们的初始化步骤是怎样的呢&#xff1f;这个答案在这篇博客中给出。 singleton Spring 的 scope 默认为 singleton&#xff0c;第一部…

C++11(左值(引用),右值(引用),移动语义,完美转发)

目录 一、左值与左值引用 1、左值 2、左值引用 3、意义 二、右值与右值引用 1、右值 2、右值引用 三、右值引用使用场景和意义 1、右值的分类 2、移动构造 3、移动赋值 四、万能引用 五、完美转发 一、左值与左值引用 1、左值 左值是一个表示数据的表达式(如变量名…

软件设计之抽象工厂模式

抽象工厂模式指把一个产品变成一个接口&#xff0c;它的子产品作为接口的实现&#xff0c;所以还需要一个总抽象工厂和它的分抽象工厂。 下面我们用一个案例去说明抽象工厂模式。 在class中可以选择super类和medium类&#xff0c;即选择一个产品的子类。在type中可以选择产品的…

LomBok的使用,MyBatis的使用(增删改查)

Lombok是一个Java库&#xff0c;能自动插入编辑器并构建工具&#xff0c;简化Java开发。通过添加注解的方式&#xff0c;不需要 为类编写getter或equels方法&#xff0c;同时可以自动化日志变量。 结构 pom <?xml version"1.0" encoding"UTF-8"?>…

LLM - FastAPI 搭建简易问答 Server

目录 一.引言 二.辅助函数 1.黑名单 2.清除函数 三.模型函数 1.加载模型 2.生成配置 四.服务部署 1.post - predict 2.get - clean_cache 3.main - run_app 五.总结 一.引言 SFT workflow 微调工作流程 一文中我们介绍了模型微调从数据到最终应用的流程 FastAPI …

C语言 选择(分支)

if 语句&#xff08;分支语句/选择语句&#xff09; 结构&#xff1a; if ( expressio ) statement 如果对 expression 求值为真&#xff08;非0&#xff09;&#xff0c;则执行 statement &#xff1b;否则&#xff0c;跳过 statement 。与 while 循环一样&#xff0c…