spring cloud commons-分布式程序概述

前言

Cloud Native是一种应用程序开发风格,它鼓励在持续交付和价值驱动开发领域轻松采用最佳实践。一个相关的规程是构建12因素应用程序,其中开发实践与交付和操作目标保持一致——例如,通过使用声明式编程、管理和监视。Spring Cloud以许多特定的方式促进了这些风格的开发。起点是一组特性,分布式系统中的所有组件都需要方便地访问这些特性。

Spring Boot包含了其中的许多特性,Spring Cloud构建在Spring Boot之上。Spring Cloud还提供了两个库:Spring Cloud Context和Spring Cloud Commons。Spring Cloud上下文为Spring Cloud应用程序的ApplicationContext(引导上下文、加密、刷新范围和环境端点)提供了实用程序和特殊服务。Spring Cloud Commons是在不同的Spring Cloud实现(例如Spring Cloud Netflix和Spring Cloud Consul)中使用的一组抽象和公共类。

如果由于“非法密钥大小”而出现异常,并且使用Sun的JDK,则需要安装Java Cryptography Extension (JCE)无限制权限策略文件。有关更多信息,请参阅以下链接:

  • Java 6 JCE
  • Java 7 JCE
  • Java 8 JCE

对于使用的JRE/JDK x64/x86版本,将文件解压到JDK/jre/lib/security文件夹中。

Spring Cloud Context:应用程序上下文服务

Spring Boot对于如何用Spring构建应用程序有自己的看法。例如,它有用于通用配置文件的常规位置,并有用于通用管理和监视任务的端点。Spring Cloud构建在此之上,并添加了一些系统中的许多组件会使用或偶尔需要的特性。

引导应用程序上下文

Spring Cloud应用程序通过创建一个“bootstrap”上下文来操作,这个上下文是主应用程序的父上下文。此上下文负责从外部源加载配置属性,并对本地外部配置文件中的属性进行解密。这两个上下文共享一个Environment,该Environment是任何Spring应用程序的外部属性的源。默认情况下,引导程序属性(不是bootstrap.properties但是在引导阶段加载的属性)以较高的优先级添加,因此本地配置不能覆盖它们。

引导上下文使用与主应用程序上下文不同的约定来定位外部配置。您可以使用bootstrap.yml,而不是application.yml(或.properties)。将引导程序的外部配置和主上下文很好地分开。

bootstrap.yml

spring:
  application:
    name: foo
  cloud:
    config:
      uri: ${SPRING_CONFIG_URI:http://localhost:8888}

如果您的应用程序需要从服务器进行任何特定于应用程序的配置,那么设置spring.application.name(在bootstrap.ymlapplication.yml中)是一个好主意。要将属性spring.application.name用作应用程序的上下文ID,必须在bootstrap.[properties | yml]中设置它。

如果您想检索特定的配置文件,您还应该在bootstrap.[properties | yml]中设置spring.profiles.active

您可以通过设置spring.cloud.bootstrap.enabled=false来完全禁用引导过程(例如在系统属性中)。

Application Context层次结构

如果您从SpringApplication或SpringApplicationBuilder构建应用程序上下文,则引导上下文将作为父上下文添加到该上下文。子上下文从父上下文继承属性源和概要文件是Spring的一个特性,因此与不使用Spring Cloud Config构建相同的上下文相比,“主”应用程序上下文包含额外的属性源。

  • “bootstrap”:如果在引导上下文中找到了propertysourcelocator,并且它们具有非空属性,那么就会出现一个可选的CompositePropertySource。一个例子是来自Spring Cloud Config Server的属性。参见“自定义引导属性源”了解如何自定义这个属性源的内容。
  • “applicationConfig:[classpath:bootstrap.yml]”(以及相关的文件,如果Spring配置文件是活动的):如果你有一个bootstrap.yml(或.properties),这些属性用于配置引导上下文。然后当父上下文被设置时,它们被添加到子上下文中。它们的优先级低于application.yml(或.properties)和作为创建Spring引导应用程序过程的普通部分添加到子程序的任何其他属性源。有关如何自定义这些属性源的内容,请参阅“更改引导属性的位置”。

由于属性源的排序规则,“bootstrap”条目优先。但是,请注意,这些不包含任何来自bootstrap.yml的数据,优先级非常低,但可用于设置默认值。

您可以通过设置您创建的任何ApplicationContext的父上下文来扩展上下文层次结构—例如,通过使用它自己的接口或SpringApplicationBuilder便利方法(parent()、child()和sibling())。引导上下文是您自己创建的最高级祖先的父。层次结构中的每个上下文都有自己的“bootstrap”(可能是空的)属性源,以避免无意中将值从父级提升到其子级。如果有配置服务器,那么层次结构中的每个上下文(原则上)也可以有不同的spring.application.name,因此也可以有不同的远程属性源。正常的Spring应用程序上下文行为规则适用于属性解析:根据名称和属性源名称,子上下文中的属性会覆盖父上下文中的属性。(如果子元素具有与父元素同名的属性源,则父元素的值不会包含在子元素中)。

请注意,SpringApplicationBuilder允许在整个层次结构中共享Environment,但这不是默认的。因此,兄弟上下文(特别是)不需要具有相同的配置文件或属性源,即使它们可能与父上下文共享相同的值。

更改引导程序属性的位置

bootstrap.yml(或.properties)位置可以通过设置spring.cloud.bootstrap.name(默认:bootstrap), spring.cloud.bootstrap.location来指定(默认:空)或spring.cloud.bootstrap.additional-location(默认为空)——例如,在系统属性中。

这些属性的行为与spring.config.*相同名称的变体类似。spring.cloud.bootstrap.location替换默认位置,只使用指定的位置。要将位置添加到默认位置列表中,请使用spring.cloud.bootstrap.additional-location可以使用。实际上,它们用于通过在其Environment中设置这些属性来设置引导ApplicationContext。如果有一个活动的配置文件(来自spring.profiles.active或通过您正在构建的上下文中的Environment API),该配置文件中的属性也会被加载,与常规Spring引导应用程序相同——例如,从bootstrap-development.propertiesdevelopment配置文件。

重写远程属性的值

由引导上下文添加到应用程序中的属性源通常是“远程的”(例如,来自Spring Cloud Config Server)。如果您想让您的应用程序用它们自己的系统属性或配置文件覆盖远程属性,远程属性源必须通过设置spring.cloud.config.allowOverride=true来授予它权限(不能在本地设置此值)。一旦设置了这个标志,两个更细粒度的设置将控制远程属性与系统属性和应用程序的本地配置的关系:

  • spring.cloud.config.overrideNone=true:覆盖任何本地属性源。
  • spring.cloud.config.overrideSystemProperties=false:只有系统属性、命令行参数和环境变量(而不是本地配置文件)应该覆盖远程设置。

自定义引导配置

通过在名为org.springframework.cloud.bootstrap.BootstrapConfiguration的键下向/META-INF/spring.factories添加条目,可以将引导上下文设置为执行任何您喜欢的操作。它包含一个逗号分隔的Spring @Configuration类列表,这些类用于创建上下文。您想要在主应用程序上下文中用于自动装配的任何bean都可以在这里创建。有一个针对ApplicationContextInitializer类型的@Bean的特殊契约。如果希望控制启动顺序,可以使用@Order注释标记类(默认顺序为last)。

在添加自定义BootstrapConfiguration时,要小心不要将添加的类@ComponentScan错误地扫描到“主”应用程序上下文中,在那里它们可能不需要。为引导配置类使用单独的包名,并确保您的@ComponentScan@SpringBootApplication注释配置类没有涵盖该名称。

引导过程通过将初始化器注入主SpringApplication实例(这是正常的Spring引导启动顺序,无论它作为独立应用程序运行还是部署在应用服务器中)结束。首先,从spring.factories中找到的类创建一个引导上下文。然后,在主SpringApplication启动之前,将所有类型为ApplicationContextInitializer@Bean添加到主SpringApplication中。

自定义引导属性源

由引导过程添加的外部配置的默认属性源是Spring Cloud Config Server,但是您可以通过向引导上下文(通过spring.factories)添加PropertySourceLocator类型的bean来添加其他源。例如,可以从不同的服务器或数据库插入其他属性。

作为一个例子,考虑以下自定义定位器:

@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {

    @Override
    public PropertySource locate(Environment environment) {
        return new MapPropertySource("customProperty",
                Collections.singletonMap("property.from.sample.custom.source", "worked as intended"));
    }

}

传入的Environment是即将创建的ApplicationContext的环境—换句话说,是我们为其提供附加属性源的环境。它已经有了由Spring引导提供的普通属性源,因此您可以使用它们来定位特定于此Environment的属性源(例如,通过将其键入spring.application.name,就像在缺省的Spring Cloud Config Server属性源定位器中所做的那样)。

如果您创建一个包含此类的jar,然后添加一个包含以下设置的META-INF/spring.factories,则customProperty PropertySource将出现在任何在其类路径中包含该jar的应用程序中:

org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator

日志配置

如果您使用Spring Boot配置日志设置,则应该将此配置放在bootstrap.[yml | properties]中如果你想要它适用于所有事件。

要使Spring Cloud正确地初始化日志配置,您不能使用自定义前缀。例如,在初始化日志系统时,Spring Cloud不能识别使用custom.loggin.logpath

环境变化

应用程序侦听环境变更事件,并以两种标准方式对变更作出反应(可以按常规方式以@Bean的形式添加其他ApplicationListeners)。当观察到一个EnvironmentChangeEvent时,它有一个已经改变的键值列表,应用程序使用它们来:

  • 在上下文中重新绑定任何@ConfigurationProperties bean。
  • logging.level.*中的任何属性设置日志程序级别。

注意,在默认情况下,Spring Cloud Config客户机不会轮询Environment中的更改。通常,我们不推荐使用这种方法来检测更改(尽管您可以使用@Scheduled注释来设置它)。如果您有一个扩展的客户机应用程序,最好将EnvironmentChangeEvent广播到所有实例,而不是让它们轮询更改(例如,通过使用Spring Cloud Bus)。

只要您能够对Environment进行实际更改并发布事件,EnvironmentChangeEvent就会涵盖大量的刷新用例。注意,这些api是公共的,并且是核心Spring的一部分。可以通过访问/configprops端点(标准的Spring引导执行器特性)来验证更改是否绑定到@ConfigurationProperties bean。例如,数据源可以在运行时更改其maxPoolSize(Spring Boot创建的默认数据源是@ConfigurationProperties bean)并动态增长容量。重新绑定@ConfigurationProperties没有涵盖另一个大的用例类,在这些用例中,您需要对刷新进行更多的控制,并且需要对整个ApplicationContext进行原子化的更改。为了解决这些问题,我们使用了@RefreshScope

刷新范围

当有配置更改时,标记为@RefreshScope的Spring @Bean会得到特殊处理。这个特性解决了只有在初始化时才注入配置的有状态bean的问题。例如,如果DataSource在通过Environment更改数据库URL时有打开连接,您可能希望这些连接的持有者能够完成他们正在做的事情。然后,下一次从池中借用连接时,它将获得一个带有新URL的连接。

有时,甚至可能必须在只能初始化一次的bean上应用@RefreshScope注释。如果一个bean是“不可变的”,你必须要么用@RefreshScope注释这个bean,要么在属性键下指定类名:spring.cloud.refresh.extra refreshable。如果您有一个是HikariDataSourceDataSource bean,那么它就不能被刷新。它是spring.cloud.refresh.never-refreshable的默认值。

刷新范围bean是惰性代理,在使用它们时(即在调用方法时)进行初始化,范围充当初始化值的缓存。要在下一次方法调用时强制bean重新初始化,必须使其缓存项无效。

RefreshScope是上下文中的一个bean,它有一个公共refreshAll()方法,通过清除目标缓存刷新范围中的所有bean。/refresh端点公开此功能(通过HTTP或JMX)。要按名称刷新单个bean,还有一个refresh(String)方法。

要公开/refresh端点,需要在应用程序中添加以下配置:

management:
  endpoints:
    web:
      exposure:
        include: refresh

@RefreshScope(技术上)在@Configuration类上工作,但是它可能会导致令人吃惊的行为。例如,这并不意味着在该类中定义的所有@Bean都在@RefreshScope中。特别地,在启动刷新时,依赖于这些bean的任何东西都不能依赖于它们的更新,除非它本身在@RefreshScope中。在这种情况下,会在刷新时重新生成它,并重新注入它的依赖项。此时,它们将从刷新的@Configuration中重新初始化。

加解密

Spring Cloud有一个用于本地解密属性值的Environment预处理器。它遵循与Spring Cloud Config Server相同的规则,并通过encrypt.*进行相同的外部配置。因此,您可以使用以{cipher}*的形式加密的值,只要存在有效的密钥,就会在主应用程序上下文获得Environment设置之前对它们进行解密。要在应用程序中使用加密特性,您需要在类路径中包含Spring Security RSA (Maven地址:org.springframework.security: spring-security-rsa),并且您还需要在JVM中包含完整的强大JCE扩展。

如果由于“Illegal key size”而出现异常,并且使用Sun的JDK,则需要安装Java Cryptography Extension (JCE)无限制权限策略文件。有关更多信息,请参阅以下链接:

  • Java 6 JCE
  • Java 7 JCE
  • Java 8 JCE

对于使用的jre/ JDK x64/x86版本,将文件解压到JDK/jre/lib/security文件夹中。

端点

对于Spring Boot Actuator应用程序,可以使用其他一些管理端点。您可以使用:

  • POST/actuator/env以更新Environment并重新绑定@ConfigurationProperties和日志级别。
  • /actuator/refresh重新加载引导带上下文并刷新@RefreshScope bean。
  • /actuator/restart关闭ApplicationContext并重新启动它(默认禁用)。
  • 用于调用Lifecycle方法(ApplicationContext中的stop()start())的/actuator/pause/actuator/resume

如果禁用/actuator/restart,那么也将禁用/actuator/pause/actuator/resume端点,因为它们只是/actuator/restart的特殊情况。

Spring Cloud Commons:公共抽象

服务发现、负载平衡和断路器等模式将它们自己提供给一个公共抽象层,可以由所有Spring Cloud客户机使用,而不依赖于实现(例如,使用Eureka或Consul进行发现)。

@EnableDiscoveryClient注释

Spring Cloud Commons提供了@EnableDiscoveryClient注释。它通过META-INF/spring.factories寻找DiscoveryClientReactiveDiscoveryClient接口的实现。发现客户端的实现将配置类添加到spring.factoriesorg.springframework.cloud.client.discovery.EnableDiscoveryClient键下。DiscoveryClient实现的例子包括Spring Cloud Netflix Eureka、Spring Cloud Consul Discovery和Spring Cloud Zookeeper Discovery。

在默认情况下,Spring Cloud将同时提供阻塞和反应性服务发现客户机。您可以通过设置spring.cloud.discovery.blocking.enabled=falsespring.cloud.discovery.reactive.enabled=false轻松禁用阻塞和或反应性客户端。要完全禁用服务发现,您只需设置spring.cloud.discovery.enabled=false

默认情况下,DiscoveryClient的实现会自动将本地Spring引导服务器注册到远程发现服务器。可以通过在@EnableDiscoveryClient中设置autoRegister=false来禁用此行为。

@EnableDiscoveryClient不再是必需的。您可以将DiscoveryClient实现放在类路径上,以导致Spring引导应用程序向服务发现服务器注册。

健康指示器

Commons创建了一个Spring Boot HealthIndicator, DiscoveryClient实现可以通过实现DiscoveryHealthIndicator参与其中。要禁用复合HealthIndicator,请设置spring.cloud.discovery.client.composite-indicator.enabled=false。基于DiscoveryClient的通用HealthIndicator是自动配置的(DiscoveryClientHealthIndicator)。要禁用它,请设置spring.cloud.discovery.client.health-indicator.enabled=false。若要禁用DiscoveryClientHealthIndicator的描述字段,请设置spring.cloud.discovery.client.health-indicator.include-description=false。否则,它就会像description卷起来的HealthIndicator一样冒泡起来。

排序DiscoveryClient实例

DiscoveryClient接口扩展Ordered。这在使用多个发现客户机时非常有用,因为它允许您定义返回的发现客户机的顺序,类似于您如何排序Spring应用程序加载的bean。默认情况下,任何DiscoveryClient的顺序都设置为0。如果您想为您的自定义DiscoveryClient实现设置不同的顺序,您只需覆盖getOrder()方法,以便它返回适合您的设置的值。除此之外,您可以使用属性来设置Spring Cloud提供的DiscoveryClient实现的顺序,以及其他的ConsulDiscoveryClientEurekaDiscoveryClientZookeeperDiscoveryClient。为此,您只需要设置spring.cloud.{clientIdentifier}.discovery.order(或Eureka的eureka.client.order)属性到期望的值。

SimpleDiscoveryClient

如果类路径中没有服务注册支持的DiscoveryClient,那么将使用使用属性来获取服务和实例信息的SimpleDiscoveryClient实例。

可用实例的信息应该通过属性传递到下列格式:spring.cloud.discovery.client.simple.instances.service1[0].uri=http://s11:8080, spring.cloud.discovery.client.simple.instances是常见的前缀,然后service1代表服务问题的ID,而[0]表示实例的索引号(在这个例子中,索引从0开始),然后uri的值是可用的实际uri实例。

ServiceRegistry

Commons现在提供了一个ServiceRegistry接口,该接口提供了register(Registration)deregister(Registration)等方法,可以让您提供自定义注册服务。Registration是一个标记接口。

下面的例子显示了ServiceRegistry的使用情况:

@Configuration
@EnableDiscoveryClient(autoRegister=false)
public class MyConfiguration {
    private ServiceRegistry registry;

    public MyConfiguration(ServiceRegistry registry) {
        this.registry = registry;
    }

    // called through some external process, such as an event or a custom actuator endpoint
    public void register() {
        Registration registration = constructRegistration();
        this.registry.register(registration);
    }
}

每个ServiceRegistry实现都有自己的Registry实现。

  • ZookeeperRegistrationZookeeperServiceRegistry使用
  • EurekaregistryEurekaServiceRegistry一起使用
  • ConsulRegistrationConsulServiceRegistry一起使用

如果您正在使用ServiceRegistry接口,则需要为您正在使用的ServiceRegistry实现传递正确的Registry实现。

ServiceRegistry自动注册

默认情况下,ServiceRegistry实现自动注册正在运行的服务。要禁用该行为,可以设置:@EnableDiscoveryClient(autoRegister=false)永久禁用自动注册。spring.cloud.service-registry.auto-registration.enabled=false通过配置禁用该行为。

ServiceRegistry自动注册事件

当服务自动注册时,将触发两个事件。第一个事件称为InstancePreRegisteredEvent,它在注册服务之前被触发。第二个事件称为InstanceRegisteredEvent,在服务注册后触发。您可以注册一个ApplicationListener来侦听这些事件并对其做出反应。

如果spring.cloud.service-registry.auto-registration.enabled属性设置为false,则不会触发这些事件。

服务注册执行器端点

Spring Cloud Commons提供了一个/service-registry执行器端点。这个端点依赖于Spring应用程序上下文中的Registration bean。使用GET调用/service-registry返回Registration的状态。将POST与JSON主体一起使用到相同的端点,会将当前Registration的状态更改为新值。JSON主体必须包含带有首选值的status字段。请参阅ServiceRegistry实现的文档,您在更新状态和为状态返回的值时使用允许的值。例如,Eureka支持的状态是UPDOWNOUT_OF_SERVICEUNKNOWN

Spring RestTemplate作为负载平衡客户机

可以将RestTemplate配置为使用负载平衡器客户机。要创建一个负载均衡的RestTemplate,请创建一个RestTemplate @Bean并使用@LoadBalanced限定符,如下面的示例所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

    public String doOtherStuff() {
        String results = restTemplate.getForObject("http://stores/stores", String.class);
        return results;
    }
}

不再通过自动配置创建RestTemplate bean。单个应用程序必须创建它。

URI需要使用虚拟主机名(即服务名,而不是主机名)。Ribbon客户机用于创建完整的物理地址。看到{githubroot}/spring-cloud-netflix /blob/master/ spring-cloud-netflix-ribbon/src/main/java /org/springframework/cloud/netflix/ribbon/RibbonAutoConfiguration.java[RibbonAutoConfiguration]获取RestTemplate如何设置的详细信息。

要使用负载平衡的RestTemplate,您需要在类路径中有一个负载平衡器实现。推荐的实现是BlockingLoadBalancerClient。在项目中添加Spring Cloud LoadBalancer starter,以便使用它。RibbonLoadBalancerClient也可以使用,但目前正在维护中,我们不建议将其添加到新项目中。

默认情况下,如果你同时拥有RibbonLoadBalancerClientBlockingLoadBalancerClient,为了保持向后兼容性,使用RibbonLoadBalancerClient。要覆盖它,您可以设置spring.cloud.loadbalance .ribbon.enabled属性为false

Spring WebClient作为一个负载平衡客户端

您可以将WebClient配置为自动使用负载平衡器客户机。要创建负载均衡的WebClient,请创建一个WebClient.Builder @Bean,并使用@LoadBalanced限定符,如下所示:

@Configuration
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    public Mono doOtherStuff() {
        return webClientBuilder.build().get().uri("http://stores/stores")
                        .retrieve().bodyToMono(String.class);
    }
}

URI需要使用虚拟主机名(即服务名,而不是主机名)。Ribbon客户机或Spring Cloud LoadBalancer用于创建完整的物理地址。

如果你想使用@LoadBalanced WebClient.Builder,您需要在类路径中有一个负载平衡器实现。我们建议您在项目中添加Spring Cloud LoadBalancer starter。然后,在下面使用ReactiveLoadBalancer。另外,这个功能也可以与spring-cloud-starter-netflix-ribbon一起使用,但是请求是由一个非响应的LoadBalancerClient处理的。此外,spring-cloud-start -netflix-ribbon已经处于维护模式,所以我们不建议将其添加到新项目中。如果您的类路径中同时有spring-cloud-starter- loadbalancerspring-cloud-starter-netflix-Ribbon,则默认使用Ribbon。要切换到Spring Cloud LoadBalancer,请设置spring.cloud.loadbalancer.ribbon.enabled属性为假。

重试失败的请求

可以配置负载平衡的RestTemplate来重试失败的请求。默认情况下,该逻辑是禁用的。您可以通过在应用程序的类路径中添加Spring Retry来启用它。负载平衡的RestTemplate执行与重试失败请求相关的功能区配置值。你可以使用client.ribbon.MaxAutoRetries,client.ribbon.MaxAutoRetriesNextServerclient.ribbon.OkToRetryOnAllOperations属性。如果您想在类路径上禁用Spring重试的重试逻辑,可以设置spring.cloud.loadbalance.retry.enabled=false。有关这些属性的功能,请参阅Ribbon文档。

如果你想在重试中实现一个BackOffPolicy,你需要创建一个LoadBalancedRetryFactory类型的bean并覆盖createBackOffPolicy方法:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryFactory retryFactory() {
        return new LoadBalancedRetryFactory() {
            @Override
            public BackOffPolicy createBackOffPolicy(String service) {
                return new ExponentialBackOffPolicy();
            }
        };
    }
}

前面示例中的client应该替换为您的Ribbon客户机的名称。

如果您想在重试功能中添加一个或多个RetryListener实现,您需要创建LoadBalancedRetryListenerFactory类型的bean,并返回您想用于给定服务的RetryListener数组,如下面的示例所示:

@Configuration
public class MyConfiguration {
    @Bean
    LoadBalancedRetryListenerFactory retryListenerFactory() {
        return new LoadBalancedRetryListenerFactory() {
            @Override
            public RetryListener[] createRetryListeners(String service) {
                return new RetryListener[]{new RetryListener() {
                    @Override
                    public  boolean open(RetryContext context, RetryCallback callback) {
                        //TODO Do you business...
                        return true;
                    }

                    @Override
                     public  void close(RetryContext context, RetryCallback callback, Throwable throwable) {
                        //TODO Do you business...
                    }

                    @Override
                    public  void onError(RetryContext context, RetryCallback callback, Throwable throwable) {
                        //TODO Do you business...
                    }
                }};
            }
        };
    }
}

多个RestTemplate对象

如果您想要一个不负载均衡的RestTemplate,那么创建一个RestTemplate bean并注入它。要访问负载均衡的RestTemplate,在创建@Bean时使用@LoadBalanced限定符,如下面的示例所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    RestTemplate loadBalanced() {
        return new RestTemplate();
    }

    @Primary
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }
}

public class MyClass {
@Autowired
private RestTemplate restTemplate;

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalanced;

    public String doOtherStuff() {
        return loadBalanced.getForObject("http://stores/stores", String.class);
    }

    public String doStuff() {
        return restTemplate.getForObject("http://example.com", String.class);
    }
}

注意,在前面的示例中,在纯RestTemplate声明上使用了@Primary注释,以消除未限定的@Autowired注入的歧义。

如果您看到错误,比如java.lang.IllegalArgumentException: Can not set org.springframework.web.client.RestTemplate field com.my.app.Foo.restTemplate to com.sun.proxy.$Proxy89,尝试注入RestOperations或设置spring.aop.proxyTargetClass=true

多个WebClient对象

如果你想要一个不负载均衡的WebClient,创建一个WebClient bean并注入它。要访问负载均衡的WebClient,在创建@Bean时使用@LoadBalanced限定符,如下面的例子所示:

@Configuration
public class MyConfiguration {

    @LoadBalanced
    @Bean
    WebClient.Builder loadBalanced() {
        return WebClient.builder();
    }

    @Primary
    @Bean
    WebClient.Builder webClient() {
        return WebClient.builder();
    }
}

public class MyClass {
    @Autowired
    private WebClient.Builder webClientBuilder;

    @Autowired
    @LoadBalanced
    private WebClient.Builder loadBalanced;

    public Mono doOtherStuff() {
        return loadBalanced.build().get().uri("http://stores/stores")
                        .retrieve().bodyToMono(String.class);
    }

    public Mono doStuff() {
        return webClientBuilder.build().get().uri("http://example.com")
                        .retrieve().bodyToMono(String.class);
    }
}

Spring WebFlux WebClient作为负载平衡客户机

Spring WebFlux可以同时使用响应性和非响应性的WebClient配置,正如主题所描述的:

  • Spring WebFlux WebClientReactorLoadBalancerExchangeFilterFunction
  • [load-balancer-exchange-filter-functionload-balancer-exchange-filter-function]

Spring WebFlux WebClientReactorLoadBalancerExchangeFilterFunction

您可以将WebClient配置为使用ReactiveLoadBalancer。如果您将Spring Cloud LoadBalancer starter添加到您的项目中,并且spring-webflux在类路径中,则会自动配置ReactorLoadBalancerExchangeFilterFunction。下面的例子展示了如何配置一个WebClient来使用响应式负载均衡器:

public class MyClass {
    @Autowired
    private ReactorLoadBalancerExchangeFilterFunction lbFunction;

    public Mono doOtherStuff() {
        return WebClient.builder().baseUrl("http://stores")
            .filter(lbFunction)
            .build()
            .get()
            .uri("/stores")
            .retrieve()
            .bodyToMono(String.class);
    }
}

URI需要使用虚拟主机名(即服务名,而不是主机名)。ReactorLoadBalancer用于创建完整的物理地址。

默认情况下,如果类路径中有spring-cloud-netflix-ribbon,则使用LoadBalancerExchangeFilterFunction来保持向后兼容性。要使用ReactorLoadBalancerExchangeFilterFunction,请设置spring.cloud.loadbalancer.ribbon.enabled属性为false

Spring WebFlux WebClient带有非反应性负载均衡器客户端

如果你的项目中没有Spring Cloud LoadBalancer starter,但是你有spring-cloud-starter-netflix-ribbon,你仍然可以使用LoadBalancerClientWebClient。如果spring-webflux在类路径上,LoadBalancerExchangeFilterFunction会自动配置。但是请注意,这在内部使用了一个非反应性客户机。下面的例子展示了如何配置一个WebClient来使用负载平衡器:

public class MyClass {
    @Autowired
    private LoadBalancerExchangeFilterFunction lbFunction;

    public Mono doOtherStuff() {
        return WebClient.builder().baseUrl("http://stores")
            .filter(lbFunction)
            .build()
            .get()
            .uri("/stores")
            .retrieve()
            .bodyToMono(String.class);
    }
}

URI需要使用虚拟主机名(即服务名,而不是主机名)。LoadBalancerClient用于创建完整的物理地址。

警告:这种方法现在已被弃用。我们建议您使用带有响应式负载均衡器的WebFlux。

忽略网络接口

有时,忽略某些已命名的网络接口是有用的,这样就可以将它们从服务发现注册中排除(例如,在Docker容器中运行时)。可以设置一个正则表达式列表,以忽略所需的网络接口。下面的配置忽略docker0接口和所有以veth开始的接口:

application.yml

spring:
  cloud:
    inetutils:
      ignoredInterfaces:
        - docker0
        - veth.*

您还可以通过使用正则表达式列表强制只使用指定的网络地址,如下面的示例所示:

bootstrap.yml

spring:
  cloud:
    inetutils:
      preferredNetworks:
        - 192.168
        - 10.0

你也可以强制只使用网站本地地址,如下面的例子所示:

application.yml

spring:
  cloud:
    inetutils:
      useOnlySiteLocalInterfaces: true

有关什么构成站点-本地地址的详细信息,请参阅Inet4Address.html.isSiteLocalAddress() 。

HTTP客户机的工厂

Spring Cloud Commons提供了用于创建Apache HTTP客户机(ApacheHttpClientFactory)和OK HTTP客户机(OkHttpClientFactory)的bean。只有当OK HTTP jar位于类路径中时,才会创建OkHttpClientFactory bean。另外,Spring Cloud Commons提供了用于创建两个客户机使用的连接管理器的bean: ApacheHttpClientConnectionManagerFactory用于Apache HTTP客户机,OkHttpClientConnectionPoolFactory用于OK HTTP客户机。如果您想定制如何在下游项目中创建HTTP客户机,您可以提供自己的这些bean的实现。另外,如果您提供一个类型为HttpClientBuilderOkHttpClient.Builder的bean,默认工厂使用这些构建器作为返回下游项目的构建器的基础。您还可以通过设置spring.cloud.httpclientfactories.apache.enabledspring.cloud.httpclientfactories.ok.enabledfalse来禁用这些bean的创建。

启用特性

Spring Cloud Commons提供了一个/features执行器端点。这个端点返回类路径上可用的特性以及它们是否被启用。返回的信息包括特性类型、名称、版本和供应商。

要素类型

有两种类型的“特征”:抽象的和命名的。

抽象特性是定义接口或抽象类并由实现创建的特性,如DiscoveryClientLoadBalancerClientLockService。抽象类或接口用于在上下文中查找该类型的bean。显示的版本是bean.getClass().getPackage().getImplementationVersion()

命名特性是那些没有实现特定类的特性。这些特性包括“断路器”、“API网关”、“Spring Cloud Bus”等。这些特性需要一个名称和一个bean类型。

声明的特性

任何模块都可以声明任意数量的HasFeature bean,如下面的例子所示:

@Bean
public HasFeatures commonsFeatures() {
  return HasFeatures.abstractFeatures(DiscoveryClient.class, LoadBalancerClient.class);
}

@Bean
public HasFeatures consulFeatures() {
  return HasFeatures.namedFeatures(
    new NamedFeature("Spring Cloud Bus", ConsulBusAutoConfiguration.class),
    new NamedFeature("Circuit Breaker", HystrixCommandAspect.class));
}

@Bean
HasFeatures localFeatures() {
  return HasFeatures.builder()
      .abstractFeature(Something.class)
      .namedFeature(new NamedFeature("Some Other Feature", Someother.class))
      .abstractFeature(Somethingelse.class)
      .build();
}

每个bean都应该放入一个适当保护的@Configuration中。

Spring Cloud兼容性验证

由于一些用户在设置Spring Cloud应用时存在问题,我们决定添加一个兼容性验证机制。如果您的当前设置与Spring Cloud的要求不兼容,并附带一个报告,显示出究竟哪里出了问题,那么它就会崩溃。

目前我们正在验证哪个版本的Spring Boot被添加到您的类路径中。

报告示例

***************************
APPLICATION FAILED TO START
***************************

Description:

Your project setup is incompatible with our requirements due to following reasons:

- Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train


Action:

Consider applying the following actions:

- Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] .
You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn].
If you want to learn more about the Spring Cloud Release train compatibility, you can visit this page [https://spring.io/projects/spring-cloud#overview] and check the [Release Trains] section.

为了禁用该特性,将spring.cloud.compatibility-verifier.enabled设置为false。如果您想覆盖兼容的Spring引导版本,只需设置spring.cloud.compatibility-verifier.compatible-boot-versions属性,并用逗号分隔的兼容Spring引导版本列表。

Spring Cloud负载平衡器

Spring Cloud提供了自己的客户端负载平衡器抽象和实现。对于负载平衡机制,添加了ReactiveLoadBalancer接口,并为其提供了基于轮询的实现。为了从反应的ServiceInstanceListSupplier获得实例选择。目前,我们支持ServiceInstanceListSupplier的基于服务发现的实现,该实现使用类路径中可用的Discovery Client从服务发现中检索可用的实例。

为了方便使用Spring Cloud LoadBalancer,我们提供了可以与WebClient使用的ReactorLoadBalancerExchangeFilterFunction和与RestTemplate一起使用的BlockingLoadBalancerClient。你可以在以下部分看到更多的信息和使用的例子:

  • Spring RestTemplate as a Load Balancer Client
  • Spring WebClient as a Load Balancer Client
  • Spring WebFlux WebClient with ReactorLoadBalancerExchangeFilterFunction

Spring Cloud 负载均衡器缓存

除了每次必须选择实例时通过DiscoveryClient检索实例的基本ServiceInstanceListSupplier实现之外,我们提供了两个缓存实现。

Caffeine支持的load均衡器缓存实现

如果类路径中有com.github.ben-manes.caffeine:caffeine,那么将使用基于Caffeine的实现。有关如何配置它的信息,请参阅LoadBalancerCacheConfiguration部分。

如果您正在使用Caffeine,您还可以通过在spring.cloud.loadbalancer.cache.caffeine.spec属性中传递您自己的Caffeine Specification来覆盖LoadBalancer的默认Caffeine缓存设置。

警告:通过您自己的Caffeine Specification将覆盖任何其他LoadBalancerCache设置,包括一般的LoadBalancer缓存配置字段,如ttlcapacity

默认load均衡器缓存实现

如果类路径中没有Caffeine,将使用spring-cloud-starter-loadbalancer自动提供的DefaultLoadBalancerCache。有关如何配置它的信息,请参阅LoadBalancerCacheConfiguration部分。

要使用Caffeine而不是默认缓存,请将com.github.ben-manes.caffeine:caffeine依赖项添加到类路径。

负载均衡器缓存配置

通过向Duration转换器语法传递一个符合Spring引导字符串的字符串,您可以设置自己的ttl值(写入之后的时间,条目应该过期),表示为Duration。作为spring.cloud.loadbalancer.cache.ttl属性的值。您还可以通过设置spring.cloud.loadbalancer.cache.capacity属性的值来设置自己的LoadBalancer缓存初始容量。

默认设置包括ttl设置为35秒,默认初始容量为256秒。

还可以通过将spring.cloud.loadbalancer.cache.enabled的值设置为false来禁用loadBalancer缓存。

尽管基本的、非缓存的实现对于原型设计和测试很有用,但它的效率远低于缓存版本,因此我们建议在生产中始终使用缓存版本。

基于zone的负载平衡

为了启用基于区域的负载平衡,我们提供了ZonePreferenceServiceInstanceListSupplier。我们使用特定于DiscoveryClientzone配置(例如,eureka.instance.metadata-map.zone)来选择客户端试图为其筛选可用服务实例的区域。

还可以通过设置spring.cloud.loadbalancer.zone属性的值来覆盖特定于DiscoveryClient的区域设置。

目前,只有Eureka发现客户端被测试设置负载平衡区。对于其他发现客户端,设置spring.cloud.loadbalancer.zone属性。更多的工具很快就会到来。

要确定检索到的ServiceInstance的区域,我们检查其元数据映射中“zone”键下的值。

ZonePreferenceServiceInstanceListSupplier筛选检索到的实例,并且只返回同一区域内的实例。如果该区域为null或同一区域内没有实例,则返回所有检索到的实例。

为了使用基于区域的负载平衡方法,您必须在自定义配置中实例化ZonePreferenceServiceInstanceListSupplier bean。

我们使用委托来处理ServiceInstanceListSupplier bean。我们建议在ZonePreferenceServiceInstanceListSupplier的构造函数中传递DiscoveryClientServiceInstanceListSupplier委托,然后用CachingServiceInstanceListSupplier包装后者以利用load均衡器缓存机制。

您可以使用这个示例配置来设置它:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSuppliers.builder()
                    .withDiscoveryClient()
                    .withZonePreference()
                    .withCaching()
                    .build(context);
    }
}

对load均衡器进行实例健康检查

可以为LoadBalancer启用计划健康检查。为此提供了HealthCheckServiceInstanceListSupplier。它定期验证委托ServiceInstanceListSupplier提供的实例是否仍然存在,并且只返回健康的实例,除非没有实例—然后返回所有检索到的实例。

在使用SimpleDiscoveryClient时,这种机制特别有用。对于由实际服务注册中心支持的客户端,不需要使用它,因为我们在查询外部ServiceDiscovery之后已经获得了健康的实例。

对于每个服务只有少量实例的设置,也建议使用此供应商,以避免重试调用失败的实例。

HealthCheckServiceInstanceListSupplier使用带有spring.cloud.loadbalance.healthcheck前缀的属性。您可以为调度程序设置initialDelayinterval。您可以通过设置spring.cloud.loadbalancer.healthcheck.path.default的值来设置healthcheck URL的默认路径。您还可以通过设置spring.cloud.loadbalancer.healthcheck.path.[SERVICE_ID]的值为任何给定的服务设置一个特定的值,用正确的服务ID替换[SERVICE_ID]。如果没有设置路径,则默认使用/actuator/health

如果您依赖于默认路径(/actuator/health),请确保将spring-boot-starter-actuator添加到您的合作者的依赖项中,除非您计划自己添加这样的端点。

为了使用健康检查调度程序方法,您必须在自定义配置中实例化一个HealthCheckServiceInstanceListSupplier bean。

我们使用委托来处理ServiceInstanceListSupplier bean。我们建议在HealthCheckServiceInstanceListSupplier的构造函数中传递DiscoveryClientServiceInstanceListSupplier委托。

您可以使用这个示例配置来设置它:

public class CustomLoadBalancerConfiguration {

    @Bean
    public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier(
            ConfigurableApplicationContext context) {
        return ServiceInstanceListSupplier.builder()
                    .withDiscoveryClient()
                    .withHealthChecks()
                    .build(context);
        }
    }

HealthCheckServiceInstanceListSupplier有自己的基于Reactor Flux replay()的缓存机制,因此,如果它被使用,您可能希望跳过使用CachingServiceInstanceListSupplier包装该供应商。

Spring Cloud LoadBalancer Starter

我们还提供了一个启动器,可以让您轻松地在Spring Boot应用程序中添加Spring Cloud load均衡器。为了使用它,只需在构建文件中添加org.springframework.cloud:spring-cloud-starter-loadbalancer到您的Spring Cloud依赖项。

Spring Cloud load均衡器启动器包括Spring Boot Caching和Evictor。

如果类路径中同时有Ribbon和Spring Cloud LoadBalancer,为了保持向后兼容性,默认将使用基于Ribbon的实现。为了切换到使用Spring Cloud load均衡器,请确保您设置了spring.cloud.loadbalancer.ribbon.enabledfalse

传递您自己的Spring Cloud load均衡器配置

您也可以使用@LoadBalancerClient注释来传递您自己的负载均衡器客户端配置,传递负载均衡器客户端名称和配置类,如下所示:

@Configuration
@LoadBalancerClient(value = "stores", configuration = CustomLoadBalancerConfiguration.class)
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

为了使您自己的LoadBalancer配置工作更容易,我们向ServiceInstanceListSupplier类添加了一个builder()方法。

您还可以通过将spring.cloud.loadbalancer.configurations属性的值设置为zone-preference来使用带有缓存的ZonePreferenceServiceInstanceListSupplier,或者将health-check设置为带有缓存的HealthCheckServiceInstanceListSupplier,来使用我们的替代预定义配置来替代默认配置。

您可以使用这个特性来实例化ServiceInstanceListSupplierReactorLoadBalancer的不同实现,无论是您编写的,还是我们提供的替代方案(例如ZonePreferenceServiceInstanceListSupplier),以覆盖默认设置。

您可以在这里看到一个自定义配置的示例。

注释value参数(stores在上面的示例中)指定我们应该使用给定的自定义配置向其发送请求的服务的服务id。

您还可以通过@LoadBalancerClients注释传递多个配置(针对多个负载平衡器客户端),如下面的示例所示:

@Configuration
@LoadBalancerClients({@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class), @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)})
public class MyConfiguration {

    @Bean
    @LoadBalanced
    public WebClient.Builder loadBalancedWebClientBuilder() {
        return WebClient.builder();
    }
}

Spring Cloud 断路器

简介

Spring Cloud断路器提供了跨不同断路器实现的抽象。它为您的应用程序提供了一致的API,让开发人员可以选择最适合您的应用程序需要的断路器实现。

支持实现

Spring Cloud支持以下断路器实现:

  • Netflix Hystrix
  • Resilience4J
  • Sentinel
  • Spring Retry

核心概念

要在代码中创建断路器,可以使用CircuitBreakerFactory API。当您在类路径中包含Spring Cloud断路器启动器时,将自动为您创建实现此API的bean。下面的例子展示了如何使用这个API的简单例子:

@Service
public static class DemoControllerService {
    private RestTemplate rest;
    private CircuitBreakerFactory cbFactory;

    public DemoControllerService(RestTemplate rest, CircuitBreakerFactory cbFactory) {
        this.rest = rest;
        this.cbFactory = cbFactory;
    }

    public String slow() {
        return cbFactory.create("slow").run(() -> rest.getForObject("/slow", String.class), throwable -> "fallback");
    }

}

CircuitBreakerFactory.create API创建了一个名为CircuitBreaker的类的实例。run方法接受一个Supplier和一个FunctionSupplier是代码,你要包装在断路器。该Function是在断路器跳闸时执行的回退。函数传递了导致回退被触发的Throwable。如果不想提供回退,可以选择排除回退。

Reactive代码中的断路器

如果项目反应器位于类路径上,您还可以为响应代码使用ReactiveCircuitBreakerFactory。下面的例子展示了如何做到这一点:

@Service
public static class DemoControllerService {
    private ReactiveCircuitBreakerFactory cbFactory;
    private WebClient webClient;


    public DemoControllerService(WebClient webClient, ReactiveCircuitBreakerFactory cbFactory) {
        this.webClient = webClient;
        this.cbFactory = cbFactory;
    }

    public Mono slow() {
        return webClient.get().uri("/slow").retrieve().bodyToMono(String.class).transform(
        it -> cbFactory.create("slow").run(it, throwable -> return Mono.just("fallback")));
    }
}

ReactiveCircuitBreakerFactory.create API创建了一个名为ReactiveCircuitBreaker的类的实例。run方法取一个Mono或一个Flux并将其封装在断路器中。您可以选择配置一个回退Function,如果断路器跳闸并传递导致故障的Throwable,将调用该函数。

配置

您可以通过创建Customizer类型的bean来配置断路器。Customizer接口有一个方法(称为customize),该方法接受要定制的Object

有关如何自定义给定实现的详细信息,请参阅以下文档:

  • Hystrix
  • Resilience4J
  • Sentinal
  • Spring Retry

一些CircuitBreaker实现,如Resilience4JCircuitBreaker调用customize方法每次CircuitBreaker#run被调用。它可能效率低下。在这种情况下,您可以使用CircuitBreaker#once的方法。在多次调用customize没有意义的情况下,例如在消费Resilience4j的事件时,它非常有用。

下面的示例显示了每个io.github.resilience4j.circuitbreaker.CircuitBreaker用于消费事件的方式。

Customizer.once(circuitBreaker -> {
  circuitBreaker.getEventPublisher()
    .onStateTransition(event -> log.info("{}: {}", event.getCircuitBreakerName(), event.getStateTransition()));
}, CircuitBreaker::getName)

CachedRandomPropertySource

Spring Cloud Context提供了一个PropertySource,它根据键缓存随机值。在缓存功能之外,它的工作原理与Spring Boot的RandomValuePropertySource相同。当您希望在Spring应用程序上下文重新启动后仍保持一致的随机值时,这个随机值可能非常有用。属性值采用cachedrandom.[yourkey].[type]的形式,yourkey是缓存中的密钥。type值可以是Spring引导的RandomValuePropertySource支持的任何类型。

myrandom=${cachedrandom.appname.value}

你可能感兴趣的:(cloud)