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)无限制权限策略文件。有关更多信息,请参阅以下链接:
对于使用的JRE/JDK x64/x86版本,将文件解压到JDK/jre/lib/security文件夹中。
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.yml
或application.yml
中)是一个好主意。要将属性spring.application.name
用作应用程序的上下文ID,必须在bootstrap.[properties | yml]
中设置它。
如果您想检索特定的配置文件,您还应该在bootstrap.[properties | yml]
中设置spring.profiles.active
。
您可以通过设置spring.cloud.bootstrap.enabled=false
来完全禁用引导过程(例如在系统属性中)。
如果您从SpringApplication或SpringApplicationBuilder构建应用程序上下文,则引导上下文将作为父上下文添加到该上下文。子上下文从父上下文继承属性源和概要文件是Spring的一个特性,因此与不使用Spring Cloud Config构建相同的上下文相比,“主”应用程序上下文包含额外的属性源。
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.properties
的development
配置文件。
由引导上下文添加到应用程序中的属性源通常是“远程的”(例如,来自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
。如果您有一个是HikariDataSource
的DataSource
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)无限制权限策略文件。有关更多信息,请参阅以下链接:
对于使用的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客户机使用,而不依赖于实现(例如,使用Eureka或Consul进行发现)。
Spring Cloud Commons提供了@EnableDiscoveryClient
注释。它通过META-INF/spring.factories
寻找DiscoveryClient
和ReactiveDiscoveryClient
接口的实现。发现客户端的实现将配置类添加到spring.factories
的org.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=false
或spring.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
实现的顺序,以及其他的ConsulDiscoveryClient
、EurekaDiscoveryClient
和ZookeeperDiscoveryClient
。为此,您只需要设置spring.cloud.{clientIdentifier}.discovery.order
(或Eureka的eureka.client.order
)属性到期望的值。
如果类路径中没有服务注册支持的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
使用Eurekaregistry
与EurekaServiceRegistry
一起使用ConsulRegistration
与ConsulServiceRegistry
一起使用如果您正在使用ServiceRegistry
接口,则需要为您正在使用的ServiceRegistry
实现传递正确的Registry
实现。
默认情况下,ServiceRegistry
实现自动注册正在运行的服务。要禁用该行为,可以设置:@EnableDiscoveryClient(autoRegister=false)
永久禁用自动注册。spring.cloud.service-registry.auto-registration.enabled=false
通过配置禁用该行为。
当服务自动注册时,将触发两个事件。第一个事件称为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支持的状态是UP
、DOWN
、OUT_OF_SERVICE
和UNKNOWN
。
可以将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
也可以使用,但目前正在维护中,我们不建议将其添加到新项目中。
默认情况下,如果你同时拥有
RibbonLoadBalancerClient
和BlockingLoadBalancerClient
,为了保持向后兼容性,使用RibbonLoadBalancerClient
。要覆盖它,您可以设置spring.cloud.loadbalance .ribbon.enabled
属性为false
。
您可以将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- loadbalancer
和spring-cloud-starter-netflix-Ribbon
,则默认使用Ribbon。要切换到Spring Cloud LoadBalancer,请设置spring.cloud.loadbalancer.ribbon.enabled
属性为假。
可以配置负载平衡的RestTemplate
来重试失败的请求。默认情况下,该逻辑是禁用的。您可以通过在应用程序的类路径中添加Spring Retry来启用它。负载平衡的RestTemplate
执行与重试失败请求相关的功能区配置值。你可以使用client.ribbon.MaxAutoRetries
,client.ribbon.MaxAutoRetriesNextServer
和client.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
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
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);
}
}
WebClient
作为负载平衡客户机Spring WebFlux可以同时使用响应性和非响应性的WebClient
配置,正如主题所描述的:
WebClient
和ReactorLoadBalancerExchangeFilterFunction
WebClient
和ReactorLoadBalancerExchangeFilterFunction
您可以将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
。
WebClient
带有非反应性负载均衡器客户端如果你的项目中没有Spring Cloud LoadBalancer starter,但是你有spring-cloud-starter-netflix-ribbon,你仍然可以使用LoadBalancerClient
的WebClient
。如果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() 。
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的实现。另外,如果您提供一个类型为HttpClientBuilder
或OkHttpClient.Builder
的bean,默认工厂使用这些构建器作为返回下游项目的构建器的基础。您还可以通过设置spring.cloud.httpclientfactories.apache.enabled
或spring.cloud.httpclientfactories.ok.enabled
为false
来禁用这些bean的创建。
Spring Cloud Commons提供了一个/features
执行器端点。这个端点返回类路径上可用的特性以及它们是否被启用。返回的信息包括特性类型、名称、版本和供应商。
有两种类型的“特征”:抽象的和命名的。
抽象特性是定义接口或抽象类并由实现创建的特性,如DiscoveryClient
、LoadBalancerClient
或LockService
。抽象类或接口用于在上下文中查找该类型的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 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提供了自己的客户端负载平衡器抽象和实现。对于负载平衡机制,添加了ReactiveLoadBalancer
接口,并为其提供了基于轮询的实现。为了从反应的ServiceInstanceListSupplier
获得实例选择。目前,我们支持ServiceInstanceListSupplier
的基于服务发现的实现,该实现使用类路径中可用的Discovery Client从服务发现中检索可用的实例。
为了方便使用Spring Cloud LoadBalancer,我们提供了可以与WebClient
使用的ReactorLoadBalancerExchangeFilterFunction
和与RestTemplate
一起使用的BlockingLoadBalancerClient
。你可以在以下部分看到更多的信息和使用的例子:
除了每次必须选择实例时通过DiscoveryClient
检索实例的基本ServiceInstanceListSupplier
实现之外,我们提供了两个缓存实现。
如果类路径中有com.github.ben-manes.caffeine:caffeine
,那么将使用基于Caffeine的实现。有关如何配置它的信息,请参阅LoadBalancerCacheConfiguration部分。
如果您正在使用Caffeine,您还可以通过在spring.cloud.loadbalancer.cache.caffeine.spec
属性中传递您自己的Caffeine Specification来覆盖LoadBalancer的默认Caffeine缓存设置。
警告:通过您自己的Caffeine Specification将覆盖任何其他LoadBalancerCache设置,包括一般的LoadBalancer缓存配置字段,如ttl
和capacity
。
如果类路径中没有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缓存。
尽管基本的、非缓存的实现对于原型设计和测试很有用,但它的效率远低于缓存版本,因此我们建议在生产中始终使用缓存版本。
为了启用基于区域的负载平衡,我们提供了ZonePreferenceServiceInstanceListSupplier
。我们使用特定于DiscoveryClient
的zone
配置(例如,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);
}
}
可以为LoadBalancer启用计划健康检查。为此提供了HealthCheckServiceInstanceListSupplier
。它定期验证委托ServiceInstanceListSupplier提供的实例是否仍然存在,并且只返回健康的实例,除非没有实例—然后返回所有检索到的实例。
在使用
SimpleDiscoveryClient
时,这种机制特别有用。对于由实际服务注册中心支持的客户端,不需要使用它,因为我们在查询外部ServiceDiscovery之后已经获得了健康的实例。
对于每个服务只有少量实例的设置,也建议使用此供应商,以避免重试调用失败的实例。
HealthCheckServiceInstanceListSupplier
使用带有spring.cloud.loadbalance.healthcheck
前缀的属性。您可以为调度程序设置initialDelay
和interval
。您可以通过设置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 Fluxreplay()
的缓存机制,因此,如果它被使用,您可能希望跳过使用CachingServiceInstanceListSupplier包装该供应商。
我们还提供了一个启动器,可以让您轻松地在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.enabled
为false
。
您也可以使用@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
,来使用我们的替代预定义配置来替代默认配置。
您可以使用这个特性来实例化ServiceInstanceListSupplier
或ReactorLoadBalancer
的不同实现,无论是您编写的,还是我们提供的替代方案(例如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断路器提供了跨不同断路器实现的抽象。它为您的应用程序提供了一致的API,让开发人员可以选择最适合您的应用程序需要的断路器实现。
Spring Cloud支持以下断路器实现:
要在代码中创建断路器,可以使用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
和一个Function
。Supplier
是代码,你要包装在断路器。该Function
是在断路器跳闸时执行的回退。函数传递了导致回退被触发的Throwable
。如果不想提供回退,可以选择排除回退。
如果项目反应器位于类路径上,您还可以为响应代码使用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
。
有关如何自定义给定实现的详细信息,请参阅以下文档:
一些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)
Spring Cloud Context提供了一个PropertySource,它根据键缓存随机值。在缓存功能之外,它的工作原理与Spring Boot的RandomValuePropertySource相同。当您希望在Spring应用程序上下文重新启动后仍保持一致的随机值时,这个随机值可能非常有用。属性值采用cachedrandom.[yourkey].[type]
的形式,yourkey
是缓存中的密钥。type
值可以是Spring引导的RandomValuePropertySource
支持的任何类型。
myrandom=${cachedrandom.appname.value}