
本指南将引导您了解如何使用 Spring 云网关
您将构建什么
您将使用春云网关.
你需要什么
- 约15分钟
- 最喜欢的文本编辑器或 IDE
- JDK 1.8或以后
- 格拉德尔 4+或梅文 3.2+
- 您也可以将代码直接导入到 IDE 中:
如何完成本指南
像大多数春天一样入门指南,您可以从头开始并完成每个步骤,也可以绕过您已经熟悉的基本设置步骤。无论哪种方式,您最终都会得到工作代码。
要从头开始,请继续从 Spring 初始化开始.
要跳过基础知识,请执行以下操作:
- 下载并解压缩本指南的源存储库,或使用吉特:
git clone https://github.com/spring-guides/gs-gateway.git
- 光盘成
gs-gateway/initial
- 跳转到创建简单路线.
完成后,您可以根据 中的代码检查结果。gs-gateway/complete
从 Spring 初始化开始
你可以使用这个预初始化项目,然后单击生成以下载 ZIP 文件。此项目配置为适合本教程中的示例。
手动初始化项目:
- 导航到https://start.spring.io.此服务拉入应用程序所需的所有依赖项,并为您完成大部分设置。
- 选择 Gradle 或 Maven 以及您要使用的语言。本指南假定您选择了 Java。
- 单击依赖项,然后选择网关、弹性 4J 和合约存根运行程序。
- 单击生成。
- 下载生成的 ZIP 文件,该文件是配置了您选择的 Web 应用程序的存档。
如果您的 IDE 集成了 Spring Initializr,则可以从 IDE 完成此过程。 |
您也可以从 Github 分叉项目,然后在 IDE 或其他编辑器中打开它。 |
创建简单路线
Spring 云网关使用路由来处理对下游服务的请求。在本指南中,我们将所有请求路由到HTTPBin.可以通过多种方式配置路由,但在本指南中,我们使用网关提供的 Java API。
要开始使用,请在 中创建一个新的 of 类型。Bean
RouteLocator
Application.java
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes().build();
}
该方法采用可用于创建路由的。除了创建路由之外,还允许您向路由添加谓词和筛选器,以便您可以根据特定条件路由句柄,并根据需要更改请求/响应。myRoutes
RouteLocatorBuilder
RouteLocatorBuilder
现在,我们可以创建一个路由,当向网关发出请求时,该路由将请求路由到。在此路由的配置中,我们添加一个过滤器,该过滤器在路由请求之前将值为 of 的请求标头添加到请求:https://httpbin.org/get
/get
Hello
World
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.build();
}
要测试我们的简单网关,我们可以在 端口 .应用程序运行后,向 发出请求。您可以通过在终端中使用以下 cURL 命令来执行此操作:Application.java
8080
http://localhost:8080/get
$ curl http://localhost:8080/get
您应该会收到类似于以下输出的响应:
{
"args": {},
"headers": {
"Accept": "*/*",
"Connection": "close",
"Forwarded": "proto=http;host=\"localhost:8080\";for=\"0:0:0:0:0:0:0:1:56207\"",
"Hello": "World",
"Host": "httpbin.org",
"User-Agent": "curl/7.54.0",
"X-Forwarded-Host": "localhost:8080"
},
"origin": "0:0:0:0:0:0:0:1, 73.68.251.70",
"url": "http://localhost:8080/get"
}
请注意,HTTPBin 显示值为 的标头已在请求中发送。Hello
World
使用弹簧云断路器
现在我们可以做一些更有趣的事情。由于网关后面的服务可能会表现不佳并影响我们的客户,因此我们可能希望将我们创建的路由包装在断路器中。您可以在Spring Cloud Gateway中使用Resilience4J Spring Cloud Breaker实现来执行此操作。这是通过一个简单的过滤器实现的,您可以将其添加到您的请求中。我们可以创建另一条路线来证明这一点。
要使用此过滤器,您需要将反应式 Resilience4J 断路器依赖项添加到类路径中。
pom.xml
...
org.springframework.cloud
spring-cloud-starter-circuitbreaker-reactor-resilience4j
...
build.gradle
...
implementation("org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j")
...
在下一个示例中,我们使用 HTTPBin 的延迟 API,它在发送响应之前等待一定的秒数。由于此 API 可能需要很长时间才能发送其响应,因此我们可以将使用此 API 的路由包装在断路器中。下面的清单为我们的对象添加了一条新路由:RouteLocator
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f.circuitBreaker(config -> config.setName("mycmd")))
.uri("http://httpbin.org:80")).
build();
}
此新路由配置与我们创建的先前路由配置之间存在一些差异。首先,我们使用主机谓词而不是路径谓词。这意味着,只要主机是 ,我们就将请求路由到 HTTPBin 并将该请求包装在断路器中。我们通过对路由应用过滤器来实现此目的。我们可以使用配置对象来配置断路器过滤器。在此示例中,我们为断路器指定名称 .circuitbreaker.com
mycmd
现在我们可以测试这条新路线了。为此,我们需要启动应用程序,但是,这一次,我们将向 .同样重要的是,我们包含一个具有主机的标头。否则,不会路由请求。我们可以使用以下 cURL 命令:/delay/3
Host
circuitbreaker.com
$ curl --dump-header - --header 'Host: www.circuitbreaker.com' http://localhost:8080/delay/3
我们用来查看响应标头。之后告诉 cURL 将标头打印到 stdout。--dump-header - --dump-header |
使用此命令后,您应该在终端中看到以下内容:
HTTP/1.1 504 Gateway Timeout
content-length: 0
如您所见,断路器在等待来自 HTTPBin 的响应时超时。当断路器超时时,我们可以选择提供回退,以便客户端不会收到更有意义的东西。例如,在生产方案中,您可能会从缓存中返回一些数据,但是,在我们的简单示例中,我们返回带有正文的响应。504
fallback
为此,我们可以修改断路器过滤器,以提供在超时时调用的 URL:
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder) {
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri("http://httpbin.org:80"))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri("http://httpbin.org:80"))
.build();
}
现在,当断路器包装路由超时时,它会调用网关应用程序。现在,我们可以将终结点添加到应用程序中。/fallback
/fallback
在 中,我们添加类级别注释,然后将以下内容添加到类中:Application.java
@RestController
@RequestMapping
src/main/java/gateway/Application.java
@RequestMapping("/fallback")
public Mono fallback() {
return Mono.just("fallback");
}
要测试此新的回退功能,请重新启动应用程序,然后再次发出以下 cURL 命令
$ curl --dump-header - --header 'Host: www.circuitbreaker.com' http://localhost:8080/delay/3
有了回退,我们现在看到我们从网关获得一个响应正文为 .200
fallback
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: text/plain;charset=UTF-8
fallback
编写测试
作为一个优秀的开发人员,我们应该编写一些测试来确保我们的网关正在做我们期望它应该做的事情。在大多数情况下,我们希望限制对外部资源的依赖,尤其是在单元测试中,因此我们不应该依赖HTTPBin。解决此问题的一种方法是使路由中的 URI 可配置,以便我们可以根据需要更改 URI。
为此,在 中,我们可以创建一个名为 的新类:Application.java
UriConfiguration
@ConfigurationProperties
class UriConfiguration {
private String httpbin = "http://httpbin.org:80";
public String getHttpbin() {
return httpbin;
}
public void setHttpbin(String httpbin) {
this.httpbin = httpbin;
}
}
要启用 ,我们还需要向 添加类级注解。ConfigurationProperties
Application.java
@EnableConfigurationProperties(UriConfiguration.class)
有了我们的新配置类,我们可以在方法中使用它:myRoutes
src/main/java/gateway/Application.java
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) {
String httpUri = uriConfiguration.getHttpbin();
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri(httpUri))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f
.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri(httpUri))
.build();
}
我们没有将 URL 硬编码到 HTTPBin,而是从新的配置类中获取 URL。
以下清单显示了 的完整内容:Application.java
src/main/java/gateway/Application.java
@SpringBootApplication
@EnableConfigurationProperties(UriConfiguration.class)
@RestController
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
@Bean
public RouteLocator myRoutes(RouteLocatorBuilder builder, UriConfiguration uriConfiguration) {
String httpUri = uriConfiguration.getHttpbin();
return builder.routes()
.route(p -> p
.path("/get")
.filters(f -> f.addRequestHeader("Hello", "World"))
.uri(httpUri))
.route(p -> p
.host("*.circuitbreaker.com")
.filters(f -> f
.circuitBreaker(config -> config
.setName("mycmd")
.setFallbackUri("forward:/fallback")))
.uri(httpUri))
.build();
}
@RequestMapping("/fallback")
public Mono fallback() {
return Mono.just("fallback");
}
}
@ConfigurationProperties
class UriConfiguration {
private String httpbin = "http://httpbin.org:80";
public String getHttpbin() {
return httpbin;
}
public void setHttpbin(String httpbin) {
this.httpbin = httpbin;
}
}
现在我们可以创建一个名为 in 的新类。在新类中,我们添加了以下内容:ApplicationTest
src/main/test/java/gateway
@SpringBootTest(webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT,
properties = {"httpbin=http://localhost:${wiremock.server.port}"})
@AutoConfigureWireMock(port = 0)
public class ApplicationTest {
@Autowired
private WebTestClient webClient;
@Test
public void contextLoads() throws Exception {
//Stubs
stubFor(get(urlEqualTo("/get"))
.willReturn(aResponse()
.withBody("{\"headers\":{\"Hello\":\"World\"}}")
.withHeader("Content-Type", "application/json")));
stubFor(get(urlEqualTo("/delay/3"))
.willReturn(aResponse()
.withBody("no fallback")
.withFixedDelay(3000)));
webClient
.get().uri("/get")
.exchange()
.expectStatus().isOk()
.expectBody()
.jsonPath("$.headers.Hello").isEqualTo("World");
webClient
.get().uri("/delay/3")
.header("Host", "www.circuitbreaker.com")
.exchange()
.expectStatus().isOk()
.expectBody()
.consumeWith(
response -> assertThat(response.getResponseBody()).isEqualTo("fallback".getBytes()));
}
}
我们的测试利用了Spring Cloud Contract的WireMock,建立了一个可以模拟HTTPBin的API的服务器。首先要注意的是 的使用。这个注释为我们在一个随机端口上启动 WireMock。@AutoConfigureWireMock(port = 0)
接下来,请注意,我们利用我们的类并将注释中的属性设置为本地运行的 WireMock 服务器。在测试中,我们为通过网关调用的 HTTPBin API 设置“存根”,并模拟我们预期的行为。最后,我们使用向网关发出请求并验证响应。UriConfiguration
httpbin
@SpringBootTest
WebTestClient
总结
祝贺!您刚刚构建了您的第一个 Spring 云网关应用程序!