网站首页 > 技术文章 正文
创建我的复古网关系统
您可以在 13-cloud/myretro-gateway 文件夹中找到这一部分的代码。如果您想使用 Spring Initializr (https://start.spring.io) 从头开始,请将 Group 字段设置为 com.apress,将 Artifact 和 Name 字段都设置为 myretro-gateway,并将 Actuator、Consul Configuration、Consul Discovery、Reactive Gateway 和 Resilience4J Circuit Breaker 作为依赖项添加。其他所有设置保持默认。然后,生成并下载项目,解压缩后导入到您喜欢的 IDE 中。
让我们来审查一下 build.gradle 文件。请参见列表 13-13。
plugins {
id 'java'
id 'org.springframework.boot' version '3.2.2'
id 'io.spring.dependency-management' version '1.1.4'
}
group = 'com.apress'
version = '0.0.1-SNAPSHOT'
java {
sourceCompatibility = '17'
}
repositories {
mavenCentral()
}
ext {
set('springCloudVersion', "2023.0.0")
}
dependencies {
// Actuator
implementation 'org.springframework.boot:spring-boot-starter-actuator'
// Gateway
implementation 'org.springframework.cloud:spring-cloud-starter-gateway'
implementation 'org.springframework.cloud:spring-cloud-starter-circuitbreaker-reactor-resilience4j'
// Consul
implementation 'org.springframework.cloud:spring-cloud-starter-consul-config'
implementation 'org.springframework.cloud:spring-cloud-starter-consul-discovery'
// Kubernetes
implementation 'org.springframework.cloud:spring-cloud-starter-kubernetes-fabric8-all'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testImplementation 'io.projectreactor:reactor-test'
}
dependencyManagement {
imports {
mavenBom "org.springframework.cloud:spring-cloud-dependencies:${springCloudVersion}"
}
}
tasks.named('test') {
useJUnitPlatform()
}
列表 13-13 的 build.gradle 文件
列表 13-13 显示我们需要包含依赖项 spring-cloud-starter-gateway、spring-cloud-starter-circuitbreaker-reactor-resilience4j(将在本章稍后讨论)和 spring-cloud-starter-kubernetes-fabric8-all(在最后一节“使用云平台:Kubernetes”中会提到)。此外,我们还添加了 Consul 的依赖项,以便它能够自动注册到 Consul 服务器。Spring Cloud Gateway 的一个很酷的功能是,如果您已经在使用 Consul,它可以无需任何编程就利用本地服务。
Spring Cloud Gateway 是专为 WebFlux 设计的,因此您可以在常规 MVC 或反应式 Web 应用程序中使用这项技术。
接下来,打开或创建 application.yaml 文件。请参阅列表 13-14。
spring:
application:
name: myretro-gateway
config:
import: optional:consul://
cloud:
gateway:
routes:
- id: users
uri: lb://users-service
predicates:
- Path=/users/**
- id: myretro
uri: lb://my-retro-app
predicates:
- Path=/retros/**
server:
port: ${PORT:8080}
列表 13-14 源文件:src/main/resources/application.yaml
让我们一起回顾一下 application.yaml 文件:
- 请记住,当您想要使用服务发现时,始终需要这个属性。由于我们使用的是 Consul,这一点尤其重要。设置的值是 myretro-gateway,它会自动以这个名称注册到 Consul 中。
- spring.config.import:在添加 spring-cloud-starter-consul-config 依赖时,即使我们不使用它,也需要指定这个属性。在我们的示例中,我们不会将 Consul 作为外部配置使用,因此可以将其声明为可选的,这就是我们在其值开头添加这个内容的原因。
- spring.cloud.gateway.*: 这包括所有网关的声明配置。在这种情况下,我们定义了两个路由,一个指向 /users 端点,另一个指向 /retros 端点。用户路由的 uri 属性使用了服务名称(在这里是 users-service),并加上了 lb:// 前缀,这表示我们需要这样声明负载均衡器的使用;同时请注意,我们有两个 Users Services 应用实例在运行(分别在 8091 和 8092 端口)。我们为每个服务声明了 predicates.Path,值为 /users/** 和 /retros/**。 这意味着当我们通过 /users 访问网关时,它会重定向到 http://users-service/users(http://localhost:8091/users 或 http://localhost:8092/users,具体取决于负载均衡器,通常采用轮询方式)。
当然,还有更多的配置选项,但我们已经涵盖了与服务发现和负载均衡相关的基本选项。
就这样!不需要任何编程或其他操作。
启动我的复古网关
在运行 My Retro Gateway 之前,请确保所有服务(consul、vault、users-service 和 my-retro-app)都已启动并且在 Consul 中可见。
要运行 My Retro Gateway 应用,您可以使用您的 IDE 或者以下命令:
./gradlew bootRun
默认情况下,应用程序运行在 8080 端口。如果您打开 Consul UI(http://localhost:8500),可以查看所有服务。请参见图 13-16。
图 13-16 Consul UI 服务部分,显示所有服务的列表(http://localhost:8500/ui/dc1/services)
现在您已经成功启动了 My Retro Gateway,是时候进行测试了。您可以选择使用浏览器或终端。如果您使用终端,可以执行以下命令来查看用户:
curl -s http://localhost:8080/users | jq
[
{
"email": "ximena@email.com",
"name": "Ximena",
"gravatarUrl": "https://www.gravatar.com/avatar/23bb62a7d0ca63c9a804908e57bf6bd4?d=wavatar",
"password": "aw2s0meR!",
"userRole": [
"USER"
],
"active": true
},
{
"email": "norma@email.com",
"name": "Norma",
"gravatarUrl": "https://www.gravatar.com/avatar/f07f7e553264c9710105edebe6c465e7?d=wavatar",
"password": "aw2s0meR!",
"userRole": [
"USER",
"ADMIN"
],
"active": false
}
]
如果你执行以下命令,你将获得回溯信息:
curl -s http://localhost:8080/retros | jq
[
{
"retroBoardId": "4efaf18e-2141-40c5-abff-0c0951d62938",
"name": "Spring Boot 3 Retro",
"cards": [
{
"cardId": "d9df505f-3564-4104-a339-415db9f4a29a",
"comment": "Nice to meet everybody",
"cardType": "HAPPY",
"created": "2024-02-12 13:17:30",
"modified": "2024-02-12 13:17:30"
},
{
"cardId": "3906a3cb-69cc-412f-8965-42979ac91052",
"comment": "When are we going to travel?",
"cardType": "MEH",
"created": "2024-02-12 13:17:30",
"modified": "2024-02-12 13:17:30"
},
{
"cardId": "fdbce1c8-f9ec-45fd-873b-b4ab43c2aff5",
"comment": "When are we going to travel?",
"cardType": "SAD",
"created": "2024-02-12 13:17:30",
"modified": "2024-02-12 13:17:30"
}
],
"created": "2024-02-12 13:17:30",
"modified": "2024-02-12 13:17:30"
}
]
但是如果你关闭两个用户服务应用,会发生什么呢?要了解情况,请这样做,然后执行
curl -s http://localhost:8080/users | jq
{
"timestamp": "2024-02-12T20:14:12.361+00:00",
"path": "/users",
"status": 503,
"error": "Service Unavailable",
"requestId": "2a02d2b8-2"
}
那么,在服务中断时,我们如何添加默认消息或虚拟用户呢?可以使用在下一节中讨论的断路器模式。
更多网关特性
Spring Cloud Gateway 拥有许多其他功能,例如谓词和过滤器。谓词的示例包括 After、Before、Between、Cookie、Header、Host、Method、Path、Query、ReadBody、RemoteAddr、XForwardedRemoteAddr、Weight、CloudFoundryRouteService 以及自定义选项。过滤器的种类繁多,以下是一些按字母顺序排列到字母 J 的示例:AddRequestHeader、AddRequestHeaderIfNotPresent、AddRequestParameter、AddResponseHeader、CircuitBreaker、CacheResponseBody、DedupeResponseHeader、FallbackHeaders 和 JsonToGrpc。如果现有的过滤器无法满足您的业务逻辑需求,您还可以选择创建自定义过滤器。让我们来回顾一下这些谓词和过滤器。
断路器过滤器
要理解 CircuitBreaker 过滤器的目的,首先需要了解 Circuit Breaker 模式。Circuit Breaker 模式是一种弹性机制,用于保护系统免受级联故障的影响,当依赖的服务或资源不可用或过载时,它就像一个自动开关,可以处于三种状态之一:
- 关闭:正常运行;请求已转发至服务。
- 打开:服务故障阈值已达到,请求会立即被中断并转向备用机制(例如,缓存响应或替代服务)。
- 半开状态:在超时后,单个请求会探测服务。如果请求成功,电路将关闭;如果失败,电路将保持开放更长时间。
该模式可以防止对故障服务的进一步负担,并允许系统在问题解决时继续平稳运行。CircuitBreaker 过滤器是基于电路断路器模式的。
将 CircuitBreaker 过滤器添加到我的 Retro Gateway 应用中
让我们在 My Retro Gateway 应用的 application.yaml 文件中添加 CircuitBreaker 过滤器。您可以在 spring.cloud.gateway.routes 的用户部分添加以下代码片段:
filters:
- name: CircuitBreaker
args:
name: users
fallbackUri: forward:/fallback/users
我们正在定义过滤器的名称为 CircuitBreaker,并声明它所需的一些参数,包括名称和 fallbackUri。这意味着如果/users 端点无法访问,它将默认转向/fallback/users 端点。完整的配置详见列表 13-15。
spring:
application:
name: myretro-gateway
config:
import: optional:consul://
cloud:
gateway:
routes:
- id: users
uri: lb://users-service
predicates:
- Path=/users/**
filters:
- name: CircuitBreaker
args:
name: users
fallbackUri: forward:/fallback/users
- id: myretro
uri: lb://my-retro-app
predicates:
- Path=/retros/**
filters:
- name: CircuitBreaker
args:
name: retros
fallbackUri: forward:/fallback/retros
server:
port: ${PORT:8080}
列表 13-15 src/main/resources/application.yaml 中的 CircuitBreaker
列表 13-15 展示了 application.yaml 文件的最终版本。关于 fallbackUri,我们声明了转发到新的路由/fallback/users 和/fallback/retros。这意味着我们需要实现这些回退功能。因此,请打开或创建 MyRetroFallbackController 类。请参见列表 13-16。
package com.apress.myretrogateway;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.time.LocalDateTime;
import java.util.Map;
@RestController
@RequestMapping("/fallback")
public class MyRetroFallbackController {
@GetMapping("/users")
public ResponseEntity userFallback() {
return ResponseEntity.ok(Map.of(
"status","Service Down",
"message","/users endpoint is not available at this moment",
"time",LocalDateTime.now(),
"data", Map.of(
"email","dummy@email.com",
"name","Dummy",
"password","dummy",
"active",false)
));
}
@GetMapping("/retros")
public ResponseEntity retroFallback() {
return ResponseEntity.ok(Map.of("status","Service Down","message","/retros endpoint is not available at this moment","time",LocalDateTime.now()));
}
}
列表 13-16 源代码:src/main/java/com/apress/myretrogateway/MyRetroFallbackController.java
列表 13-16 展示了 MyRetroFallbackController 类。你对此已经很熟悉,因此无需解释。实际上,你使用的是一个默认响应,这不一定非得像我们这里用 Map 的方式,你也可以联系一个你知道永远不会宕机的其他服务。
所以,如果你想进行测试,可以关闭用户服务应用的两个实例,然后用新的配置重新启动我的复古网关。请在/users 端点执行以下命令:
curl -s http://localhost:8080/users | jq
{
"message": "/users endpoint is not available a this moment",
"status": "Service Down",
"data": {
"active": false,
"name": "Dummy",
"email": "dummy@email.com",
"password": "dummy"
},
"time": "2024-02-12T16:00:34.087459"
}
现在,您可以尝试关闭 my-retro-app 服务,然后通过 /retros 进行调用,以查看控制器的消息。
在 Docker Compose 中整合云环境
到目前为止,我们一直是单独处理每个组件,现在让我们在这个单一文件中一起处理所有内容。我们使用 Docker Compose 来搭建整个环境。
接下来,我们来定义 compose.yaml 文件。您可以在 13-cloud/docker-compose 文件夹中找到这个文件。请参见列表 13-17。
services:
## HashiCorp Consul
consul-server:
hostname: consul-server
container_name: consul-server
image: consul:1.15.4
restart: always
networks:
- cloud
healthcheck:
test: ["CMD", "curl", "-X", "GET", "localhost:8500/v1/status/leader"]
interval: 1s
timeout: 3s
retries: 60
command: "agent -server -ui -node=server-1 -bootstrap-expect=1 -client=0.0.0.0"
consul-client:
hostname: consul-client
container_name: consul-client
image: consul:1.15.4
restart: always
networks:
- cloud
command: "agent -node=client-1 -join=consul-server -retry-join=172.17.0.2"
consul-init:
hostname: consul-init
container_name: consul-init
image: consul:1.15.4
depends_on:
consul-server:
condition: service_healthy
volumes:
- ./consul-init.sh:/tmp/consul-init.sh
networks:
- cloud
command: |
sh -c "/tmp/consul-init.sh"
## PostgreSQL
postgres:
hostname: postgres
container_name: postgres
image: postgres
platform: linux/amd64
restart: always
networks:
- cloud
environment:
POSTGRES_PASSWORD: mysecretpassword
POSTGRES_USER: admin
POSTGRES_DB: users_db
ports:
- "5432:5432"
healthcheck:
test: pg_isready
interval: 10s
timeout: 5s
retries: 5
## Users Service
users-service:
image: users
build:
context: ../users
dockerfile: Dockerfile
restart: always
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://postgres:5432/users_db
- SPRING_CLOUD_CONSUL_HOST=consul-server
networks:
- cloud
depends_on:
consul-server:
condition: service_healthy
postgres:
condition: service_healthy
## My Retro App
my-retro-app:
image: myretro
build:
context: ../myretro
dockerfile: Dockerfile
environment:
- SPRING_CLOUD_CONSUL_HOST=consul-server
networks:
- cloud
depends_on:
consul-server:
condition: service_healthy
## Gateway
myretro-gateway:
image: myretro-gateway
build:
context: ../myretro-gateway
dockerfile: Dockerfile
networks:
- cloud
environment:
- SPRING_CLOUD_CONSUL_HOST=consul-server
depends_on:
consul-server:
condition: service_healthy
ports:
- "8080:8080"
## Networks
networks:
cloud:
name: cloud
#external: true
列表 13-17 compose.yaml
列表 13-17 显示,我们只需定义网关,而无需定义任何出口端口。在继续之前,请仔细查看列表 13-17,并注意我们需要为每个 Spring Boot 服务添加 SPRING_CLOUD_CONSUL_HOST 环境变量,以及为用户服务添加 SPRING_DATASOURCE_URL。此外,我们还有一个 consul-init 服务,用于初始化所需的属性。
现在你可以启动环境了
docker compose up
第一次运行此命令时,由于我们需要构建所有服务,因此会花费一些时间。之后的运行会非常快速。启动并运行后,您可以通过向 /users 和 /retros 端点发送请求来进行测试:
curl -s http://localhost:8080/users | jq
curl -s http://localhost:8080/retros | jq
要停止服务,您可以打开一个新的终端,切换到包含 compose.yaml 的目录,然后执行命令。
docker compose down
在每个项目(用户、myretro、myretro-gateway)中,我都创建了以下的 Dockerfile:
FROM eclipse-temurin:17-jdk-jammy AS build
WORKDIR /workspace/app
COPY . /workspace/app
RUN --mount=type=cache,target=/root/.gradle ./gradlew clean build -x test
RUN mkdir -p build/dependency && (cd build/dependency; jar -xf ../libs/*-SNAPSHOT.jar)
FROM eclipse-temurin:17-jdk-jammy
VOLUME /tmp
ARG DEPENDENCY=/workspace/app/build/dependency
COPY --from=build ${DEPENDENCY}/BOOT-INF/lib /app/lib
COPY --from=build ${DEPENDENCY}/META-INF /app/META-INF
COPY --from=build ${DEPENDENCY}/BOOT-INF/classes /app
ENTRYPOINT ["java","-cp","app:app/lib/*","com.apress.users.UsersApplication"]
这个文件是 docker-compose 用来构建镜像的。因此,它会创建每个镜像,并且这些镜像应该在您的本地 Docker 注册表中。您可以通过以下命令进行检查。
docker images | grep -E "retro|users"
如果你没有任何内容,可以在每个项目文件夹中使用以下命令来构建镜像:
# cd users
docker build -t users .
# cd myretro
docker build -t myretro .
# cd myretro-gateway
docker build -t myretro-gateway .
创建多种架构的镜像
Docker 支持多架构,允许您创建一个可以在不同计算机系统上无缝运行的单一 Docker 镜像,例如使用 ARM 或 x86 处理器的系统。这就像拥有一个可以控制不同品牌电视的通用遥控器——一个镜像可以在各种设备上运行,而无需为每种设备单独创建版本。这是通过将多个版本的应用程序(每个版本针对特定架构编译)打包到一个镜像中实现的。Docker 会自动选择适合其运行设备的正确版本,从而使部署变得更加简单和灵活。我正在使用 M3 Mac,因此默认情况下,一些架构层是基于 ARM64 的,但我们如何实现多架构支持,例如 Linux AMD64?
Docker 的最新版本提供了--platform 参数,使我们能够创建多架构镜像。要创建 ARM64 和 AMD64/Intel 架构的镜像,您可以执行以下命令:
docker build \
--push \
--platform linux/arm64,linux/amd64 \
--tag <your-username>/<your-image-name>:<your-tag> .
这个命令不仅会创建您的镜像,还会添加所需的操作系统架构层(ARM64/AMD64),并将镜像推送到 Docker Hub 注册表(--push)。这意味着您需要登录到 Docker 注册表(我建议您在 https://hub.docker.com/上创建一个免费的账户)。当然,您可以去掉--push,但在下一节中我们会需要它。
如果在执行之前的命令时遇到“错误:docker 驱动程序不支持多平台构建”的提示,您需要启用 containerd 功能。请查看此链接以获取更多信息:https://docs.docker.com/desktop/containerd/#build-multi-platform-images。
因此,对于用户项目,我们可以进行执行
docker build \
--push \
--platform linux/arm64,linux/amd64 \
--tag felipeg48/users:latest .
关于 myretro 项目:
docker build \
--push \
--platform linux/arm64,linux/amd64 \
--tag felipeg48/myretro:latest .
关于 myretro-gateway 项目:
docker build \
--push \
--platform linux/arm64,linux/amd64 \
--tag felipeg48/myretro-gateway:latest .
你可以访问 Docker Hub 来查看和管理你的镜像。例如,请参见图 13-17。
在 Docker Hub 上查看镜像 (https://hub.docker.com/repository/docker/felipeg48/users/tags)
现在我们可以开始讨论云平台了!
猜你喜欢
- 2025-01-09 Spring Boot集成Redis Search快速入门Demo
- 2025-01-09 Spring Boot 3.x嵌入MongoDB 进行测试
- 2025-01-09 java安全之fastjson链分析
- 2025-01-09 MyBatis初级实战之五:一对一关联查询
- 2025-01-09 精通Spring Boot 3 : 8. Spring Boot 测试 (2)
- 2025-01-09 DevSecOps 管道: 使用Jenkins实现安全的多语言应用程序
- 2025-01-09 Liquibase+Spring+Maven: 管理数据库轻松搞定
- 2025-01-09 比较一下JSON与XML两种数据格式?
- 2025-01-09 Java批量导入时,如何去除重复数据并返回结果?
- 2025-01-09 Spring Boot集成Mockito快速入门Demo
- 02-21走进git时代, 你该怎么玩?_gits
- 02-21GitHub是什么?它可不仅仅是云中的Git版本控制器
- 02-21Git常用操作总结_git基本用法
- 02-21为什么互联网巨头使用Git而放弃SVN?(含核心命令与原理)
- 02-21Git 高级用法,喜欢就拿去用_git基本用法
- 02-21Git常用命令和Git团队使用规范指南
- 02-21总结几个常用的Git命令的使用方法
- 02-21Git工作原理和常用指令_git原理详解
- 最近发表
- 标签列表
-
- cmd/c (57)
- c++中::是什么意思 (57)
- sqlset (59)
- ps可以打开pdf格式吗 (58)
- phprequire_once (61)
- localstorage.removeitem (74)
- routermode (59)
- vector线程安全吗 (70)
- & (66)
- java (73)
- org.redisson (64)
- log.warn (60)
- cannotinstantiatethetype (62)
- js数组插入 (83)
- resttemplateokhttp (59)
- gormwherein (64)
- linux删除一个文件夹 (65)
- mac安装java (72)
- reader.onload (61)
- outofmemoryerror是什么意思 (64)
- flask文件上传 (63)
- eacces (67)
- 查看mysql是否启动 (70)
- java是值传递还是引用传递 (58)
- 无效的列索引 (74)