# 云原生应用程序 [Cloud Native](https://pivotal.io/platform-as-a-service/migrating-to-cloud-native-application-architectures-ebook)是一种应用程序开发风格,它鼓励在持续交付和价值驱动的开发领域轻松采用最佳实践。一个相关的规程是构建[12 因素应用程序](https://12factor.net/),在该规程中,开发实践与交付和操作目标保持一致——例如,通过使用声明式编程以及管理和监视。 Spring 云以许多特定的方式促进了这些开发风格。起点是一组特性,分布式系统中的所有组件都需要轻松访问这些特性。 这些特性中的许多都包含在[Spring Boot](https://projects.spring.io/spring-boot)中, Spring 云就是建立在这些特性上的。 Spring Cloud 以两个库的形式提供了更多的功能: Spring Cloud Context 和 Spring Cloud Commons。 Spring Cloud Context 为 Spring 云应用程序的`ApplicationContext`提供实用程序和特殊服务(Bootstrap 上下文、加密、刷新范围和环境端点)。 Spring Cloud Commons 是一组抽象和公共类,用于不同的云实现(例如 Spring Cloud Netflix 和 Spring Cloud Consul)。 如果由于“非法密钥大小”而导致异常,并且使用了 Sun 的 JDK,则需要安装 JCE 的无限强度管辖权策略文件。有关更多信息,请参见以下链接: * [Java 6 JCE](https://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html) * [Java 7 JCE](https://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html) * [Java 8 JCE](https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) 将这些文件解压缩到 JDK/JRE/lib/security 文件夹中,用于你使用的 JRE/JDK x64/x86 的任何版本。 | |Spring 云是在非限制性的 Apache2.0 许可下发布的。
如果你想对文档的这一部分做出贡献,或者如果你发现了一个错误,那么你可以在{docslink}[github]上找到该项目的源代码和发行追踪器。| |---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ## [](#spring-cloud-context-application-context-services)[1. Spring Cloud Context: Application Context Services](#spring-cloud-context-application-context-services) Spring 对于如何使用 Spring 构建应用程序,Boot 持有一种固执己见的观点。例如,它具有用于公共配置文件的常规位置,并且具有用于公共管理和监视任务的端点。 Spring 云在此基础上构建,并添加了一些系统中的许多组件将使用或偶尔需要的功能。 ### [](#the-bootstrap-application-context)[1.1.引导程序应用程序上下文](#the-bootstrap-application-context) Spring 云应用程序通过创建“引导”上下文来操作,该上下文是主应用程序的父上下文。这个上下文负责从外部源加载配置属性,并对本地外部配置文件中的属性进行解密。这两个上下文共享一个`Environment`,它是任何 Spring 应用程序的外部属性的源。默认情况下,Bootstrap 属性(不是`bootstrap.properties`,而是在 Bootstrap 阶段加载的属性)是以较高的优先级添加的,因此它们不能被本地配置覆盖。 与主应用程序上下文相比,引导程序上下文使用不同的约定来定位外部配置。而不是`application.yml`(或`.properties`),你可以使用`bootstrap.yml`,将引导程序和主上下文的外部配置很好地分开。下面的清单展示了一个示例: 示例 1.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`(例如,在系统属性中),可以完全禁用引导程序进程。 ### [](#application-context-hierarchies)[1.2.应用程序上下文层次结构](#application-context-hierarchies) 如果你从`SpringApplication`或`SpringApplicationBuilder`构建应用程序上下文,则引导程序上下文将作为父上下文添加到该上下文。 Spring 的一个特性是,子上下文从其父上下文继承属性源和配置文件,因此,与在没有 Spring 云配置的情况下构建相同的上下文相比,“主”应用程序上下文包含额外的属性源。额外的财产来源是: * “bootstrap”:如果在 bootstrap 上下文中找到任何`PropertySourceLocators`,并且它们具有非空属性,则会以高优先级出现一个可选的`CompositePropertySource`。一个例子是来自 Spring Cloud Config 服务器的属性。有关如何自定义此属性源的内容,请参见“[自定义引导程序属性源](#customizing-bootstrap-property-sources)”。 * “ApplicationConfig:[ Classpath:bootstrap.yml]”(如果 Spring 配置文件处于活动状态,则包含相关文件):如果你有`bootstrap.yml`(或`.properties`),则这些属性将用于配置 bootstrap 上下文。然后,当设置其父上下文时,它们被添加到子上下文中。它们的优先级低于`application.yml`(或`.properties`)和作为创建 Spring 引导应用程序过程的正常部分添加到子程序的任何其他属性源。有关如何自定义这些属性源的内容,请参见“[更改 BootStrap 属性的位置](#customizing-bootstrap-properties)”。 由于属性源的排序规则,“bootstrap”条目优先。然而,请注意,这些不包含来自`bootstrap.yml`的任何数据,这具有很低的优先级,但可以用来设置默认值。 你可以通过设置你创建的任何`ApplicationContext`的父上下文来扩展上下文层次结构——例如,通过使用它自己的接口或使用`SpringApplicationBuilder`便利方法(`parent(),`child()`和`sibling()`)。引导程序上下文是你自己创建的最高级祖先的父级。层次结构中的每个上下文都有自己的“Bootstrap”(可能是空的)属性源,以避免无意中将值从父级推广到子级。如果有一个配置服务器,层次结构中的每个上下文(原则上)也可以有不同的`spring.application.name`,因此也可以有不同的远程属性源。 Spring 正常的应用程序上下文行为规则适用于属性解析:来自子上下文的属性通过名称和属性源名覆盖父上下文中的属性。(如果子具有与父具有相同名称的属性源,则来自父的值不包含在子属性中)。 请注意,`SpringApplicationBuilder`允许你在整个层次结构中共享`Environment`,但这不是默认的。因此,兄弟上下文(尤其是)不需要具有相同的概要文件或属性源,即使它们可能与父上下文共享公共值。 ### [](#customizing-bootstrap-properties)[1.3.更改 BootStrap 属性的位置](#customizing-bootstrap-properties) `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`。实际上,它们是用来设置 Bootstrap`ApplicationContext`的,方法是在其`Environment`中设置这些属性。如果有一个活动配置文件(来自`spring.profiles.active`或通过你正在构建的上下文中的`Environment`API),则该配置文件中的属性也将被加载,这与常规 Spring 启动应用程序中的属性相同——例如,对于`development`配置文件,从`bootstrap-development.properties`开始。 ### [](#overriding-bootstrap-properties)[1.4.重写远程属性的值](#overriding-bootstrap-properties) 通过引导程序上下文添加到应用程序中的属性源通常是“远程”的(例如,来自 Spring Cloud Config Server)。默认情况下,不能在本地重写它们。如果你想让你的应用程序用它们自己的系统属性或配置文件重写远程属性,那么远程属性源必须通过设置`spring.cloud.config.allowOverride=true`来授予它权限(在本地设置它是不起作用的)。一旦设置了该标志,两个更细粒度的设置将控制远程属性相对于系统属性和应用程序本地配置的位置: * `spring.cloud.config.overrideNone=true`:覆盖任何本地属性源。 * `spring.cloud.config.overrideSystemProperties=false`:只有系统属性、命令行参数和环境变量(但不包括本地配置文件)才应覆盖远程设置。 ### [](#customizing-the-bootstrap-configuration)[1.5.自定义引导程序配置](#customizing-the-bootstrap-configuration) 通过在名为`org.springframework.cloud.bootstrap.BootstrapConfiguration`的键下向`/META-INF/spring.factories`添加条目,可以将引导程序上下文设置为执行任何你喜欢的操作。这包含一个用逗号分隔的列表 Spring `@Configuration`类,这些类用于创建上下文。可以在这里创建你希望在主应用程序上下文中可用以进行自动连接的任何 bean。有一份`@Beans`型`ApplicationContextInitializer`的特殊合同。如果要控制启动序列,可以使用`@Order`注释标记类(默认顺序为`last`)。 | |在添加自定义`BootstrapConfiguration`时,请注意,所添加的类不是`@ComponentScanned`错误地添加到你的“主”应用程序上下文中,
为引导配置类使用一个单独的包名,并确保该名称尚未被`@ComponentScan`或`@SpringBootApplication`注释的配置类覆盖。| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 引导程序通过将初始化器注入主`SpringApplication`实例(这是正常的 Spring 启动序列,无论它是作为独立应用程序运行还是部署在应用程序服务器中)而结束。首先,从`spring.factories`中的类创建一个引导程序上下文。然后,所有`@Beans`类型的`ApplicationContextInitializer`在启动之前被添加到主`SpringApplication`中。 ### [](#customizing-bootstrap-property-sources)[1.6.自定义引导程序属性源](#customizing-bootstrap-property-sources) 由 BootStrap 进程添加的用于外部配置的默认属性源是 Spring Cloud Config 服务器,但是你可以通过将类型为`PropertySourceLocator`的 bean 添加到 BootStrap 上下文(通过`spring.factories`)来添加其他源。例如,你可以从不同的服务器或数据库插入额外的属性。 作为示例,请考虑以下自定义定位器: ``` @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`将出现在其 Classpath 上包含该 jar 的任何应用程序中: ``` org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator ``` ### [](#logging-configuration)[1.7.日志配置](#logging-configuration) 如果你使用 Spring 引导来配置日志设置,那么如果你希望将此配置应用于所有事件,则应将其放置在`bootstrap.[yml | properties]`中。 | |对于 Spring Cloud 要正确初始化日志配置,不能使用自定义前缀。例如,在初始化日志系统时,使用不会被 Spring Cloud 识别。| |---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#environment-changes)[1.8.环境变化](#environment-changes) 应用程序监听`EnvironmentChangeEvent`并以两种标准方式对更改做出反应(额外的`ApplicationListeners`可以以正常方式添加为`@Beans`)。当观察到`EnvironmentChangeEvent`时,它具有已更改的键值列表,应用程序将这些值用于: * 在上下文中重新绑定任何`@ConfigurationProperties`bean。 * 在`logging.level.*`中为任何属性设置记录器级别。 请注意, Spring Cloud Config 客户机在默认情况下不会轮询`Environment`中的更改。通常,我们不建议使用这种方法来检测更改(尽管你可以使用“@schedule”注释对其进行设置)。如果你有一个扩展的客户机应用程序,那么最好向所有实例广播`EnvironmentChangeEvent`,而不是让它们轮询更改(例如,通过使用[Spring Cloud Bus](https://github.com/spring-cloud/spring-cloud-bus))。 `EnvironmentChangeEvent`涵盖了一大类刷新用例,只要你可以实际对`Environment`进行更改并发布事件。请注意,这些 API 是公共的,并且是 CORE 的一部分 Spring)。你可以通过访问`/configprops`端点(一种标准的 Spring 引导执行器功能)来验证这些更改是否绑定到`@ConfigurationProperties`bean。例如,`DataSource`可以在运行时更改其`maxPoolSize`(由 Spring 引导创建的默认`DataSource`是`@ConfigurationProperties` Bean)并动态地增加容量。重新绑定`@ConfigurationProperties`并不包括另一大类用例,其中你需要对刷新进行更多控制,并且需要对整个`ApplicationContext`进行原子级更改。为了解决这些问题,我们有`@RefreshScope`。 ### [](#refresh-scope)[1.9.刷新范围](#refresh-scope) 当存在配置更改时,标记为`@RefreshScope`的 Spring `@Bean`将得到特殊处理。这个特性解决了有状态 bean 仅在初始化时才注入配置的问题。例如,如果当通过`Environment`更改数据库 URL 时,`DataSource`具有打开的连接,那么你可能希望这些连接的持有者能够完成他们正在做的事情。然后,下一次当某个对象从池中借用一个连接时,它将获得一个带有新 URL 的连接。 有时,在某些只能初始化一次的 bean 上应用`@RefreshScope`注释甚至是强制性的。如果 Bean 是“不可变的”,则必须用`@RefreshScope`注释 Bean,或者在属性键下指定类名:`spring.cloud.refresh.extra-refreshable`。 | |如果你有一个`DataSource` Bean,这是一个`HikariDataSource`,它不能被
刷新。它是`spring.cloud.refresh.never-refreshable`的默认值。如果需要刷新
不同的`DataSource`实现,请选择该实现。| |---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| Refresh Scope bean 是惰性代理,它在使用它们时(即调用方法时)进行初始化,并且作用域充当初始化值的缓存。要强制 Bean 在下一个方法调用时重新初始化,你必须使其缓存条目无效。 `RefreshScope`是上下文中的 Bean,并具有一个公共`refreshAll()`方法,可以通过清除目标缓存来刷新范围内的所有 bean。`/refresh`端点公开此功能(通过 HTTP 或 JMX)。要按名称刷新个人 Bean,还存在`refresh(String)`方法。 要公开`/refresh`端点,需要向应用程序添加以下配置: ``` management: endpoints: web: exposure: include: refresh ``` | |`@RefreshScope`在`@Configuration`类上工作(技术上),但它可能会导致令人惊讶的行为。
例如,这并不意味着在该类中定义的所有`@Beans`本身都在`@RefreshScope`中。
,具体来说,任何依赖于这些 bean 的东西都不能依赖于在启动刷新时对它们进行更新,除非它本身在`@RefreshScope`中。
在这种情况下,它会在刷新时被重建,并且其依赖项会被重新注入。
在这一点上,它们会从刷新的`@Configuration`中重新初始化。| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#encryption-and-decryption)[1.10.加密和解密](#encryption-and-decryption) Spring 云具有用于本地解密属性值的`Environment`预处理器。它遵循与 Spring Cloud Config 服务器相同的规则,并且通过`encrypt.*`具有相同的外部配置。因此,你可以使用`{cipher}*`形式的加密值,并且,只要存在有效的密钥,就可以在主应用程序上下文获得`Environment`设置之前对它们进行解密。要在应用程序中使用加密功能,你需要在 Classpath( Maven 坐标:`org.springframework.security:spring-security-rsa`)中包括 Spring 安全 RSA,并且还需要在 JVM 中提供全强度的 JCE 扩展。 如果由于“非法密钥大小”而导致异常,并且使用了 Sun 的 JDK,则需要安装 JCE 的无限强度管辖权策略文件。有关更多信息,请参见以下链接: * [Java 6 JCE](https://www.oracle.com/technetwork/java/javase/downloads/jce-6-download-429243.html) * [Java 7 JCE](https://www.oracle.com/technetwork/java/javase/downloads/jce-7-download-432124.html) * [Java 8 JCE](https://www.oracle.com/technetwork/java/javase/downloads/jce8-download-2133166.html) 将这些文件解压缩到 JDK/JRE/lib/security 文件夹中,用于你使用的 JRE/JDK x64/x86 的任何版本。 ### [](#endpoints)[1.11. Endpoints](#endpoints) 对于 Spring 引导执行器应用程序,一些额外的管理端点是可用的。你可以使用: * `POST`到`/actuator/env`以更新`Environment`并重新绑定`@ConfigurationProperties`和日志级别。要启用此端点,你必须设置`management.endpoint.env.post.enabled=true`。 * `/actuator/refresh`重新加载引导表带上下文并刷新`@RefreshScope`bean。 * `/actuator/restart`关闭`ApplicationContext`并重新启动它(默认禁用)。 * `/actuator/pause`和`/actuator/resume`用于在`ApplicationContext`上调用`Lifecycle`方法(`stop()’和`start()`)。 | |如果禁用`/actuator/restart`端点,那么`/actuator/pause`和`/actuator/resume`端点
也将被禁用,因为它们只是`/actuator/restart`的特殊情况。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ## [](#spring-cloud-commons-common-abstractions)[2. Spring Cloud Commons: Common Abstractions](#spring-cloud-commons-common-abstractions) 诸如服务发现、负载平衡和断路器之类的模式将自身扩展到一个公共抽象层,该抽象层可以被所有 Spring 云客户机使用,而不依赖于实现(例如,使用 Eureka 或 Consul 的发现)。 ### [](#discovery-client)[2.1. The `@EnableDiscoveryClient` Annotation](#discovery-client) Spring Cloud Commons 提供了`@EnableDiscoveryClient`注释。这将查找带有`META-INF/spring.factories`和`ReactiveDiscoveryClient`接口的`META-INF/spring.factories`的实现。发现客户端的实现在`org.springframework.cloud.client.discovery.EnableDiscoveryClient`键下向`spring.factories`添加配置类。`DiscoveryClient`实现的示例包括[Spring Cloud Netflix Eureka](https://cloud.spring.io/spring-cloud-netflix/)、[Spring Cloud Consul Discovery](https://cloud.spring.io/spring-cloud-consul/)和[Spring Cloud Zookeeper Discovery](https://cloud.spring.io/spring-cloud-zookeeper/)。 Spring 默认情况下,云将提供阻塞和反应式服务发现客户端。通过设置`spring.cloud.discovery.blocking.enabled=false`或`spring.cloud.discovery.reactive.enabled=false`,你可以轻松地禁用阻塞和/或反应客户端。要完全禁用服务发现,只需设置`spring.cloud.discovery.enabled=false`。 默认情况下,`DiscoveryClient`的实现方式是用远程发现服务器自动注册本地 Spring 引导服务器。可以通过在`@EnableDiscoveryClient`中设置`autoRegister=false`来禁用此行为。 | |`@EnableDiscoveryClient`不再需要。
你可以在 Classpath 上放置一个`DiscoveryClient`实现,以使 Spring 引导应用程序向服务发现服务器注册。| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| #### [](#health-indicators)[2.1.1.健康指标](#health-indicators) Commons 自动配置以下 Spring 引导健康指示器。 ##### [](#discoveryclienthealthindicator)[发现潜在的指示剂](#discoveryclienthealthindicator) 此健康指标是基于当前注册的`DiscoveryClient`实现的。 * 要完全禁用,请设置`spring.cloud.discovery.client.health-indicator.enabled=false`。 * 要禁用 description 字段,请设置`spring.cloud.discovery.client.health-indicator.include-description=false`。否则,它可以像卷起的`description`的`HealthIndicator`那样冒泡。 * 要禁用服务检索,请设置`spring.cloud.discovery.client.health-indicator.use-services-query=false`。默认情况下,指示器调用客户机的`getServices`方法。在使用许多注册服务的部署中,在每次检查期间检索所有服务的成本可能太高。这将跳过服务检索,而是使用客户机的`probe`方法。 ##### [](#discoverycompositehealthcontributor)[发现复合健康贡献者](#discoverycompositehealthcontributor) 这个复合健康指示器基于所有注册的`DiscoveryHealthIndicator`bean。要禁用,请设置`spring.cloud.discovery.client.composite-indicator.enabled=false`。 #### [](#ordering-discoveryclient-instances)[2.1.2. Ordering `DiscoveryClient` instances](#ordering-discoveryclient-instances) `DiscoveryClient`接口扩展`Ordered`。这在使用多个发现客户机时非常有用,因为它允许你定义返回的发现客户机的顺序,类似于你如何订购由 Spring 应用程序加载的 bean。默认情况下,任意`DiscoveryClient`的顺序设置为 `0’。如果你想为你的自定义`DiscoveryClient`实现设置不同的顺序,你只需要覆盖`getOrder()`方法,以便它返回适合你的设置的值。除此之外,你还可以使用属性来设置由 Spring Cloud 提供的`DiscoveryClient`实现的顺序,其中包括`ConsulDiscoveryClient`、`EurekaDiscoveryClient`和 `zookeeperDiscoveryclient’。为了做到这一点,你只需要将 ` Spring.cloud.{clientifier}.discovery.order`(或`eureka.client.order`for Eureka)属性设置为所需的值。 #### [](#simplediscoveryclient)[2.1.3.简单的发现](#simplediscoveryclient) 如果在 Classpath 中没有支持服务注册中心的`DiscoveryClient`,则将使用`SimpleDiscoveryClient`实例,该实例使用属性来获取有关服务和实例的信息。 有关可用实例的信息应通过以下格式的属性传递到:` Spring.cloud.discovery.client.simple.instants.service1[0].uri=http://s11:8080`,其中 ` Spring.cloud.discovery.client.simple.instants` 是常见的前缀,那么`service1`表示所讨论的服务的 ID,而`[0]`表示实例的索引号(在示例中可见,索引以`0`开始),然后`uri`的值是实例可用的实际 URI。 ### [](#serviceregistry)[2.2.ServiceRegistry](#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`实现。 * `ZookeeperRegistration`与`ZookeeperServiceRegistry`连用 * `EurekaRegistration`与`EurekaServiceRegistry`连用 * `ConsulRegistration`与`ConsulServiceRegistry`连用 如果你正在使用`ServiceRegistry`接口,那么你将需要为所使用的`Registry`实现传递正确的`Registry`实现。 #### [](#serviceregistry-auto-registration)[2.2.1.ServiceRegistry 自动注册](#serviceregistry-auto-registration) 默认情况下,`ServiceRegistry`实现自动注册正在运行的服务。要禁用该行为,可以将:\*`@EnableDiscoveryClient(autoRegister=false)`设置为永久禁用自动注册。\*`spring.cloud.service-registry.auto-registration.enabled=false`通过配置禁用行为。 ##### [](#serviceregistry-auto-registration-events)[ServiceRegistry 自动注册事件](#serviceregistry-auto-registration-events) 当服务自动注册时,将触发两个事件。第一个事件称为“InstancePreRegistereRedevent”,在服务注册之前被触发。第二个事件称为`InstanceRegisteredEvent`,在服务注册后触发。你可以注册一个“ApplicationListener”来监听这些事件并对其做出反应。 | |如果`spring.cloud.service-registry.auto-registration.enabled`属性设置为`false`,则不会触发这些事件。| |---|---------------------------------------------------------------------------------------------------------------------------| #### [](#service-registry-actuator-endpoint)[2.2.2.服务注册中心执行器端点](#service-registry-actuator-endpoint) 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`。 ### [](#rest-template-loadbalancer-client)[2.3. Spring RestTemplate as a Load Balancer Client](#rest-template-loadbalancer-client) 你可以将`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 需要使用虚拟主机名(即服务名,而不是主机名)。BlockingLoadBalancerClient 用于创建完整的物理地址。 | |要使用负载平衡的`RestTemplate`,你需要在 Classpath 中有一个负载平衡器实现。
将[Spring Cloud LoadBalancer starter](#spring-cloud-loadbalancer-starter)添加到你的项目中才能使用它。| |---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#webclinet-loadbalancer-client)[2.4. Spring WebClient as a Load Balancer Client](#webclinet-loadbalancer-client) 你可以将`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 需要使用虚拟主机名(即服务名,而不是主机名)。 Spring 云负载平衡器用于创建完整的物理地址。 | |如果要使用`@LoadBalanced WebClient.Builder`,则需要在 Classpath 中有一个负载均衡器
实现。我们建议你将[Spring Cloud LoadBalancer starter](#spring-cloud-loadbalancer-starter)添加到你的项目中。
然后,下面使用`ReactiveLoadBalancer`。| |---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| #### [](#retrying-failed-requests)[2.4.1.重试失败的请求](#retrying-failed-requests) 可以将负载平衡的`RestTemplate`配置为重试失败的请求。默认情况下,此逻辑是禁用的。对于非反应性版本(带有`RestTemplate`),你可以通过在应用程序的 Classpath 中添加[Spring Retry](https://github.com/spring-projects/spring-retry)来启用它。对于反应式版本(带有`WebTestClient), you need to set ` Spring.cloud.loadBalancer.retry.enabled=true`)。 如果希望在 Classpath 上使用 Spring 重试或反应式重试禁用重试逻辑,则可以设置`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`-设置最大退避持续时间(默认情况下,最大长值为毫秒) - `spring.cloud.loadbalancer.retry.backoff.jitter`-设置用于计算的抖动 g 每个调用的实际退避持续时间(默认情况下为 0.5)。 对于反应式实现,你还可以实现你自己的`LoadBalancerRetryPolicy`,以便对负载平衡的调用重试进行更详细的控制。 | |单独的 loadbalancer 客户机可以单独配置,具有与上面相同的属性,但前缀是`spring.cloud.loadbalancer.clients..*`,其中`clientId`是 loadbalancer 的名称。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |对于负载平衡的重试,默认情况下,我们将`ServiceInstanceListSupplier` Bean 换成`RetryAwareServiceInstanceListSupplier`,以便从先前选择的实例中选择不同的实例(如果可用的话)。可以通过将`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 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... } }}; } }; } } ``` ### [](#multiple-resttemplate-objects)[2.5. Multiple `RestTemplate` Objects](#multiple-resttemplate-objects) 如果你想要一个不是负载平衡的`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`。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#multiple-webclient-objects)[2.6.多个 WebClient 对象](#multiple-webclient-objects) 如果你想要一个不是负载平衡的`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); } } ``` ### [](#loadbalanced-webclient)[2.7. Spring WebFlux `WebClient` as a Load Balancer Client](#loadbalanced-webclient) Spring WebFlux 可以同时处理反应性和非反应性`WebClient`配置,正如主题所描述的那样: * [Spring WebFlux `WebClient` with `ReactorLoadBalancerExchangeFilterFunction`](#webflux-with-reactive-loadbalancer) * [[负载平衡器-交换-过滤器-功能负载平衡器-交换-过滤器-功能]](# 负载平衡器-交换-过滤器-功能负载平衡器-交换-过滤器-功能) #### [](#webflux-with-reactive-loadbalancer)[2.7.1. Spring WebFlux `WebClient` with `ReactorLoadBalancerExchangeFilterFunction`](#webflux-with-reactive-loadbalancer) 你可以将`WebClient`配置为使用`ReactiveLoadBalancer`。如果将[Spring Cloud LoadBalancer starter](#spring-cloud-loadbalancer-starter)添加到项目中,并且`spring-webflux`位于 Classpath 上,则`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`用于创建完整的物理地址。 #### [](#load-balancer-exchange-filter-function)[2.7.2. Spring WebFlux `WebClient` with a Non-reactive Load Balancer Client](#load-balancer-exchange-filter-function) 如果`spring-webflux`在 Classpath 上,则`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](#webflux-with-reactive-loadbalancer)代替。 ### [](#ignore-network-interfaces)[2.8.忽略网络接口](#ignore-network-interfaces) 有时,忽略某些已命名的网络接口是有用的,这样它们就可以被排除在服务发现注册之外(例如,在 Docker 容器中运行时)。可以设置一个正则表达式列表,以忽略所需的网络接口。以下配置忽略`docker0`接口和所有以`veth`开头的接口: 示例 2.application.yml ``` spring: cloud: inetutils: ignoredInterfaces: - docker0 - veth.* ``` 还可以通过使用正则表达式列表强制只使用指定的网络地址,如下例所示: 示例 3.bootstrap.yml ``` spring: cloud: inetutils: preferredNetworks: - 192.168 - 10.0 ``` 你还可以强制只使用站点本地地址,如下例所示: 示例 4.application.yml ``` spring: cloud: inetutils: useOnlySiteLocalInterfaces: true ``` 有关什么是站点本地地址的更多详细信息,请参见[iNet4address.html.issitelocaladdress()](https://docs.oracle.com/javase/8/docs/api/java/net/Inet4Address.html#isSiteLocalAddress--)。 ### [](#http-clients)[2.9.HTTP 客户端工厂](#http-clients) Spring Cloud Commons 提供了用于创建 Apache HTTP 客户端和 OK HTTP 客户端的 bean。只有当确定的 HTTP jar 在 Classpath 上时,才会创建`OkHttpClientFactory` Bean。此外, Spring Cloud Commons 提供了用于创建两个客户端使用的连接管理器的 bean:用于 Apache HTTP 客户端的和用于 OK HTTP 客户端的。如果你想定制如何在下游项目中创建 HTTP 客户机,那么你可以提供你自己的这些 bean 的实现。此外,如果你提供了类型`HttpClientBuilder`或`OkHttpClient.Builder`的 Bean,则默认工厂将这些构建器用作将构建器返回到下游项目的基础。还可以通过将`spring.cloud.httpclientfactories.apache.enabled`或`spring.cloud.httpclientfactories.ok.enabled`设置为`false`来禁用这些 bean 的创建。 ### [](#enabled-features)[2.10.已启用的功能](#enabled-features) Spring Cloud Commons 提供了`/features`执行器端点。这个端点返回 Classpath 上可用的功能以及它们是否被启用。返回的信息包括功能类型、名称、版本和供应商。 #### [](#feature-types)[2.10.1.特征类型](#feature-types) 有两种类型的“特征”:抽象的和命名的。 抽象特性是定义了接口或抽象类并创建了实现的特性,例如`DiscoveryClient`、`LoadBalancerClient`或`LockService`。抽象类或接口用于在上下文中查找该类型的 Bean。显示的版本是`bean.getClass().getPackage().getImplementationVersion()`。 命名特性是指不具有它们实现的特定类的特性。这些功能包括“断路器”、“API 网关”、“ Spring 云总线”等。这些特征需要一个名称和 Bean 类型。 #### [](#declaring-features)[2.10.2.声明功能](#declaring-features) 任何模块都可以声明任意数量的`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-compatibility-verification)[2.11. Spring Cloud Compatibility Verification](#spring-cloud-compatibility-verification) 由于一些用户在设置 Spring 云应用程序时存在问题,我们决定添加一个兼容性验证机制。如果你当前的设置与 Spring 云需求不兼容,那么它将会中断,同时还会出现一份报告,显示出到底出了什么问题。 目前,我们正在验证将哪个版本的 Spring 启动添加到你的 Classpath 中。 一份报告的例子 ``` *************************** 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 启动版本列表设置“ Spring.cloud.compatibility-verifier.compatible-boot-versions”属性。 ## [](#spring-cloud-loadbalancer)[3. Spring Cloud LoadBalancer](#spring-cloud-loadbalancer) Spring 云提供了其自己的客户端负载均衡器的抽象和实现。对于负载平衡机制,增加了`ReactiveLoadBalancer`接口,并为其提供了**基于循环**和**随机**实现方式。为了得到要从反应中选择的实例`ServiceInstanceListSupplier`是使用的。目前,我们支持基于服务发现的`ServiceInstanceListSupplier`实现,该实现使用 Classpath 中可用的[发现客户端](#discovery-client)从服务发现中检索可用实例。 | |通过将`spring.cloud.loadbalancer.enabled`的值设置为`false`,可以禁用 Spring Cloud LoadBalancer。| |---|---------------------------------------------------------------------------------------------------------------------------| ### [](#switching-between-the-load-balancing-algorithms)[3.1.在负载平衡算法之间切换](#switching-between-the-load-balancing-algorithms) 默认情况下使用的`ReactiveLoadBalancer`实现是`RoundRobinLoadBalancer`。要切换到不同的实现,对于选定的服务或所有服务,可以使用[自定义负载平衡器配置机制](#custom-loadbalancer-configuration)。 例如,可以通过`@LoadBalancerClient`注释传递以下配置,以切换到使用`RandomLoadBalancer`: ``` public class CustomLoadBalancerConfiguration { @Bean ReactorLoadBalancer randomLoadBalancer(Environment environment, LoadBalancerClientFactory loadBalancerClientFactory) { String name = environment.getProperty(LoadBalancerClientFactory.PROPERTY_NAME); return new RandomLoadBalancer(loadBalancerClientFactory .getLazyProvider(name, ServiceInstanceListSupplier.class), name); } } ``` | |作为`@LoadBalancerClient`或`@LoadBalancerClients`配置参数传递的类不应使用`@Configuration`进行注释,也不应在组件扫描范围之外。| |---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#spring-cloud-loadbalancer-integrations)[3.2. Spring Cloud LoadBalancer integrations](#spring-cloud-loadbalancer-integrations) Spring 为了使云负载平衡器易于使用,我们提供了`ReactorLoadBalancerExchangeFilterFunction`可以与`WebClient`和`BlockingLoadBalancerClient`一起使用的`RestTemplate`。你可以在以下部分中看到更多信息和使用示例: * [Spring RestTemplate as a Load Balancer Client](#rest-template-loadbalancer-client) * [Spring WebClient as a Load Balancer Client](#webclinet-loadbalancer-client) * [Spring WebFlux WebClient with `ReactorLoadBalancerExchangeFilterFunction`](#webflux-with-reactive-loadbalancer) ### [](#loadbalancer-caching)[3.3. Spring Cloud LoadBalancer Caching](#loadbalancer-caching) 除了基本的`ServiceInstanceListSupplier`实现外,我们还提供了两种缓存实现,这种实现在每次必须选择实例时都通过`DiscoveryClient`检索实例。 #### [](#caffeine-backed-loadbalancer-cache-implementation)[3.3.1. ](#caffeine-backed-loadbalancer-cache-implementation)[Caffeine](https://github.com/ben-manes/caffeine)-支持负载平衡器缓存实现 如果在 Classpath 中有,则将使用基于咖啡因的实现方式。有关如何配置它的信息,请参见[负荷平衡状态](#loadbalancer-cache-configuration)部分。 如果你正在使用咖啡因,还可以通过在`spring.cloud.loadbalancer.cache.caffeine.spec`属性中传递你自己的[咖啡因规格](https://static.javadoc.io/com.github.ben-manes.caffeine/caffeine/2.2.2/com/github/benmanes/caffeine/cache/CaffeineSpec.html)来覆盖负载平衡器的默认咖啡因缓存设置。 警告:传递你自己的咖啡因规范将覆盖任何其他 loadBalancerCache 设置,包括[通用负载平衡器缓存配置](#loadbalancer-cache-configuration)字段,例如`ttl`和`capacity`。 #### [](#default-loadbalancer-cache-implementation)[3.3.2.默认的 LoadBalancer 缓存实现](#default-loadbalancer-cache-implementation) 如果在 Classpath 中没有咖啡因,则将使用`DefaultLoadBalancerCache`,它自动带有`spring-cloud-starter-loadbalancer`。有关如何配置它的信息,请参见[负荷平衡状态](#loadbalancer-cache-configuration)部分。 | |要使用咖啡因而不是默认的缓存,请在 Classpath 中添加`com.github.ben-manes.caffeine:caffeine`依赖项。| |---|-----------------------------------------------------------------------------------------------------------------------| #### [](#loadbalancer-cache-configuration)[3.3.3.LoadBalancer 缓存配置](#loadbalancer-cache-configuration) 你可以将你自己的`ttl`值(写完之后条目应该过期的时间)表示为`Duration`,方法是传递一个与`String`兼容的[Spring Boot `String` to `Duration` converter syntax](https://docs.spring.io/spring-boot/docs/current/reference/html/spring-boot-features.html#boot-features-external-config-conversion-duration).作为`spring.cloud.loadbalancer.cache.ttl`属性的值。还可以通过设置`spring.cloud.loadbalancer.cache.capacity`属性的值来设置自己的 LoadBalancer 缓存初始容量。 默认设置包括将`ttl`设置为 35 秒,而默认的`initialCapacity`是`256`。 通过将`spring.cloud.loadbalancer.cache.enabled`的值设置为`false`,你也可以完全禁用 LoadBalancer 缓存。 | |尽管基本的、非缓存的实现对于原型设计和测试很有用,但它的效率比缓存版本低得多,因此我们建议在生产中始终使用缓存版本。如果缓存已经由`DiscoveryClient`实现完成,例如`EurekaDiscoveryClient`,则应禁用负载平衡器缓存以防止双重缓存。| |---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#zone-based-load-balancing)[3.4.基于区域的负载平衡](#zone-based-load-balancing) 为了启用基于区域的负载平衡,我们提供了`ZonePreferenceServiceInstanceListSupplier`。我们使用`DiscoveryClient`-特定的`zone`配置(例如,`eureka.instance.metadata-map.zone`)来选择客户机试图为其筛选可用服务实例的区域。 | |你还可以通过设置`spring.cloud.loadbalancer.zone`属性的值来覆盖`DiscoveryClient`特定的区域设置。| |---|------------------------------------------------------------------------------------------------------------------------------| | |目前,只有 Eureka 发现客户机被检测来设置 loadBalancer 区域。对于其他发现客户端,设置`spring.cloud.loadbalancer.zone`属性。不久还会有更多的测试。| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |为了确定检索到的`ServiceInstance`的区域,我们在其元数据映射中检查`"zone"`键下的值。| |---|----------------------------------------------------------------------------------------------------------------------| `ZonePreferenceServiceInstanceListSupplier`过滤检索到的实例,只返回同一区域内的实例。如果区域是`null`,或者同一区域内没有实例,则返回所有检索到的实例。 为了使用基于区域的负载平衡方法,你必须在[自定义配置](#custom-loadbalancer-configuration)中实例化`ZonePreferenceServiceInstanceListSupplier` Bean。 我们使用委托来处理`ServiceInstanceListSupplier`bean。我们建议在`ZonePreferenceServiceInstanceListSupplier`的构造函数中传递一个`DiscoveryClientServiceInstanceListSupplier`委托,然后用`CachingServiceInstanceListSupplier`包装后者,以利用[负载平衡器缓存机制](#loadbalancer-caching)。 你可以使用这个示例配置来设置它: ``` public class CustomLoadBalancerConfiguration { @Bean public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder() .withDiscoveryClient() .withZonePreference() .withCaching() .build(context); } } ``` ### [](#instance-health-check-for-loadbalancer)[3.5.LoadBalancer 的实例健康检查](#instance-health-check-for-loadbalancer) 可以为 loadBalancer 启用计划的 HealthCheck。为此提供了`HealthCheckServiceInstanceListSupplier`。它会定期验证委托“ServiceInstanceListSupplier”提供的实例是否还活着,并且只返回健康的实例,除非没有-然后返回所有检索到的实例。 | |这种机制在使用`SimpleDiscoveryClient`时特别有用。对于由实际服务注册中心支持的
客户机,没有必要使用它,因为在查询外部服务发现之后,我们已经获得了
健康的实例。| |---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |对于每个服务
具有少量实例的设置,也建议使用此供应商,以避免在失败的实例上重试调用。| |---|-----------------------------------------------------------------------------------------------------------------------------------------------------| | |如果使用任何服务发现支持的供应商,通常不需要添加此健康检查机制,因为我们直接从服务注册中心检索实例的健康状态
。| |---|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |`HealthCheckServiceInstanceListSupplier`依赖于由委托通量提供的更新实例。在极少数情况下,当你希望使用不刷新实例的委托时,即使实例列表可能会更改(例如我们提供的`DiscoveryClientServiceInstanceListSupplier`),你可以将`spring.cloud.loadbalancer.health-check.refetch-instances`设置为`true`,以便通过`HealthCheckServiceInstanceListSupplier`刷新实例列表。然后,你还可以通过修改`spring.cloud.loadbalancer.health-check.refetch-instances-interval`的值来调整刷新间隔,并 OPT 通过将`spring.cloud.loadbalancer.health-check.repeat-health-check`设置为`false`来禁用额外的 HealthCheck 重复,因为每个实例 refetch
也将触发 HealthCheck。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| `HealthCheckServiceInstanceListSupplier`使用带有 ` Spring.cloud.loadBalancer.health-check` 前缀的属性。你可以为调度程序设置`initialDelay`和`interval`。你可以通过设置`spring.cloud.loadbalancer.health-check.path.default`属性的值来设置 HealthCheck URL 的默认路径。还可以通过设置`spring.cloud.loadbalancer.health-check.path.[SERVICE_ID]`属性的值,用服务的正确 ID 替换`[SERVICE_ID]`,为任何给定的服务设置特定值。如果没有指定`[SERVICE_ID]`,则默认使用`/actuator/health`。如果`[SERVICE_ID]`被设置为`null`或作为一个值为空,那么将不执行健康检查。还可以通过设置`spring.cloud.loadbalancer.health-check.port`的值来为健康检查请求设置自定义端口。如果没有设置,则请求的服务在服务实例中可用的端口。 | |如果你依赖默认路径,请确保将`spring-boot-starter-actuator`添加到合作者的依赖项中,除非你打算自己添加这样的端点。| |---|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| 为了使用健康检查计划程序方法,你必须在[自定义配置](#custom-loadbalancer-configuration)中实例化`HealthCheckServiceInstanceListSupplier` Bean。 我们使用委托来处理`ServiceInstanceListSupplier`bean。我们建议在`HealthCheckServiceInstanceListSupplier`的构造函数中传递一个`DiscoveryClientServiceInstanceListSupplier`委托。 你可以使用这个示例配置来设置它: ``` public class CustomLoadBalancerConfiguration { @Bean public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder() .withDiscoveryClient() .withHealthChecks() .build(context); } } ``` | |对于非反应性堆栈,使用`withBlockingHealthChecks()`创建此供应商。
你还可以传递你自己的`WebClient`或`RestTemplate`实例用于检查。| |---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |`HealthCheckServiceInstanceListSupplier`有自己的基于反应器流量`replay()`的缓存机制。因此,如果正在使用它,你可能希望跳过用`CachingServiceInstanceListSupplier`包装该供应商。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#same-instance-preference-for-loadbalancer)[3.6.LoadBalancer 的相同实例首选项](#same-instance-preference-for-loadbalancer) 你可以以这样一种方式设置 loadBalancer,即它更喜欢先前选择的实例(如果该实例可用的话)。 为此,你需要使用`SameInstancePreferenceServiceInstanceListSupplier`。你可以通过将`spring.cloud.loadbalancer.configurations`的值设置为`same-instance-preference`,或者通过提供你自己的`ServiceInstanceListSupplier` Bean 来配置它——例如: ``` public class CustomLoadBalancerConfiguration { @Bean public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder() .withDiscoveryClient() .withSameInstancePreference() .build(context); } } ``` | |这也是 ZooKeeper`StickyRule`的替代品。| |---|------------------------------------------------------| ### [](#request-based-sticky-session-for-loadbalancer)[3.7.LoadBalancer 中基于请求的粘性会话](#request-based-sticky-session-for-loadbalancer) 你可以以这样一种方式设置 loadBalancer,即它更喜欢请求 cookie 中提供的带有`instanceId`的实例。如果请求是通过`ClientRequestContext`或`ServerHttpRequestContext`传递给负载平衡器的,我们目前支持这一点,这是 SC 负载平衡器交换过滤器函数和过滤器所使用的。 为此,你需要使用`RequestBasedStickySessionServiceInstanceListSupplier`。你可以通过将`spring.cloud.loadbalancer.configurations`的值设置为`request-based-sticky-session`,或者通过提供你自己的`ServiceInstanceListSupplier` Bean 来配置它——例如: ``` public class CustomLoadBalancerConfiguration { @Bean public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder() .withDiscoveryClient() .withRequestBasedStickySession() .build(context); } } ``` 对于该功能,在向前发送请求之前,有必要更新所选的服务实例(如果该实例不可用,它可能与原始请求 cookie 中的服务实例不同)。为此,将`spring.cloud.loadbalancer.sticky-session.add-service-instance-cookie`的值设置为`true`。 默认情况下,cookie 的名称是`sc-lb-instance-id`。你可以通过更改`spring.cloud.loadbalancer.instance-id-cookie-name`属性的值来修改它。 | |此功能目前支持 WebClient 支持的负载平衡。| |---|------------------------------------------------------------------------| ### [](#spring-cloud-loadbalancer-hints)[3.8. Spring Cloud LoadBalancer Hints](#spring-cloud-loadbalancer-hints) Spring Cloud LoadBalancer 允许你设置在`Request`对象内传递给 LoadBalancer 的`String`提示,这些提示以后可以在`ReactiveLoadBalancer`实现中使用,这些实现可以处理它们。 通过设置`spring.cloud.loadbalancer.hint.default`属性的值,可以为所有服务设置默认提示。还可以通过设置`spring.cloud.loadbalancer.hint.[SERVICE_ID]`属性的值,用服务的正确 ID 替换`[SERVICE_ID]`,为任何给定的服务设置特定值。如果提示不是由用户设置的,则使用`default`。 ### [](#hints-based-loadbalancing)[3.9.基于提示的负载平衡](#hints-based-loadbalancing) 我们还提供了`HintBasedServiceInstanceListSupplier`,这是用于基于提示的实例选择的`ServiceInstanceListSupplier`实现。 `HintBasedServiceInstanceListSupplier`检查提示请求标头(默认标头名称是`X-SC-LB-Hint`,但你可以通过更改`spring.cloud.loadbalancer.hint-header-name`属性的值来修改它),如果它发现了提示请求标头,则使用标头中传递的提示值来过滤服务实例。 如果没有添加任何提示头,`HintBasedServiceInstanceListSupplier`将使用[来自属性的提示值](#spring-cloud-loadbalancer-hints)来过滤服务实例。 如果没有设置任何提示,则返回委托提供的所有服务实例,不管是通过头还是通过属性。 在筛选过程中,`HintBasedServiceInstanceListSupplier`查找在`hint`键下具有匹配值集的服务实例。如果没有找到匹配的实例,则返回委托提供的所有实例。 你可以使用以下示例配置来设置它: ``` public class CustomLoadBalancerConfiguration { @Bean public ServiceInstanceListSupplier discoveryClientServiceInstanceListSupplier( ConfigurableApplicationContext context) { return ServiceInstanceListSupplier.builder() .withDiscoveryClient() .withHints() .withCaching() .build(context); } } ``` ### [](#transform-the-load-balanced-http-request)[3.10.转换负载平衡的 HTTP 请求](#transform-the-load-balanced-http-request) 你可以使用所选的`ServiceInstance`来转换负载平衡的 HTTP 请求。 对于`RestTemplate`,你需要实现和定义`LoadBalancerRequestTransformer`如下: ``` @Bean public LoadBalancerRequestTransformer transformer() { return new LoadBalancerRequestTransformer() { @Override public HttpRequest transformRequest(HttpRequest request, ServiceInstance instance) { return new HttpRequestWrapper(request) { @Override public HttpHeaders getHeaders() { HttpHeaders headers = new HttpHeaders(); headers.putAll(super.getHeaders()); headers.add("X-InstanceId", instance.getInstanceId()); return headers; } }; } }; } ``` 对于`WebClient`,你需要实现和定义`LoadBalancerClientRequestTransformer`如下: ``` @Bean public LoadBalancerClientRequestTransformer transformer() { return new LoadBalancerClientRequestTransformer() { @Override public ClientRequest transformRequest(ClientRequest request, ServiceInstance instance) { return ClientRequest.from(request) .header("X-InstanceId", instance.getInstanceId()) .build(); } }; } ``` 如果定义了多个转换器,那么它们将按照定义 bean 的顺序应用。或者,你可以使用`LoadBalancerRequestTransformer.DEFAULT_ORDER`或`LoadBalancerClientRequestTransformer.DEFAULT_ORDER`来指定顺序。 ### [](#spring-cloud-loadbalancer-starter)[3.11. Spring Cloud LoadBalancer Starter](#spring-cloud-loadbalancer-starter) 我们还提供了一个启动器,允许你在 Spring 启动应用程序中轻松添加 Spring Cloud LoadBalancer。为了使用它,只需在构建文件中的 Spring 云依赖项中添加`org.springframework.cloud:spring-cloud-starter-loadbalancer`。 | |Spring 云负载平衡器启动器包括[Spring Boot Caching](https://docs.spring.io/spring-boot/docs/current/reference/html/boot-features-caching.html)和[Evictor](https://github.com/stoyanr/Evictor)。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#custom-loadbalancer-configuration)[3.12. Passing Your Own Spring Cloud LoadBalancer Configuration](#custom-loadbalancer-configuration) 你还可以使用`@LoadBalancerClient`注释来传递你自己的负载平衡器客户端配置,传递负载平衡器客户端和配置类的名称,如下所示: ``` @Configuration @LoadBalancerClient(value = "stores", configuration = CustomLoadBalancerConfiguration.class) public class MyConfiguration { @Bean @LoadBalanced public WebClient.Builder loadBalancedWebClientBuilder() { return WebClient.builder(); } } ``` TIP 为了使在自己的 loadBalancer 配置上的工作更容易,我们在`ServiceInstanceListSupplier`类中添加了一个`builder()`方法。 TIP 你还可以将`spring.cloud.loadbalancer.configurations`属性的值设置为`zone-preference`,从而在缓存中使用`ZonePreferenceServiceInstanceListSupplier`,或者在缓存中使用`health-check`,从而替代默认的预定义配置。 你可以使用此功能实例化`ServiceInstanceListSupplier`或`ReactorLoadBalancer`的不同实现,这些实现可以是你编写的,也可以是我们作为替代方案提供的(例如`ZonePreferenceServiceInstanceListSupplier`),以覆盖默认设置。 你可以看到一个自定义配置[here](#zoned-based-custom-loadbalancer-configuration)的示例。 | |注释`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(); } } ``` | |作为`@LoadBalancerClient`或`@LoadBalancerClients`配置参数传递的类不应使用`@Configuration`进行注释,也不应在组件扫描范围之外。| |---|----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#loadbalancer-lifecycle)[3.13. Spring Cloud LoadBalancer Lifecycle](#loadbalancer-lifecycle) Bean 中使用[自定义负载平衡器配置](#custom-loadbalancer-configuration)进行注册可能是有用的一种类型是`LoadBalancerLifecycle`。 `LoadBalancerLifecycle`bean 提供了名为`onStart(Request request)`、`onStartRequest(Request request, Response lbResponse)`和`onComplete(CompletionContext completionContext)`的回调方法,你应该实现这些方法来指定在负载平衡之前和之后应该进行哪些操作。 `onStart(Request request)`将`Request`对象作为参数。它包含用于选择适当实例的数据,包括下游客户机请求和[hint](#spring-cloud-loadbalancer-hints)。`onStartRequest`也接受`Request`对象,另外,`Response`对象作为参数。另一方面,将`CompletionContext`对象提供给`onComplete(CompletionContext completionContext)`方法。它包含 loadBalancer`Response`,包括所选择的服务实例、针对该服务实例执行的请求的`Status`和(如果可用的话)返回到下游客户端的响应,以及(如果发生了异常)相应的`Throwable`。 `supports(Class requestContextClass, Class responseClass, Class serverTypeClass)`方法可用于确定所讨论的处理器是否处理所提供类型的对象。如果未被用户重写,则返回`true`。 | |在前面的方法调用中,`RC`表示`RequestContext`类型,`RES`表示客户端响应类型,`T`表示返回的服务器类型。| |---|--------------------------------------------------------------------------------------------------------------------------------------| ### [](#loadbalancer-micrometer-stats-lifecycle)[3.14. Spring Cloud LoadBalancer Statistics](#loadbalancer-micrometer-stats-lifecycle) 我们提供了一个名为`LoadBalancerLifecycle` Bean 的`MicrometerStatsLoadBalancerLifecycle`,它使用 Micrometer 为负载平衡调用提供统计信息。 为了将此 Bean 添加到你的应用程序上下文中,将`spring.cloud.loadbalancer.stats.micrometer.enabled`的值设置为`true`,并使`MeterRegistry`可用(例如,将[Spring Boot Actuator](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html)添加到你的项目中)。 `MicrometerStatsLoadBalancerLifecycle`在`MeterRegistry`中记录以下仪表: * `loadbalancer.requests.active`:允许你监视任何服务实例当前活动请求的数量(通过标记可获得的服务实例数据)的度量标准; * `loadbalancer.requests.success`:一个计时器,用于度量以将响应传递给基础客户机而结束的任何负载平衡请求的执行时间; * `loadbalancer.requests.failed`:一个计时器,用于度量任何负载平衡请求的执行时间,这些请求以异常结束; * `loadbalancer.requests.discard`:一种计数器,用于测量被丢弃的负载平衡请求的数量,即负载平衡器尚未检索到要在其上运行请求的服务实例的请求。 有关服务实例、请求数据和响应数据的附加信息将随时通过标记添加到度量中。 | |对于某些实现方式,例如`BlockingLoadBalancerClient`,请求和响应数据可能不可用,因为我们从参数建立泛型类型,并且可能无法确定类型和读取数据。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| | |当一个给定的计价器至少增加了一条记录时,计价器在注册表中进行注册。| |---|----------------------------------------------------------------------------------------------| | |你可以通过[adding `MeterFilters`](https://docs.spring.io/spring-boot/docs/current/reference/html/production-ready-features.html#production-ready-metrics-per-meter-properties)进一步配置这些指标的行为(例如,添加[发布百分位和直方图](https://micrometer.io/docs/concepts#_histograms_and_percentiles))。| |---|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ### [](#configuring-individual-loadbalancerclients)[3.15.配置单个 loadbalancerclient](#configuring-individual-loadbalancerclients) 单独的 loadBalancer 客户机可以单独配置不同的前缀`spring.cloud.loadbalancer.clients..`**where `clientId` is the name of the loadbalancer. Default configuration values may be set in the `spring.cloud.loadbalancer.`**名称空间,并将与客户机特定值优先合并。 示例 5.application.yml ``` spring: cloud: loadbalancer: health-check: initial-delay: 1s clients: myclient: health-check: interval: 30s ``` 上面的示例将产生一个合并的健康检查`@ConfigurationProperties`对象,其中`initial-delay=1s`和`interval=30s`。 除了以下全局属性外,每个客户机配置属性对大多数属性都有效: * `spring.cloud.loadbalancer.enabled`-全局启用或禁用负载平衡 * `spring.cloud.loadbalancer.retry.enabled`-全局启用或禁用负载平衡重试。如果全局启用它,仍然可以使用`client`-前缀属性禁用特定客户机的重试,但不能使用相反的方法。 * `spring.cloud.loadbalancer.cache.enabled`-全局启用或禁用 LoadBalancer 缓存。如果你全局启用它,你仍然可以通过创建[自定义配置](#custom-loadbalancer-configuration)来禁用特定客户机的缓存,该命令不包括`CachingServiceInstanceListSupplier`在`ServiceInstanceListSupplier`委托层次结构中的`CachingServiceInstanceListSupplier`,但不是相反。 * `spring.cloud.loadbalancer.stats.micrometer.enabled`-全局启用或禁用负载平衡器千分尺指标 | |对于已经使用的映射的属性,可以在不使用`clients`关键字(例如,`hints`,`health-check.path`)的情况下为每个客户机指定不同的值,我们保留了这种行为,以使库向后兼容。它将在下一个主要版本中进行修改。| |---|---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| ## [](#spring-cloud-circuit-breaker)[4. Spring Cloud Circuit Breaker](#spring-cloud-circuit-breaker) ### [](#introduction)[4.1.导言](#introduction) Spring 云断路器提供了跨越不同断路器实现方式的抽象。它提供了在应用程序中使用的一致的 API,允许你(开发人员)选择最适合你的应用程序需求的断路器实现。 #### [](#supported-implementations)[4.1.1.支持的实现](#supported-implementations) Spring 云支持以下断路器实现方式: * [Resilience4J](https://github.com/resilience4j/resilience4j) * [Sentinel](https://github.com/alibaba/Sentinel) * [Spring Retry](https://github.com/spring-projects/spring-retry) ### [](#core-concepts)[4.2.核心概念](#core-concepts) 要在代码中创建断路器,可以使用`CircuitBreakerFactory`API。当你在 Classpath 上包含 Spring 云断路器启动器时,将自动为你创建实现此 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`,从而触发回退。如果你不想提供备份,则可以选择排除备份。 #### [](#circuit-breakers-in-reactive-code)[4.2.1.无功码中的断路器](#circuit-breakers-in-reactive-code) 如果 Project Reactor 在类路径上,你也可以使用`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`,将调用该回退。 ### [](#configuration)[4.3.配置](#configuration) 你可以通过创建类型`Customizer`的 bean 来配置断路器。`Customizer`接口有一个用于自定义`Object`的方法(称为`customize`)。 有关如何定制给定实现的详细信息,请参见以下文档: * [Resilience4J](../../../../spring-cloud-circuitbreaker/current/reference/html/spring-cloud-circuitbreaker.html#configuring-resilience4j-circuit-breakers) * [Sentinel](https://github.com/alibaba/spring-cloud-alibaba/blob/master/spring-cloud-alibaba-docs/src/main/asciidoc/circuitbreaker-sentinel.adoc#circuit-breaker-spring-cloud-circuit-breaker-with-sentinel—​configuring-sentinel-circuit-breakers) * [Spring Retry](../../../../../spring-cloud-circuitbreaker/docs/current/reference/html/spring-cloud-circuitbreaker.html#configuring-spring-retry-circuit-breakers) 一些`CircuitBreaker`实现方式如`Resilience4JCircuitBreaker`每次调用`customize`方法都调用`CircuitBreaker#run`。这可能是低效的。在这种情况下,可以使用`CircuitBreaker#once`方法。在多次调用`customize`没有意义的情况下,例如在[消费弹性 4J 的事件](https://resilience4j.readme.io/docs/circuitbreaker#section-consume-emitted-circuitbreakerevents)的情况下,它是有用的。 下面的示例显示了每个`io.github.resilience4j.circuitbreaker.CircuitBreaker`消耗事件的方式。 ``` Customizer.once(circuitBreaker -> { circuitBreaker.getEventPublisher() .onStateTransition(event -> log.info("{}: {}", event.getCircuitBreakerName(), event.getStateTransition())); }, CircuitBreaker::getName) ``` ## [](#cachedrandompropertysource)[5.cachedrandomPropertySource](#cachedrandompropertysource) Spring 云上下文提供了一个`PropertySource`,该`PropertySource`基于键缓存随机值。在缓存功能之外,它的工作原理与 Spring boot 的[“随机价值 PropertySource”](https://github.com/spring-projects/spring-boot/blob/main/spring-boot-project/spring-boot/src/main/java/org/springframework/boot/env/RandomValuePropertySource.java)相同。如果你想要一个即使在 Spring 应用程序上下文重新启动之后仍然保持一致的随机值,那么这个随机值可能是有用的。属性值采取`cachedrandom.[yourkey].[type]`的形式,其中`yourkey`是缓存中的键。`type`值可以是 Spring boot 的`RandomValuePropertySource`所支持的任何类型。 ``` myrandom=${cachedrandom.appname.value} ``` ## [](#spring-cloud-security)[6. Security](#spring-cloud-security) ### [](#spring-cloud-security-single-sign-on)[6.1.单点登录](#spring-cloud-security-single-sign-on) | |在版本 1.3 中,所有的 OAuth2SSO 和资源服务器功能都移动到了 Spring boot
。你可以在[Spring Boot user guide](https://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/)中找到文档。| |---|-----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------| #### [](#spring-cloud-security-client-token-relay)[6.1.1.客户端令牌中继](#spring-cloud-security-client-token-relay) 如果你的应用程序是一个面向 OAuth2 客户端的用户(即声明了 `@enableOAuth2SSO` 或`@EnableOAuth2Client`),那么它在 Spring 启动时的请求范围内有一个 `OAuth2ClientContext’。你可以从这个上下文创建自己的`OAuth2RestTemplate`和一个自动连线`OAuth2ProtectedResourceDetails`,然后上下文将始终向下游转发访问令牌,如果过期,还将自动刷新访问令牌。(这些是 Spring 安全性和 Spring 引导的功能。 #### [](#spring-cloud-security-resource-server-token-relay)[6.1.2.资源服务器令牌中继](#spring-cloud-security-resource-server-token-relay) 如果你的应用程序有`@EnableResourceServer`,那么你可能希望将传入的令牌向下游中继到其他服务。如果你使用“RESTTemplate”来联系下游服务,那么这只是一个如何在正确的上下文中创建模板的问题。 如果你的服务使用`UserInfoTokenServices`来验证传入的令牌(即它正在使用`security.oauth2.user-info-uri`配置),那么你可以简单地使用自动连线`OAuth2RestTemplate`创建`OAuth2RestTemplate`(它将在到达后端代码之前由身份验证过程填充)。相当于(使用 Spring Boot1.4),你可以在配置中注入一个“userinforesttemplateFactory”并获取它的。例如: MyConfiguration.java ``` @Bean public OAuth2RestTemplate restTemplate(UserInfoRestTemplateFactory factory) { return factory.getUserInfoRestTemplate(); } ``` 然后,这个 REST 模板将具有与身份验证筛选器使用的相同的`OAuth2ClientContext`(请求作用域),因此你可以使用它发送具有相同访问令牌的请求。 如果你的应用程序没有使用`UserInfoTokenServices`,但仍然是一个客户端(即它声明`@EnableOAuth2Client`或`@EnableOAuth2Sso`),那么使用 Spring 安全云,用户从`@Autowired``OAuth2Context`创建的任何`OAuth2RestOperations`也将转发令牌。默认情况下,此功能是作为 MVC 处理程序拦截器实现的,因此它仅在 Spring MVC 中工作。如果你不使用 MVC,你可以使用自定义过滤器或 AOP 拦截器包装“AccessTokenContextRelay”来提供相同的功能。 下面是一个基本示例,展示了在其他地方创建的自动连线 REST 模板的使用情况(“foo.com”是一个资源服务器,接受与周围应用程序相同的令牌): mycontroller.java ``` @Autowired private OAuth2RestOperations restTemplate; @RequestMapping("/relay") public String relay() { ResponseEntity response = restTemplate.getForEntity("https://foo.com/bar", String.class); return "Success! (" + response.getBody() + ")"; } ``` 如果你不想转发令牌(这是一个有效的选择,因为你可能希望充当你自己的角色,而不是向你发送令牌的客户机),那么你只需要创建自己的“OAuth2Context”,而不是自动布线默认的。 如果可用,Feign 客户机还将获取一个使用“OAuth2ClientContext”的拦截器,因此他们还应该在`RestTemplate`可以进行令牌中继的任何地方进行令牌中继。 ## [](#configuration-properties)[7.配置属性](#configuration-properties) 要查看所有 Spring Cloud Commons 相关配置属性的列表,请检查[附录页](appendix.html)。