SmallRye Stork 揭秘:探索新功能和增强功能

自 2022 年 1 月首次发布以来,Stork 经历了显著的发展,引入了扩展其功能并改善开发者体验的新功能。

这篇博文将深入探讨 SmallRye Stork 在首次发布后的演变过程,详细介绍其新增功能。但首先,让我们简要介绍一下 Stork 可以为您做什么。SmallRye Stork 是一个客户端服务发现和选择框架。它提供与 Kubernetes、Eureka 和 Hashicorp Consul 的开箱即用集成,以及一系列选择策略,包括轮询、两选其一和最佳响应时间。但 Stork 最值得注意的特点是其可扩展性。您可以创建自己的服务选择策略或插入自己的服务发现机制。如果您还不了解,一个好的入门方式是查看我们的 入门指南

此外,我们的文档也得到了增强,为经验丰富的用户和初次接触 Stork 的用户提供了全面的指南。为了进一步支持您的探索,还有 一个视频 和补充内容详细展示了 Stork 的功能,请随时查看。时间不多?不用担心,我们有 完美的视频,让您在一分钟内了解 Stork。

通过最新增加的功能,我们强调 Stork 如何继续重塑客户端服务发现和选择领域。

现在让我们来看看自首次发布以来 Stork 最有趣的新增功能。

编程方式的服务定义

最初,您必须在应用程序配置中配置 Stork。您需要为要发现和选择的每个服务配置服务发现和选择(可选)。

从 1.2.0 版本开始,Stork 提供了一个编程 API,允许用户通过代码而非声明式或外部配置文件来定义服务发现和选择配置。这意味着您可以使用 Java 的全部表达能力来显式指定新的服务定义,并进行手动查找和选择。这在应用程序的配置需求在运行时才能确定,并且可以在不重新启动应用程序的情况下调整设置时特别有益。

在使用 Stork 的编程 API 时,您可以

  • 检索单例 Stork 实例。此实例配置了它管理的 Services 集合。

  • 注册新的服务定义。

  • 检索您想要使用的 Service。每个 Service 都与一个名称相关联。

  • 检索 ServiceInstance,它将提供访问实际实例的元数据。

在下面的代码中,我们使用 Stork 编程 API 来设置和配置具有不同发现方法和服务选择策略的服务

package examples;

import io.smallrye.stork.Stork;
import io.smallrye.stork.api.ServiceDefinition;
import io.smallrye.stork.loadbalancer.random.RandomConfiguration;
import io.smallrye.stork.servicediscovery.consul.ConsulRegistrarConfiguration;
import io.smallrye.stork.servicediscovery.staticlist.StaticConfiguration;
import io.smallrye.stork.servicediscovery.staticlist.StaticRegistrarConfiguration;

public class DefinitionExample {

    public static void example(Stork stork) {
        String example = "localhost:8080, localhost:8081";

        // A service using a static list of locations as discovery
        // As not set, it defaults to round-robin to select the instance.
        stork.defineIfAbsent("my-service",
                ServiceDefinition.of(new StaticConfiguration().withAddressList(example)));

        // Another service using the random selection strategy, instead of round-robin
        stork.defineIfAbsent("my-second-service",
                ServiceDefinition.of(new StaticConfiguration().withAddressList(example),
                        new RandomConfiguration()));

        // Another service using the random selection strategy, instead of round-robin
        // and a static service registrar
        stork.defineIfAbsent("my-second-service",
                ServiceDefinition.of(new StaticConfiguration().withAddressList(example),
                        new RandomConfiguration(), new StaticRegistrarConfiguration()));
    }
}

需要注意的是,在编程配置和声明式配置之间进行选择通常取决于您应用程序的特定需求和限制。

作为 CDI Bean 提供的服务发现和选择策略。

第二个显著的改进是与 CDI 的集成。

用户可能更喜欢使用一个利用 CDI 机制来轻松管理和注入依赖项,并拥有更易于测试和维护代码的框架。Stork 现在可以做到这一点。从 2.0.1 版本开始,用户可以将服务发现和负载均衡器作为 Bean 使用。为此,它在初始化期间除了 SPI 提供者外,还会查找 CDI Bean。值得一提的是,这一增强功能也有助于改善 Quarkus 的体验。

新增了服务发现方法。

我们很高兴宣布新增了使用 DNS 和 Knative 的一些服务发现策略。

通过 Knative 服务发现,Smallrye Stork 能够通过其无服务器基础设施实现无缝的服务发现,即使没有正在运行的“pod”。

Stork Knative 服务发现实现与 Kubernetes 的非常相似。

Stork 将会向集群请求 Knative 服务,而不是 Kubernetes 实现所使用的普通 Kubernetes 服务。同样,为了做到这一点,Stork 使用 Fabric 8 Knative Client,它只是 Fabric8 Kubernetes Client 的一个扩展。

基于 DNS 的服务发现也将持续存在。当一个服务在一个域名系统 (DNS) 服务器中注册了一个或多个实例时,Stork 将能够通过查询 DNS 来发现它们。这种策略简单且被广泛使用,所以 Stork 不能不实现它。

新的粘性服务选择策略

Stork 负载均衡器家族已扩展了一个新的实现:粘性服务选择实现。

Stork 实现的粘性服务选择是指一种策略,其中客户端“粘附”到服务的特定实例,直到该实例失败,然后选择另一个实例。还可以配置一个回退期,用于指定重试失败的服务实例的时间长度。这在保持与同一实例的一致连接有益的情况下可能很有用,例如在处理会话或有状态应用程序时。

增强的服务实例缓存过期策略。

自几乎第一次发布以来,Stork 一直通过扩展 CachingServiceDiscovery 类来提供发现实例的内存缓存。

截至 1.3 版本,此功能已扩展,允许在指定持续时间内保留缓存的服务实例,并实现自定义业务逻辑以进行决策和数据过期。

这一增强功能是由 Kubernetes 服务发现的特定需求驱动的,因为频繁联系集群可能导致性能问题。因此,开箱即用地,Stork Kubernetes 服务发现现在带有一个定制的缓存过期策略,可以保留服务实例,直到发生某个事件。

如果您想为您的自定义服务发现实现这样做,您需要

  • 如上所述扩展 CachingServiceDiscovery。

  • 实现定义了过期策略的 cache 方法。

  • 当过期条件评估为 true 时,使缓存无效。

查看下面的示例

package examples;

import io.smallrye.mutiny.Uni;
import io.smallrye.stork.api.ServiceInstance;
import io.smallrye.stork.impl.CachingServiceDiscovery;
import io.smallrye.stork.impl.DefaultServiceInstance;
import io.smallrye.stork.utils.ServiceInstanceIds;

import java.util.Collections;
import java.util.List;
import java.util.concurrent.atomic.AtomicBoolean;

public class CustomExpirationCachedAcmeServiceDiscovery extends CachingServiceDiscovery {

    private final String host;
    private final int port;

    private AtomicBoolean invalidated = new AtomicBoolean();

    public CustomExpirationCachedAcmeServiceDiscovery(CachedAcmeConfiguration configuration) {
        super(configuration.getRefreshPeriod()); // (1)
        this.host = configuration.getHost();
        this.port = Integer.parseInt(configuration.getPort());
    }

    @Override
    public Uni<List<ServiceInstance>> fetchNewServiceInstances(List<ServiceInstance> previousInstances) {
        // Retrieve services...
        DefaultServiceInstance instance =
                new DefaultServiceInstance(ServiceInstanceIds.next(), host, port, false);
        return Uni.createFrom().item(() -> Collections.singletonList(instance));
    }

    @Override
    public Uni<List<ServiceInstance>> cache(Uni<List<ServiceInstance>> uni) {
        return uni.memoize().until(() -> invalidated.get());
    }

    //command-based cache invalidation: user triggers the action to invalidate the cache.
    public void invalidate() {
        invalidated.set(true);
    }

}

您可以查看 Kubernetes 服务发现代码 以获取有关基于事件的无效化示例的更多详细信息。

可观察性

可观测性是指通过分析系统的外部输出来理解和深入了解系统的内部工作和行为的能力。Stork 可观测性支持已集成到 Quarkus 3.6.0 版本中(计划下周发布)。这一新增功能将自动可观测性带到了服务发现和选择的最前沿,让我们能够实时了解 Stork 的行为。现在您可以轻松监控服务发现和选择持续时间以及错误率等指标。

如果您在 Quarkus 应用程序中使用了 Stork,现在您可以轻松地直接在 Prometheus 中查看和分析服务发现和选择响应时间以及错误等指标。有关详细信息,请查看 Stork 参考指南

总而言之,Stork 中的所有这些进步都表明我们致力于改善您在服务发现和选择方面的体验。

请继续关注更多更新。您的反馈对我们来说非常宝贵,请分享您的反馈并为使 Stork 变得更好做出贡献。