微服务系统可能具有大量具有复杂交互的组件。降低这种复杂性非常重要,至少从客户端与系统交互的角度来看是这样。网关将微服务与外部世界隐藏起来。它代表了一个独特的入口,并实现了常见的交叉要求。在本文中,您将学习如何使用 Spring Cloud Gateway 包为 Spring Boot 应用程序配置网关组件。
Spring Cloud 网关
Spring Cloud 通过 Spring Cloud Gateway 项目提供了一个网关实现。它基于 Spring Boot、Spring WebFlux 和 Reactor。由于它基于 Spring WebFlux,因此它必须在 Netty 环境中运行,而不是通常的 servlet 容器。
网关的主要功能是将来自外部客户端的请求路由到微服务。其主要组成部分是:
路由:这是基本实体。它配置了 ID、目标 URI、一个或多个谓词和过滤器。
谓词:这基于 Java 函数谓词。它表示必须在 head 或 request 参数上匹配的条件。
过滤器:它允许您更改请求或响应。
我们可以识别以下事件序列:
客户端通过网关进行呼叫。
网关决定请求是否与配置的路由匹配。
如果存在匹配项,则将请求发送到网关 Web 处理程序。
Web 处理程序将请求发送到一系列过滤器,这些过滤器可以执行与请求或响应相关的逻辑,并对它们操作更改。
执行目标服务。
Spring Cloud 网关依赖项
要将我们的 Spring Boot 应用程序实现为网关,我们必须首先在定义发布序列后提供依赖项,如下面的配置片段所示:spring-cloud-starter-gateway
XML格式
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-dependencies</artifactId>
<version>2023.0.0</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
XML格式
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-gateway</artifactId>
</dependency>
...
</dependencies>
Spring Cloud 网关配置
我们可以使用 application.yaml 文件来配置我们的网关组件。我们可以指定完全扩展的参数或快捷方式来定义谓词和过滤器。在第一种情况下,我们定义了 a 和 an 字段。该字段可以包含一个或多个键值对:name
args
args
YAML的
spring:
cloud:
gateway:
routes:
- id: route-example
uri: https://example.com
predicates:
- name: PredicateExample
args:
name: predicateName
regexp: predicateRegexpValue
在上面的示例中,我们定义了一个 ID 值为 “”的路由、一个目标 URI “,”和一个包含两个 “” 和 “” 的谓词。route-example
https://example.com
args
name
regexp
使用快捷方式模式,我们编写谓词名称,后跟“”字符,然后用逗号分隔名称和值列表。我们可以通过以下内容重写上一个示例:=
YAML的
spring:
cloud:
gateway:
routes:
- id: route-example
uri: https://example.com
predicates:
- Cookie=predicateName,predicateRegexpValue
特定的工厂类实现每个谓词和筛选器类型。有几个内置的谓词和过滤器工厂可用。上面显示的谓词就是一个示例。我们将在以下小节中列出其中的一些。Cookie
谓词内置工厂
在特定时间之后发生的匹配请求。
After Predicate Factory
After=2007-12-03T10:15:30+01:00 Europe/Paris
在特定时间之前发生的匹配请求。
Before Predicate Factory
Before=2007-12-03T10:15:30+01:00 Europe/Paris
指定要匹配的 HTTP 方法类型。
Method Route Predicate Factory
Method=GET,POST
过滤器内置工厂
允许按其名称和值将 HTTP 标头添加到请求中。
AddRequestHeader GatewayFilter Factory
AddRequestHeader=X-Request-Foo,条形图
允许按其名称和值向请求添加参数。
AddRequestParameter GatewayFilter Factory
AddRequestParameter=foo,柱线
允许按其名称和值将 HTTP 标头添加到请求中。
AddResponseHeader GatewayFilter Factory
AddResponseHeader=X-Response-Foo,条形图
要实现自定义谓词或滤波器工厂,我们必须提供特定工厂接口的实现。以下各节演示如何操作。
自定义谓词工厂
要创建自定义谓词工厂,我们可以扩展接口的抽象实现。在下面的示例中,我们定义了一个内部静态类,以将其属性传递给 apply 方法并将它们与请求进行比较。AbstractRoutePredicateFactory
RoutePredicateFactory
Configuration
爪哇岛
@Component
public class CustomPredicateFactory extends AbstractRoutePredicateFactory<CustomPredicateFactory.Configuration> {
public CustomPredicateFactory() {
super(Configuration.class);
}
@Override
public Predicate<ServerWebExchange> apply(Configurationconfig) {
return exchange -> {
ServerHttpRequest request = exchange.getRequest();
//compare request with config properties
return matches(config, request);
};
}
private boolean matches(Configuration config, ServerHttpRequest request) {
//implement matching logic
return false;
}
public static class Configuration{
//implement custom configuration properties
}
}
定制过滤器工厂
为了创建一个自定义的过滤器工厂,我们可以扩展接口的抽象实现。在下面的示例中,您可以看到一个过滤器工厂,该工厂使用对象传递的属性修改请求,另一个过滤器工厂使用对象传递的属性更改响应。AbstractGatewayFilterFactory
GatewayFilterFactory
Configuration
爪哇岛
@Component
public class PreCustomFilterFactory extends AbstractGatewayFilterFactory<PreCustomFilterFactory.Configuration> {
public PreCustomFilterFactory() {
super(Configuration.class);
}
@Override
public GatewayFilter apply(Configuration config) {
return (exchange, chain) -> {
ServerHttpRequest.Builder builder = exchange.getRequest().mutate();
//use builder to modify the request
return chain.filter(exchange.mutate().request(builder.build()).build());
};
}
public static class Configuration {
//implement the configuration properties
}
}
@Component
public class PostCustomFilterFactory extends AbstractGatewayFilterFactory<PostCustomFilterFactory.Configuration> {
public PostCustomFilterFactory() {
super(Configuration.class);
}
@Override
public GatewayFilter apply(Configuration config) {
return (exchange, chain) -> {
return chain.filter(exchange).then(Mono.fromRunnable(() -> {
ServerHttpResponse response = exchange.getResponse();
//Change the response
}));
};
}
public static class Configuration {
//implement the configuration properties
}
}
Spring Cloud 网关示例
我们将展示一个实用而简单的示例,以了解网关在真实场景中是如何工作的。您将在文章末尾找到源代码的链接。该示例基于以下堆栈:
Spring Boot:3.2.1
Spring Cloud:2023.0.0
爪哇 17
我们考虑一个最小的微服务系统,它实现了一个只有两个服务的库:一个服务和一个服务。该服务通过传递参数来调用服务以检索作者的信息。这两个应用程序的实现基于嵌入式内存 H2 数据库,并使用 JPA ORM 来映射和查询 and 表。从本演示的角度来看,最重要的部分是服务类公开的 REST 端点:book
author
book
author
authorName
Book
Author
/getAuthor
BookController
Book
爪哇岛
@RestController
@RequestMapping("/library")
public class BookController {
Logger logger = LoggerFactory.getLogger(BookService.class);
@Autowired
private BookService bookService;
@GetMapping(value = "/getAuthor", params = { "authorName" })
public Optional<Author> getAuthor(@RequestParam("authorName") String authorName) {
return bookService.getAuthor(authorName);
}
}
这两个应用程序在 Eureka 发现服务器中注册自己,并配置为发现客户端。最后一个组件是网关。网关不应将自身注册到服务发现服务器。这是因为它仅由外部客户端调用,而不是由内部微服务调用。另一方面,它可以配置为发现客户端,以自动获取其他服务并实现更动态的路由。不过,我们在这里这样做不是为了让事情变得简单。
在此示例中,我们想要展示两件事:
了解谓词值的路由机制的工作原理
演示如何通过过滤器修改请求,添加标题
网关的配置如下:
爪哇岛
spring:
application:
name: gateway-service
cloud:
gateway:
routes:
- id: add_request_header_route
uri: http://localhost:8082
predicates:
- Path=/library/**
filters:
- AddRequestHeader=X-Request-red, red
我们定义了一个 id 为 “,” 的路由,URI 值为 “”,这是预订服务的基本 URI。然后,我们有一个值为 “” 的谓词。每个以 “” 开头的调用都将匹配并使用以 “” 开头的 URI 路由到书籍的服务。add_request_header_route
http://localhost:8082
Path
/library/**
http://localhost:8080/library/
http://localhost:8082/library/
运行示例
要运行该示例,您可以通过从组件的基目录中执行 “” 命令来启动每个组件。然后,您可以通过执行 “” URI 来测试它。结果将是一个 JSON 值,其中包含有关作者的信息。如果您查看浏览器开发人员工具,您还会发现已将值为“.”的标头添加到请求中。mvn spring-boot:run
http://localhost:8080/library/getAuthor?authorName=Goethe
X-Request-red
red
在Spring Boot框架中,使用Spring Cloud Gateway包实现网关是自然的选择。它通过在微服务环境前面放置单个立面组件来降低微服务环境的复杂性。它还为实现跨领域关注点(如身份验证、授权、聚合日志记录、跟踪和监视)提供了极大的灵活性。