响应式基础设施

本节介绍 Spring Vault 对响应式编程支持的基本信息。

什么是响应式编程?

简单来说,响应式编程是指非阻塞、异步且事件驱动的应用,它们只需少量线程即可垂直扩展(即在 JVM 内部扩展),而非水平扩展(即通过集群扩展)。

响应式应用的一个关键方面是背压(backpressure)的概念,这是一种确保生产者不会压垮消费者的机制。例如,在一个从数据库到 HTTP 响应的响应式组件管道中,当 HTTP 连接过慢时,数据仓库也可以减速或完全停止,直到网络容量释放出来。

响应式 Vault 客户端

Spring Vault 的响应式客户端支持构建在可组合的认证步骤和 Spring 的函数式 WebClient 之上,通过 Reactor Netty 或 Jetty 实现,两者都提供了完全非阻塞、事件驱动的 HTTP 客户端。

它暴露了 VaultTokenSupplier 作为 VaultToken 的提供者,用于认证 HTTP 请求,并暴露了 ReactiveVaultOperations 作为主要入口点。VaultEndpointClientOptionsSSL 的核心配置在各种客户端实现中被复用。

ReactiveVaultTemplate 类位于 org.springframework.vault.core 包中,是 Spring 响应式 Vault 支持的核心类,提供了丰富的功能集来与 Vault 交互。该模板提供了便捷的操作来读取、写入和删除 Vault 中的数据,并提供了您的领域对象与 Vault 数据之间的映射。

ReactiveVaultTemplate 配置完成后是线程安全的,可以在多个实例中复用。

Vault 文档与领域类之间的映射通过委托给 WebClient 及其编解码器来完成。

ReactiveVaultTemplate 类实现了 ReactiveVaultOperations 接口。ReactiveVaultOperations 上的方法尽可能地沿用了 Vault API 中可用方法的名称,以便那些习惯于 Vault API 和 CLI 的现有 Vault 开发者熟悉此 API。例如,您会找到诸如 “write”、“delete” 和 “read” 之类的方法。设计目标是使 Vault API 与 ReactiveVaultOperations 之间的转换尽可能容易。两个 API 之间的一个主要区别是,ReactiveVaultOperations 可以传递领域对象,而不是 JSON 键值对。

引用 ReactiveVaultTemplate 实例上操作的首选方式是通过其接口 ReactiveVaultOperations

对于 ReactiveVaultTemplate 未明确暴露的功能,您可以使用几种执行回调方法之一来访问底层 API。执行回调将为您提供一个 WebClient 对象的引用。请参阅执行回调部分了解更多信息。

现在让我们看看如何在 Spring 容器的上下文中使用 Vault 的示例。

注册和配置 Spring Vault bean

使用 Spring Vault 不需要 Spring Context。然而,在托管上下文内注册的 ReactiveVaultTemplateVaultTokenSupplier 实例将参与 Spring IoC 容器提供的生命周期事件。这对于在应用关闭时处理活跃的 Vault 会话非常有用。您还可以受益于在整个应用中复用同一个 ReactiveVaultTemplate 实例。

Spring Vault 提供了一个支持性配置类,该类提供了在 Spring 上下文内部使用的 bean 定义。应用程序配置类通常继承自 AbstractVaultConfiguration,并需要提供环境特定的额外详细信息。

继承 AbstractVaultConfiguration 需要实现 `VaultEndpoint vaultEndpoint()` 和 ClientAuthentication clientAuthentication() 方法。

示例 1. 使用基于 Java 的 bean 元数据注册 Spring Vault 对象
@Configuration
public class AppConfig extends AbstractReactiveVaultConfiguration {

    /**
     * Specify an endpoint for connecting to Vault.
     */
    @Override
    public VaultEndpoint vaultEndpoint() {
        return new VaultEndpoint();                            (1)
    }

    /**
     * Configure a client authentication.
     * Please consider a more secure authentication method
     * for production use.
     */
    @Override
    public ClientAuthentication clientAuthentication() {
        return new TokenAuthentication("…");                   (2)
    }
}
1 创建一个新的 VaultEndpoint,默认指向 https://localhost:8200
2 此示例使用 TokenAuthentication 快速入门。有关支持的认证方法的详细信息,请参阅[vault.core.authentication]

会话管理

Spring Vault 需要一个令牌来认证 Vault 请求。有关认证的详细信息,请参阅[vault.core.authentication]。响应式客户端需要一个非阻塞的令牌提供者,其契约在 VaultTokenSupplier 中定义。令牌可以是静态的,也可以通过声明的认证流程获取。Vault 登录不应在每次认证的 Vault 交互时发生,而应在整个会话期间保留会话令牌。这方面由实现 ReactiveSessionManager 的会话管理器处理,例如 ReactiveLifecycleAwareSessionManager

执行回调

所有 Spring 模板类的一个常见设计特性是所有功能都通过模板的执行回调方法之一进行路由。这有助于确保异常和可能需要的任何资源管理以一致的方式执行。虽然这在 JDBC 和 JMS 的情况下比 Vault 更为必要,但它仍然提供了一个进行访问和日志记录的单一位置。因此,使用执行回调是访问 Vault API 执行我们尚未在 ReactiveVaultTemplate 上暴露的非常规操作的首选方式。

以下是执行回调方法的列表。

  • <T> T doWithVault (Function<WebClient, ? extends T> clientCallback) 使用给定的 WebClient 组合一个响应式序列,允许在没有会话上下文的情况下与 Vault 交互。

  • <T> T doWithSession (Function<WebClient, ? extends T> clientCallback) 使用给定的 WebClient 组合一个响应式序列,允许在已认证的会话中与 Vault 交互。

以下是使用回调初始化 Vault 的示例

reactiveVaultOperations.doWithVault(webClient -> {

    return webClient.put()
                    .uri("/sys/init")
                    .syncBody(request)
                    .retrieve()
                    .toEntity(VaultInitializationResponse.class);
});