Spring Cloud Commons(上)
云原生应用
Cloud Native 是一种应用程序开发风格,鼓励在持续交付和价值驱动开发领域轻松采用最佳实践。
一个相关的学科是构建 12要素应用程序 ,其中开发实践与交付和运营目标保持一致,例如,通过使用声明性编程、管理和监控。
Spring Cloud 以许多特定的方式促进了这些风格的开发。起点是一组特性,分布式系统中的所有组件都需要方便地访问这些特性。
Spring Boot 覆盖了其中的许多特性,Spring Cloud 是在 Spring Boot 之上构建的。Spring Cloud 以两个库的形式提供了更多特性:Spring Cloud Context 和 Spring Cloud Commons。
Spring Cloud Context 为 Spring Cloud 应用程序的 ApplicationContext
(bootstrap context, encryption, refresh scope, 和 environment 端点)提供实用工具和特殊服务。
Spring Cloud Commons 是在不同的 Spring Cloud 实现中使用的一组抽象和通用类(例如 Spring Cloud Netflix 和 Spring Cloud Consul)。
如果由于“Illegal key size”而导致异常,并且使用 Sun/Oracle 的 JDK,则需要安装 Java 密码扩展(JCE)无限制强度策略文件。有关更多信息,请参见以下链接:
将文件解压缩到您使用的 x64/x86 版本 JRE/JDK 的 {JAVA_HOME}/jre/lib/security 文件夹中。
当前各发行版本 JDK 已不需要这些策略文件。
Spring Cloud Context:应用程序上下文服务
Spring Boot 对如何使用 Spring 构建应用程序有自己独到的见解。例如,它具有用于公共配置文件的常规位置,并具有用于公共管理和监视任务的端点。Spring Cloud 构建在此基础上,并添加了系统中许多组件可能使用或偶尔需要的一些特性。
Bootstrap 应用程序上下文
Spring Cloud 应用程序通过创建一个“bootstrap”上下文来运行,它是主应用程序的父上下文。它负责从外部源加载配置属性,并对本地外部配置文件中的属性进行解密。
这两个上下文共享一个 Environment,它是任何 Spring 应用程序的外部属性的来源。默认情况下,“bootstrap”属性(不是bootstrap.properties
,而是“bootstrap”程序阶段加载的属性)以高优先级添加,因此它们不能被本地配置覆盖。
“bootstrap”上下文使用与主应用程序上下文不同的约定来定位外部配置。可以使用bootstrap.yml
来代替application.yml
(或.properties
),而将“bootstrap”程序和外部环境的外部配置很好地分开。以下清单显示了一个示例:
示例:bootstrap.yml
spring:
application:
name: foo
cloud:
config:
uri: ${SPRING_CONFIG_URI:http://localhost:8888}
如果您的应用程序需要来自服务器的任何特定于应用程序的配置,则最好设置spring.application.name
(在bootstrap.yml
或application.yml
中)。为了将属性spring.application.name
用作应用程序的上下文 ID,必须在bootstrap.[properties|yml]
中进行设置。
如果要检索特定的配置文件配置,还应该在bootstrap.[properties|yml]
中设置spring.profiles.active
。
您可以通过设置spring.cloud.bootstrap.enabled=false
来完全禁用“bootstrap”过程(例如,在系统属性中)。
应用程序上下文层次结构
如果你从SpringApplication
或SpringApplicationBuilder
构建应用程序上下文,“Bootstrap”上下文会作为该上下文的父级添加。这是 Spring 的一个特性,子上下文从它们的父上下文继承属性源和配置文件,因此“main”应用程序上下文包含额外的属性源,而不使用 Spring Cloud Config 构建相同的上下文。其他属性来源有:
“bootstrap”:如果在“bootstrap”上下文中找到任何PropertySourceLocators
,并且它们有非空属性,则一个可选的CompositePropertySource
会以高优先级出现。Spring Cloud Config Server 的属性就是一个例子。有关如何自定义该属性源的内容,请参见“ 自定义“bootstrap”属性源 ”。
“applicationConfig: [classpath:bootstrap.yml]”(如果 Spring profiles 是激活的,则是相关文件):如果你有一个bootstrap.yml
(或.properties
),这些属性用于配置“bootstrap”上下文。然后当父上下文被设置时,它们被添加到子上下文中。它们的优先级低于application..yml
(或.properties
)以及创建 Spring Boot 应用程序过程中正常添加到子级的任何其他属性源的优先级。。有关如何自定义这些属性源的内容,请参见“ 更改“bootstrap”属性的位置 ”。
由于属性源的排序规则,“bootstrap”条目优先。但是,请注意,这些不包含来自bootstrap.yml
的任何数据,该数据的优先级非常低,但可用于设置默认值。
您可以通过设置创建的任何ApplicationContext
的父上下文来扩展上下文层次结构,例如,使用其的接口或使用SpringApplicationBuilder
便捷方法(parent()
,child()
和sibling()
)。“bootstrap”上下文是您自己创建的最高级祖先的父级。层次结构中的每个上下文都有其自己的“bootstrap”(可能为空)属性源,以避免无意间将值从父辈提升到子孙后代。如果有配置服务器,则层次结构中的每个上下文原则上也可以具有不同的spring.application.name
,因此也具有不同的远程属性源。正常的 Spring 应用程序上下文行为规则适用于属性解析:子上下文的属性按名称以及属性源名称覆盖父级属性。(如果子项具有与父项同名的属性源,则子项中不包括来自父项的值)。
请注意,SpringApplicationBuilder
可让您在整个层次结构中共享Environment
,但这不是默认设置。因此,同级上下文尤其不需要具有相同的配置文件或属性源,即使它们可能与其父级共享相同的值。
更改“bootstrap”程序 Properties 的位置
可以通过设置spring.cloud.bootstrap.name
(默认值:bootstrap
),spring.cloud.bootstrap.location
(默认值:空)或spring.cloud.bootstrap.additional-location
(默认值:空)来指定bootstrap.yml
(或.properties
)位置。 例如,在系统属性中。
这些属性的行为类似于具有相同名称的spring.config.*
变体。使用spring.cloud.bootstrap.location
将替换默认位置,并且仅使用指定的位置。要将位置添加到默认位置列表中,可以使用spring.cloud.bootstrap.additional-location
。实际上,它们是通过在“bootstrap”程序Environment
中设置这些属性来设置“bootstrap”程序ApplicationContext
的。如果存在有效的配置文件(通过spring.profiles.active
或通过您正在构建的上下文中的Environment
API),该配置文件中的属性也会被加载,这与常规Spring Boot 应用程序中的加载情况相同。例如,从bootstrap-development.properties
中获取development
配置。
覆盖远程 Properties 的值
通过“bootstrap”上下文添加到应用程序中的属性源通常是“远程 ”(例如,来自 Spring Cloud Config Server)。默认情况下,不能在本地覆盖它们。如果要让您的应用程序使用其自己的系统属性或配置文件覆盖远程属性,则远程属性源必须通过设置spring.cloud.config.allowOverride=true
来授予其权限(在本地设置无效)。设置该标志后,将使用两个更细粒度的设置来控制远程属性相对于系统属性和应用程序本地配置的位置:
-
spring.cloud.config.overrideNone=true
:任何本地属性源都可以覆盖远程设置。 -
spring.cloud.config.overrideSystemProperties=false
:只有系统属性,命令行参数和环境变量(而不是本地配置文件)才应覆盖远程设置。
自定义 Bootstrap 配置
通过在/META-INF/spring.factories
中名为org.springframework.cloud.bootstrap.BootstrapConfiguration
的键后添加值,可以将“bootstrap”上下文设置为做任何你想做的事情。它保存一个用逗号分隔的Spring @Configuration
类列表,这些类用于创建上下文。您希望主应用程序上下文可用以进行自动装配的任何 bean 都可以在这里创建。@Beans
有一个ApplicationContextInitializer
类型的特殊契约。如果希望控制启动顺序,可以使用@Order
注释标记类(默认顺序是last
)。
当添加自定义
BootstrapConfiguration
时,要小心你添加的类不是@ComponentScanned
错误地进入你的 “main” 应用程序上下文,这里可能并不需要它们。为引导配置类使用单独的程序包名称,并确保@ComponentScan
或带注释的配置类@SpringBootApplication
尚未包含该名称。
“bootstrap”过程结束时,将初始化程序注入到主要的SpringApplication
实例中(这是正常的 Spring Boot 启动顺序,无论它是作为独立应用程序运行还是部署在应用程序服务器中)。首先,从spring.factories
中找到的类创建“bootstrap”上下文。然后,在启动之前,将类型为ApplicationContextInitializer
的所有@Beans
添加到主SpringApplication
。
自定义 Bootstrap 程序 Property 源
“bootstrap”过程添加的外部配置的默认属性来源是 Spring Cloud Config 服务器,但是您可以通过将类型PropertySourceLocator
的 beans 添加到引导上下文(通过spring.factories
)来添加其他来源。例如,您可以从其他服务器或数据库插入其他属性。
@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {
@Override
public PropertySource<?> locate(Environment environment) {
return new MapPropertySource("customProperty",
Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
}
}
传入的Environment
是即将创建的ApplicationContext
的那个,换句话说,就是我们为其提供其他属性源的那个。它已经有其正常的 Spring Boot 提供的属性源,因此您可以使用这些属性来定位特定于此 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
。
Environment 改变
应用程序侦听EnvironmentChangeEvent
并以几种标准方式对更改做出反应(用户可以通过常规方式将其他ApplicationListeners
作为@Beans
添加)。观察到EnvironmentChangeEvent
时,它会列出已更改的键值,并且应用程序将这些键值用于:
- 重新绑定上下文中的任何
@ConfigurationProperties
beans - 为
logging.level.*
中的所有属性设置记录器级别
请注意,默认情况下,Config Client 不轮询Environment
中的更改。通常,我们不建议您使用这种方法来检测更改(尽管您可以使用@Scheduled
注释对其进行设置)。如果您具有横向扩展的客户端应用程序,则最好向所有实例广播EnvironmentChangeEvent
,而不是让它们轮询更改(例如,使用 Spring Cloud Bus)。
只要您可以实际更改Environment
并发布事件,EnvironmentChangeEvent
就涵盖了一大类刷新用例。请注意,这些 API 是公共的,并且是核心 Spring 的一部分)。您可以通过访问/configprops
端点(正常的 Spring Boot Actuator 功能)来验证更改是否绑定到@ConfigurationProperties
beans。例如,DataSource
可以在运行时更改其maxPoolSize
(由 Spring Boot 创建的默认DataSource
是@ConfigurationProperties
bean)并动态地增加容量。重新绑定@ConfigurationProperties
并不涵盖另一类用例,在这种情况下,您需要对刷新有更多的控制,并且需要对整个ApplicationContext
进行原子更改。为了解决这些问题,我们有@RefreshScope
。
Refresh Scope
进行配置更改时,标记为@RefreshScope
的 Spring @Bean
将得到特殊处理。此功能解决了状态 beans 的问题,该状态仅在初始化时才注入配置。例如,如果通过 Environment 更改数据库 URL 时DataSource
具有打开的连接,则您可能希望这些连接的持有者能够完成他们正在做的事情。然后,下次某物从池中借用一个连接时,它将获得一个具有新 URL 的连接。
有时,甚至可能必须将@RefreshScope
注解应用到只能初始化一次的某些 beans 上。如果 bean 是“不可变的”,则必须用@RefreshScope
注解 bean 或在属性键spring.cloud.refresh.extra-refreshable
下指定类名。
如果您有一个属于
HikariDataSource
的数据源 bean,则不能刷新它。它是spring.cloud.refresh.never-refresh
的默认值。如果需要刷新数据源,请选择不同的数据源实现。
Refresh Scope beans 是惰性代理,在使用它们时(即调用方法时)进行初始化,并且作用域充当初始化值的缓存。要强制 bean 在下一次方法调用时重新初始化,必须使其缓存项无效。
RefreshScope
在上下文中是 bean,并具有公用的refreshAll()
方法,可通过清除目标缓存来刷新作用域中的所有 beans。/refresh
端点公开了此功能(通过 HTTP 或 JMX)。要按名称刷新单个 bean,还有一个refresh(String)
方法。
要公开/refresh
端点,您需要在应用程序中添加以下配置:
management:
endpoints:
web:
exposure:
include: refresh
@RefreshScope
(技术上)适用于@Configuration
类,但它可能会导致令人惊讶的行为。例如,这并不意味着在该类中定义的所有@bean
本身都在@RefreshScope
中。具体而言,除非刷新本身在@RefreshScope
中,否则依赖那些 beans 的任何内容都不能依赖于刷新启动时对其进行更新。在这种情况下,将在刷新时重建它,并重新注入其依赖项。此时,它们将从刷新的@Configuration
重新初始化。
加密和解密
Spring Cloud 具有 Environment
预处理器,用于在本地解密属性值。它遵循与 Config Server 相同的规则,并且通过 encrypt.*
具有相同的外部配置。因此,您可以使用{cipher}*形式的加密值,并且只要存在有效密钥,就可以在主应用程序上下文获得Environment设置之前对它们进行解密。要在应用程序中使用加密功能,您需要在类路径中包含Spring Security RSA(Maven坐标:“ org.springframework.security:spring-security-rsa”),并且还需要JVM中的全功能JCE扩展。
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/Oracle 的 JDK,则需要安装 Java 密码扩展(JCE)无限制强度策略文件。有关更多信息,请参见以下链接:
将文件解压缩到您使用的 x64/x86 版本 JRE/JDK 的 {JAVA_HOME}/jre/lib/security 文件夹中。
当前各发行版本 JDK 已不需要这些策略文件。
端点
对于 Spring Boot Actuator 应用程序,可以使用一些其他管理端点。您可以使用:
- 从
POST
到/actuator/env
以更新Environment
并重新绑定@ConfigurationProperties
和日志级别。 -
/actuator/refresh
重新加载引导上下文并刷新@RefreshScope
beans。 -
/actuator/restart
关闭ApplicationContext
并重新启动(默认情况下禁用)。 -
/actuator/pause
和/actuator/resume
用于调用Lifecycle
方法(ApplicationContext
中的stop()
和start()
)。
如果禁用
/actuator/restart
端点,则/actuator/pause
和/actuator/resume
端点也将被禁用,因为它们只是/actuator/restart
的特性。
Spring Cloud Commons:通用抽象
服务发现,负载平衡和断路器之类的模式将它们带到一个通用的抽象层,可以由所有 Spring Cloud 客户端使用,而与实现无关(例如,使用 Eureka 或 Consul 进行的发现)。
@EnableDiscoveryClient
注解
Spring Cloud Commons 提供了@EnableDiscoveryClient
注解。这将在META-INF/spring.factories
中寻找DiscoveryClient
和ReactiveDiscoveryClient
接口的实现。Discovery Client 的实现使用org.springframework.cloud.client.discovery.EnableDiscoveryClient
键将配置类添加到spring.factories
。DiscoveryClient
实现的示例包括 Spring Cloud Netflix Eureka,Spring Cloud Consul Discovery 和 Spring Cloud Zookeeper Discovery。
默认情况下,Spring Cloud 将同时提供阻塞和响应式服务发现客户端。通过设置spring.cloud.discovery.blocking.enabled=false
或spring.cloud.discovery.reactive.enabled=false
,你可以轻松地禁用阻塞和/或响应式客户端。要完全禁用服务发现,你只需要设置spring.cloud.discovery.enabled=false
。
默认情况下,DiscoveryClient
的实现会自动将本地 Spring Boot 服务器注册到远程发现服务器。可以通过在@EnableDiscoveryClient
中设置autoRegister=false
来禁用此行为。
不再需要
@EnableDiscoveryClient
。您可以在类路径上放置DiscoveryClient
实现,以使 Spring Boot 应用程序向服务发现服务器注册。
健康指标
Commons 自动配置下列 Spring Boot 运行状况指示器。
DiscoveryClientHealthIndicator
此运行状况指示器基于当前注册的DiscoveryClient
实现。
- 要完全禁用,请设置
spring.cloud.discovery.client.health-indicator.enabled=false
。 - 要禁用描述字段,请设置
spring.cloud.discovery.client.health-indicator.include-description=false
。 - 若要禁用服务检索,请设置
spring.cloud.discovery.client.health-indicator.use-services-query=false
。默认情况下,指示器调用客户机的getServices
方法。在部署许多已注册的服务时,在每次检查期间检索所有服务的成本可能太高。这将跳过服务检索,而是使用客户机的探测方法。
DiscoveryCompositeHealthContributor
此复合运行状况指示器基于所有已注册的DiscoveryHealthIndicator
bean。要禁用,设置spring.cloud.discovery.client.compositeindicator.enabled=false
。
排序DiscoveryClient
实例
DiscoveryClient
接口扩展了Ordered
。当使用多个发现客户端时,这很有用,因为它允许您定义返回的发现客户端的顺序,类似于如何排序由 Spring 应用程序加载的 beans。默认情况下,任何DiscoveryClient
的顺序都设置为0。如果要为自定义DiscoveryClient
实现设置不同的顺序,则只需覆盖getOrder()
方法,以便它返回适合您的设置的值。除此之外,您可以使用属性来设置 Spring Cloud 提供的DiscoveryClient
实现的顺序,其中包括ConsulDiscoveryClient
,EurekaDiscoveryClient
和ZookeeperDiscoveryClient
。为此,您只需要将spring.cloud.{clientIdentifier}.discovery.order
(或者 Eureka 的eureka.client.order
)属性设置为所需的值。
SimpleDiscoveryClient
如果类路径中没有Service-Registry-backed
的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。
服务注册
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
实现。
-
ZookeeperRegistration
与ZookeeperServiceRegistry
一起使用 -
EurekaRegistration
与EurekaServiceRegistry
一起使用 -
ConsulRegistration
与ConsulServiceRegistry
一起使用
如果您使用的是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
,则不会触发这些事件。
服务注册 Actuator 端点
Spring Cloud Commons 提供了一个/service-registry
Actuator 端点。该端点依赖于 Spring 应用程序上下文中的Registration
bean。使用 GET 调用/service-registry
会返回Registration
的状态。对同一端点使用具有 JSON 正文的 POST 会将当前Registration
的状态更改为新值。JSON 正文必须包含带有首选值的status
字段。请参阅更新状态时用于允许值的ServiceRegistry
实现的文档以及为状态返回的值。例如,Eureka 的受支持状态为UP
,DOWN
,OUT_OF_SERVICE
和UNKNOWN
。
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 需要使用虚拟 host 名(即服务名,而不是主机名)。BlockingLoadBalancerClient
用于创建一个完整的物理地址。
要使用负载平衡的
RestTemplate
,您需要在类路径中有一个负载平衡器实现。将 Spring Cloud LoadBalancer starter 添加到您的项目中,以便使用它。
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<String> doOtherStuff() {
return webClientBuilder.build().get().uri("http://stores/stores")
.retrieve().bodyToMono(String.class);
}
}
URI 需要使用虚拟 host 名(即服务名,而不是主机名)。Spring Cloud LoadBalancer 用于创建完整的物理地址。
如果你想使用
@LoadBalanced WebClient.Builder
,您需要在类路径中有一个负载平衡器实现。我们建议您将 Spring Cloud LoadBalancer starter 添加到项目中。然后,使用ReactiveLoadBalancer
。
重试失败的请求
负载均衡的RestTemplate
可以配置为重试失败的请求。默认情况下,该逻辑是禁用的。对于非响应式版本(使用RestTemplate
),您可以通过将 Spring Retry 添加到应用程序的类路径来启用它。对于响应式版本(带有WebTestClient
),你需要设置spring.cloud.loadbalancer.retry.enabled=true
。
如果你想在类路径上有 Spring retry 或 Reactive retry 时禁用重试逻辑,你可以设置Spring .cloud.loadbalancer.retry.enabled=false
。
对于非响应式实现,如果您希望在重试中实现BackOffPolicy
,则需要创建LoadBalancedRetryFactory
类型的 bean 并覆盖createBackOffPolicy()
方法。
对于响应式实现,你只需要将spring.cloud.loadbalancer.retry.backoff.enabled
设置为false
就可以了。
你可以设置:
-
spring.cloud.loadbalancer.retry.maxRetriesOnSameServiceInstance
:指示在同一个ServiceInstance
上重试请求的次数(针对每个选定的实例分别计算) -
spring.cloud.loadbalancer.retry.maxRetriesOnNextServiceInstance
:指示在新选择的ServiceInstance
中重试请求的次数 -
spring.cloud.loadbalancer.retry.retryableStatusCodes
:重试失败后响应的状态码。
或者响应式实现,你可以额外设置:
-
spring.cloud.loadbalancer.retry.backoff.minBackoff
,设置最小回退时间(默认为5毫秒) -
spring.cloud.loadbalancer.retry.backoff.maxBackoff
,设置最大回退时间(默认为 long 整形的最大值,单位毫秒) -
spring.cloud.loadbalancer.retry.backoff.jitter
,设置用于计算每个调用的实际回退持续时间的抖动(默认为5)
对于响应式实现,您还可以实现自己的LoadBalancerRetryPolicy
,以便对负载平衡调用重试有更详细的控制。
对于负载平衡重试,默认情况下,我们用
RetryAwareServiceInstanceListSupplier
包装ServiceInstanceListSupplier
bean,以选择先前选择的不同实例(如果可用)。你可以通过将spring.cloud.loadbalancer.retry.avoidPreviousInstance
的值设置为false
来禁用此行为。
@Configuration
public class MyConfiguration {
@Bean
LoadBalancedRetryFactory retryFactory() {
return new LoadBalancedRetryFactory() {
@Override
public BackOffPolicy createBackOffPolicy(String service) {
return new ExponentialBackOffPolicy();
}
};
}
}
如果你想在你的重试功能中添加一个或多个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 <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
//TODO Do you business...
return true;
}
@Override
public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
//TODO Do you business...
}
@Override
public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> 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<String> doOtherStuff() {
return loadBalanced.build().get().uri("http://stores/stores")
.retrieve().bodyToMono(String.class);
}
public Mono<String> doStuff() {
return webClientBuilder.build().get().uri("http://example.com")
.retrieve().bodyToMono(String.class);
}
}
Spring WebFlux WebClient 作为负载均衡器客户端
Spring WebFlux 可以与响应式和非响应式WebClient
配置一起工作。
Spring WebFlux WebClient
with ReactorLoadBalancerExchangeFilterFunction
你可以配置WebClient
来使用ReactiveLoadBalancer
。如果你将 Spring Cloud LoadBalancer starter 添加到你的项目中,并且spring-webflux
在类路径中,则会自动配置ReactorLoadBalancerExchangeFilterFunction
。下面的例子展示了如何配置一个WebClient
来使用响应式负载均衡器:
public class MyClass {
@Autowired
private ReactorLoadBalancerExchangeFilterFunction lbFunction;
public Mono<String> doOtherStuff() {
return WebClient.builder().baseUrl("http://stores")
.filter(lbFunction)
.build()
.get()
.uri("/stores")
.retrieve()
.bodyToMono(String.class);
}
}
URI 需要使用虚拟 host 名(即服务名,而不是主机名)。ReactorLoadBalancer
用于创建完整的物理地址。
Spring WebFlux WebClient
with a Non-reactive Load Balancer Client
如果spring-webflux
在类路径上,LoadBalancerExchangeFilterFunction
是自动配置的。但是,请注意,这实际上使用了一个非响应式客户端。下面的例子展示了如何配置WebClient
来使用负载平衡器:
public class MyClass {
@Autowired
private LoadBalancerExchangeFilterFunction lbFunction;
public Mono<String> doOtherStuff() {
return WebClient.builder().baseUrl("http://stores")
.filter(lbFunction)
.build()
.get()
.uri("/stores")
.retrieve()
.bodyToMono(String.class);
}
}
URI 需要使用虚拟 host 名(即服务名,而不是主机名)。LoadBalancerClient
用于创建完整的物理地址。
现在不建议使用此方法。我们建议您与 WebFlux with reactive Load-Balancer 一起使用。
忽略网络接口
有时,忽略某些命名的网络接口很有用,以便可以将它们从服务发现注册中排除(例如,在 Docker 容器中运行时)。可以设置正则表达式列表,以使所需的网络接口被忽略。以下配置将忽略docker0
接口以及所有以veth
开头的接口:
spring:
cloud:
inetutils:
ignoredInterfaces:
- docker0
- veth.*
您还可以通过使用正则表达式列表来强制仅使用指定的网络地址,如以下示例所示:
spring:
cloud:
inetutils:
preferredNetworks:
- 192.168
- 10.0
您也可以只使用站点本地地址,如以下示例所示:
spring:
cloud:
inetutils:
useOnlySiteLocalInterfaces: true
有关构成站点本地地址的详细信息,请参见 Inet4Address.html.isSiteLocalAddress()。
HTTP 客户端工厂
Spring Cloud Commons 提供了 beans 用于创建 Apache HTTP 客户端(ApacheHttpClientFactory
)和 OK HTTP 客户端(OkHttpClientFactory
)。仅当 OK HTTP jar 位于类路径上时,才创建OkHttpClientFactory
bean。此外,Spring Cloud Commons 提供了 beans 用于创建两个客户端使用的连接管理器:ApacheHttpClientConnectionManagerFactory
用于 Apache HTTP 客户端,OkHttpClientConnectionPoolFactory
用于 OK HTTP 客户端。如果您想自定义在项目中如何创建 HTTP 客户端,则可以提供自己的 beans 实现。另外,如果您提供类型为HttpClientBuilder
或OkHttpClient.Builder
的 bean,则默认工厂将使用这些构建器作为返回到项目的构建器的基础。您还可以通过将spring.cloud.httpclientfactories.apache.enabled
或spring.cloud.httpclientfactories.ok.enabled
设置为false
来禁用这些 beans 的创建。
启用的功能
Spring Cloud Commons 提供了一个/features
actuator 端点。该端点返回类路径上可用的功能以及是否已启用它们。返回的信息包括功能类型、名称、版本和供应商。
功能类型
“功能”有两种类型:抽象和命名。
抽象功能是定义接口或抽象类并创建实现的功能,例如DiscoveryClient
,LoadBalancerClient
或LockService
。抽象类或接口用于在上下文中找到该类型的 bean。显示的版本为bean.getClass().getPackage().getImplementationVersion()
。
命名功能是没有实现的特定类的功能,例如“Circuit Breaker”,“API Gateway”,“Spring Cloud Bus”等。这些功能需要一个名称和一个 bean 类型。
声明功能
任何模块都可以声明任意数量的HasFeature
beans,如以下示例所示:
@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();
}
这些 beans 中的每一个都应放入受到适当保护的@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 Boot 版本,只需用兼容的 Spring Boot 版本的逗号分隔列表设置spring.cloud.compatibility-verifier.compatible-boot-versions
属性。