找回密码
 立即注册
查看: 170|回复: 0

Spring Cloud 中文文档

[复制链接]
发表于 2023-2-17 21:46 | 显示全部楼层 |阅读模式
Spring Cloud 官方文档


Spring Cloud为开发人员提供了用于快速构建分布式系统中某些常见模式的工具(例如,配置管理,服务发现,断路器,智能路由,微代理,控制总线)。分布式系统的协调产生了样板模式,并且使用Spring云开发人员可以快速支持实现这些模式的服务和应用程序。它们可以在任何分布式环境中正常工作,包括开发人员自己的笔记本电脑,裸机数据中心和受管理的平台,例如Cloud Foundry。
版本:Greenwich.SR5

1.特点

Spring Cloud专注于为典型的用例和扩展机制提供良好的开箱即用体验,以涵盖其他情况。
    分布式/版本化配置服务注册和发现路由服务到服务的呼叫负载均衡断路器分布式消息传递

第一部分:云原生应用程序

Cloud Native是一种应用程序开发风格,可鼓励在持续交付和价值驱动型开发领域轻松采用最佳实践。一个相关的学科是构建12要素应用程序,其中开发实践与交付和运营目标保持一致,例如,通过使用声明性编程,管理和监视。Spring Cloud通过多种特定方式促进了这些发展方式。起点是一组功能,分布式系统中的所有组件都需要轻松访问这些功能。
其中Spring Cloud建立在Spring Boot上,涵盖了许多这些功能。Spring Cloud作为两个库提供了更多功能:Spring Cloud上下文和Spring Cloud Commons。Spring Cloud上下文为Spring Cloud应用程序的ApplicationContext提供了实用程序和特殊服务(引导上下文,加密,刷新作用域和环境端点)。Spring Cloud Commons是在不同的Spring Cloud实现中使用的一组抽象和通用类(例如Spring Cloud Netflix和Spring Cloud Consul)。
如果由于“密钥大小非法”而导致异常,并且使用Sun的JDK,则需要安装Java密码术扩展(JCE)无限强度管辖权策略文件。有关更多信息,请参见以下链接:
    Java 6 JCEJava 7 JCEJava 8 JCE
将文件解压缩到您使用的JRE / JDK x64 / x86版本的JDK / jre / lib / security文件夹中。


Spring Cloud是根据非限制性Apache 2.0许可证发行的。如果您想为文档的这一部分做出贡献或发现错误,可以在github上找到源代码和项目跟踪工具。
2. Spring Cloud上下文:应用程序上下文服务

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Boot对于如何使用Spring来构建应用程序有自己的看法。例如,它具有用于公共配置文件的常规位置,并具有用于公共管理和监视任务的端点。Spring Cloud以此为基础,并添加了一些功能,可能系统中的所有组件都将使用或偶尔需要这些功能。
2.1 Bootstrap应用程序上下文

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud应用程序通过创建“ bootstrap ”上下文来运行,该上下文是主应用程序的父上下文。它负责从外部源加载配置属性,并负责解密本地外部配置文件中的属性。这两个上下文共享一个Environment,它是任何Spring应用程序的外部属性的来源。默认情况下,引导程序属性(不是bootstrap.properties,而是引导程序阶段加载的属性)具有较高的优先级,因此它们不能被本地配置覆盖。
引导上下文使用与主应用程序上下文不同的约定来定位外部配置。可以使用bootstrap.yml来代替application.yml(或.properties),而将引导程序和外部环境的外部配置很好地分开。以下清单显示了一个示例:
bootstrap.yml。
  1. spring:
  2.   application:
  3.     name: foo
  4.   cloud:
  5.     config:
  6.       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来完全禁用引导过程(例如,在系统属性中)。
2.2应用程序上下文层次结构

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果从SpringApplication或SpringApplicationBuilder构建应用程序上下文,那么Bootstrap上下文将作为父级添加到该上下文。Spring的一个功能是子上下文从其父级继承属性源和配置文件,因此与构建没有Spring Cloud Config的相同上下文相比,“ 主 ”应用程序上下文包含其他属性源。其他属性来源是:
    “ bootstrap ”:如果在Bootstrap上下文中找到任何PropertySourceLocators并且具有非空属性,则会以高优先级显示可选的CompositePropertySource。一个示例是Spring Cloud Config服务器中的属性。有关如何自定义此属性源内容的说明,请参见“ 第2.6节“自定义Bootstrap Property源” ”。“ applicationConfig:[classpath:bootstrap.yml] ”(以及相关文件,如果Spring配置文件处于活动状态):如果您拥有bootstrap.yml(或.properties),则这些属性用于配置Bootstrap上下文。然后,当它们的父级被设置时,它们被添加到子级上下文。它们的优先级低于application.yml(或.properties)以及创建Spring Boot应用程序过程中正常添加到子级的任何其他属性源的优先级。有关如何自定义这些属性源内容的说明,请参见“ 第2.3节“更改引导程序Properties”的位置 ”。
由于属性源的排序规则,“ bootstrap ”条目优先。但是,请注意,这些不包含来自bootstrap.yml的任何数据,该数据的优先级非常低,但可用于设置默认值。
您可以通过设置创建的任何ApplicationContext的父上下文来扩展上下文层次结构,例如,使用其自己的界面或使用SpringApplicationBuilder便捷方法(parent(),child()和sibling())。引导上下文是您自己创建的最高级祖先的父级。层次结构中的每个上下文都有其自己的“ bootstrap ”(可能为空)属性源,以避免无意间将价值从父辈提升到子孙后代。如果有配置服务器,则层次结构中的每个上下文原则上也可以具有不同的spring.application.name,因此也具有不同的远程属性源。正常的Spring应用程序上下文行为规则适用于属性解析:子上下文的属性按名称以及属性源名称覆盖父级属性。(如果子项具有与父项同名的属性源,则子项中不包括来自父项的值)。
请注意,SpringApplicationBuilder可让您在整个层次结构中共享Environment,但这不是默认设置。因此,同级上下文尤其不需要具有相同的配置文件或属性源,即使它们可能与其父级共享相同的值。
2.3更改引导程序Properties的位置

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
可以通过设置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。实际上,它们是通过在引导程序Environment中设置这些属性来设置引导程序ApplicationContext的。如果存在有效的配置文件(通过spring.profiles.active或通过您正在构建的上下文中的Environment API),该配置文件中的属性也会被加载,这与常规Spring Boot应用程序中的加载情况相同-例如,从bootstrap-development.properties中获取development个人资料。
2.4覆盖远程Properties的值

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
通过引导上下文添加到应用程序中的属性源通常是“ 远程的 ”(例如,来自Spring Cloud Config Server)。默认情况下,不能在本地覆盖它们。如果要让您的应用程序使用其自己的系统属性或配置文件覆盖远程属性,则远程属性源必须通过设置spring.cloud.config.allowOverride=true来授予其权限(在本地设置无效)。设置该标志后,将使用两个更细粒度的设置来控制远程属性相对于系统属性和应用程序本地配置的位置:
    spring.cloud.config.overrideNone=true:从任何本地属性源覆盖。spring.cloud.config.overrideSystemProperties=false:只有系统属性,命令行参数和环境变量(而不是本地配置文件)才应覆盖远程设置。
2.5自定义Bootstrap配置

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
通过将项添加到名为org.springframework.cloud.bootstrap.BootstrapConfiguration的项下的/META-INF/spring.factories中,可以将引导上下文设置为执行您喜欢的任何操作。它包含用于创建上下文的Spring @Configuration类的逗号分隔列表。您可以在此处创建要用于主应用程序上下文进行自动装配的任何beans。@Beans类型为ApplicationContextInitializer的特殊合同。如果要控制启动顺序,则可以用@Order批注标记类(默认顺序为last)。


当添加自定义BootstrapConfiguration,小心你添加类不是@ComponentScanned错误地进入你的“ 主 ”应用程序上下文,这里可能并不需要它们。为引导配置类使用单独的程序包名称,并确保@ComponentScan或带注释的配置类@SpringBootApplication尚未包含该名称。
引导过程结束时,将初始化程序注入到主要的SpringApplication实例中(这是正常的Spring Boot启动顺序,无论它是作为独立应用程序运行还是部署在应用程序服务器中)。首先,从spring.factories中找到的类创建引导上下文。然后,在启动之前,将类型为ApplicationContextInitializer的所有@Beans添加到主SpringApplication。
2.6自定义引导程序Property源

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
引导过程添加的外部配置的默认属性来源是Spring Cloud Config服务器,但是您可以通过将类型PropertySourceLocator的beans添加到引导上下文(通过spring.factories)来添加其他来源。例如,您可以从其他服务器或数据库插入其他属性。
例如,请考虑以下定制定位器:
  1. @Configuration
  2. public class CustomPropertySourceLocator implements PropertySourceLocator {
  3.     @Override
  4.     public PropertySource<?> locate(Environment environment) {
  5.         return new MapPropertySource("customProperty",
  6.                 Collections.<String, Object>singletonMap("property.from.sample.custom.source", "worked as intended"));
  7.     }
  8. }
复制代码
传入的Environment是即将创建的ApplicationContext的那个,换句话说,就是我们为其提供其他属性源的那个。它已经有其正常的Spring Boot提供的属性源,因此您可以使用这些属性来定位特定于此Environment的属性源(例如,通过在spring.application.name上键入它,这与默认设置相同)。 Spring Cloud Config服务器属性源定位符)。
如果您创建一个包含此类的jar,然后添加包含以下内容的META-INF/spring.factories,则customPropertyPropertySource会出现在任何在其类路径中包含该jar的应用程序中:
  1. org.springframework.cloud.bootstrap.BootstrapConfiguration=sample.custom.CustomPropertySourceLocator
复制代码
2.7日志配置

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果要使用Spring Boot来配置日志设置,则应将此配置放在`bootstrap。[yml | 属性](如果您希望将其应用于所有事件)。


为了使Spring Cloud正确初始化日志记录配置,您不能使用自定义前缀。例如,初始化记录系统时,Spring Cloud无法识别使用custom.loggin.logpath。
2.8环境变化

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
应用程序侦听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。
2.9刷新范围

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
进行配置更改时,标记为@RefreshScope的Spring @Bean将得到特殊处理。此功能解决了状态beans的问题,该状态仅在初始化时才注入配置。例如,如果通过Environment更改数据库URL时DataSource具有打开的连接,则您可能希望这些连接的持有者能够完成他们正在做的事情。然后,下次某物从池中借用一个连接时,它将获得一个具有新URL的连接。
有时,甚至可能必须将@RefreshScope批注应用到只能初始化一次的某些beans上。如果bean是“不可变的”,则必须用@RefreshScope注释bean或在属性键spring.cloud.refresh.extra-refreshable下指定类名。


重要
如果您自己创建一个DataSource bean,而实现是一个HikariDataSource,则返回最特定的类型,在这种情况下为HikariDataSource。否则,您将需要设置spring.cloud.refresh.extra-refreshable=javax.sql.DataSource。
刷新作用域beans是惰性代理,它们在使用时(即在调用方法时)进行初始化,并且作用域充当初始化值的缓存。若要强制bean在下一个方法调用上重新初始化,必须使它的缓存条目无效。
RefreshScope在上下文中是bean,并具有公用的refreshAll()方法,可通过清除目标缓存来刷新作用域中的所有beans。/refresh端点公开了此功能(通过HTTP或JMX)。要按名称刷新单个bean,还有一个refresh(String)方法。
要公开/refresh端点,您需要在应用程序中添加以下配置:
  1. management:
  2.   endpoints:
  3.     web:
  4.       exposure:
  5.         include: refresh
复制代码


@RefreshScope在@Configuration类上(在技术上)有效,但是可能会导致令人惊讶的行为。例如,这并不意味着该类中定义的所有@Beans本身都在@RefreshScope中。具体而言,除非刷新本身在@RefreshScope中,否则依赖那些beans的任何内容都不能依赖于刷新启动时对其进行更新。在这种情况下,将在刷新时重建它,并重新注入其依赖项。此时,它们将从刷新的@Configuration重新初始化。
2.10加密和解密

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud具有Environment预处理器,用于在本地解密属性值。它遵循与Config Server相同的规则,并且通过encrypt.*具有相同的外部配置。因此,您可以使用{cipher}*形式的加密值,并且只要存在有效密钥,就可以在主应用程序上下文获得Environment设置之前对它们进行解密。要在应用程序中使用加密功能,您需要在类路径中包含Spring Security RSA(Maven坐标:“ org.springframework.security:spring-security-rsa”),并且还需要JVM中的全功能JCE扩展。
如果由于“密钥大小非法”而导致异常,并且使用Sun的JDK,则需要安装Java密码术扩展(JCE)无限强度管辖权策略文件。有关更多信息,请参见以下链接:
    Java 6 JCEJava 7 JCEJava 8 JCE
将文件解压缩到您使用的JRE / JDK x64 / x86版本的JDK / jre / lib / security文件夹中。
2.11端点

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
对于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的特例。
3. Spring Cloud Commons:通用抽象

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
服务发现,负载平衡和断路器之类的模式将它们带到一个通用的抽象层,可以由所有Spring Cloud客户端使用,而与实现无关(例如,使用Eureka或Consul进行的发现) )。
3.1 @EnableDiscoveryClient

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Commons提供了@EnableDiscoveryClient批注。这将寻找META-INF/spring.factories与DiscoveryClient接口的实现。Discovery Client的实现在org.springframework.cloud.client.discovery.EnableDiscoveryClient键下将配置类添加到spring.factories。DiscoveryClient实现的示例包括Spring Cloud Netflix Eureka,Spring Cloud Consul发现和Spring Cloud Zookeeper发现。
默认情况下,DiscoveryClient的实现会自动将本地Spring Boot服务器注册到远程发现服务器。可以通过在@EnableDiscoveryClient中设置autoRegister=false来禁用此行为。


不再需要@EnableDiscoveryClient。您可以在类路径上放置DiscoveryClient实现,以使Spring Boot应用程序向服务发现服务器注册。
3.1.1健康指标

公用创建了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。否则,它可能会像已卷起的HealthIndicator中的description一样冒泡。
3.1.2订购DiscoveryClient实例

DiscoveryClient接口扩展了Ordered。当使用多个发现客户端时,这很有用,因为它允许您定义返回的发现客户端的顺序,类似于如何订购由Spring应用程序加载的beans。默认情况下,任何DiscoveryClient的顺序都设置为0。如果要为自定义DiscoveryClient实现设置不同的顺序,则只需覆盖getOrder()方法,以便它返回适合您的设置的值。除此之外,您可以使用属性来设置Spring Cloud提供的DiscoveryClient实现的顺序,其中包括ConsulDiscoveryClient,EurekaDiscoveryClient和ZookeeperDiscoveryClient。为此,您只需要将spring.cloud.{clientIdentifier}.discovery.order(对于Eureka,则为eureka.client.order)属性设置为所需的值。
3.2服务注册

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Commons现在提供一个ServiceRegistry接口,该接口提供诸如register(Registration)和deregister(Registration)之类的方法,这些方法使您可以提供自定义的注册服务。Registration是标记界面。
以下示例显示了正在使用的ServiceRegistry:
  1. @Configuration
  2. @EnableDiscoveryClient(autoRegister=false)
  3. public class MyConfiguration {
  4.     private ServiceRegistry registry;
  5.     public MyConfiguration(ServiceRegistry registry) {
  6.         this.registry = registry;
  7.     }
  8.     // called through some external process, such as an event or a custom actuator endpoint
  9.     public void register() {
  10.         Registration registration = constructRegistration();
  11.         this.registry.register(registration);
  12.     }
  13. }
复制代码
每个ServiceRegistry实现都有自己的Registry实现。
    ZookeeperRegistration与ZookeeperServiceRegistry一起使用EurekaRegistration与EurekaServiceRegistry一起使用ConsulRegistration与ConsulServiceRegistry一起使用
如果您使用的是ServiceRegistry接口,则将需要为使用的ServiceRegistry实现传递正确的Registry实现。
3.2.1 ServiceRegistry自动注册

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

服务自动注册时将触发两个事件。注册服务之前会触发名为InstancePreRegisteredEvent的第一个事件。注册服务后,将触发名为InstanceRegisteredEvent的第二个事件。您可以注册ApplicationListener,以收听和响应这些事件。


如果将spring.cloud.service-registry.auto-registration.enabled设置为false,则不会触发这些事件。
3.2.2服务注册表执行器端点

Spring Cloud Commons提供了一个/service-registry执行器端点。该端点依赖于Spring应用程序上下文中的Registration bean。使用GET调用/service-registry会返回Registration的状态。对具有JSON正文的同一终结点使用POST会将当前Registration的状态更改为新值。JSON正文必须包含带有首选值的status字段。请参阅更新状态时用于允许值的ServiceRegistry实现的文档以及为状态返回的值。例如,Eureka的受支持状态为UP,DOWN,OUT_OF_SERVICE和UNKNOWN。
3.3 Spring RestTemplate作为负载均衡器客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
RestTemplate可以自动配置为在后台使用负载均衡器客户端。要创建负载均衡的RestTemplate,请创建RestTemplate@Bean并使用@LoadBalanced限定符,如以下示例所示:
  1. @Configuration
  2. public class MyConfiguration {
  3.     @LoadBalanced
  4.     @Bean
  5.     RestTemplate restTemplate() {
  6.         return new RestTemplate();
  7.     }
  8. }
  9. public class MyClass {
  10.     @Autowired
  11.     private RestTemplate restTemplate;
  12.     public String doOtherStuff() {
  13.         String results = restTemplate.getForObject("http://stores/stores", String.class);
  14.         return results;
  15.     }
  16. }
复制代码


警告
RestTemplate bean不再通过自动配置创建。各个应用程序必须创建它。
URI需要使用虚拟主机名(即服务名,而不是主机名)。Ribbon客户端用于创建完整的物理地址。有关如何设置RestTemplate的详细信息,请参见RibbonAutoConfiguration。


重要
为了使用负载均衡的RestTemplate,您需要在类路径中具有负载均衡器实现。推荐的实现是BlockingLoadBalancerClient-添加org.springframework.cloud:spring-cloud-loadbalancer以便使用它。RibbonLoadBalancerClient也可以使用,但是目前正在维护中,我们不建议将其添加到新项目中。


如果要使用BlockingLoadBalancerClient,请确保项目类路径中没有RibbonLoadBalancerClient,因为向后兼容的原因,默认情况下将使用它。
3.4 Spring WebClient作为负载均衡器客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
WebClient可以自动配置为使用负载均衡器客户端。要创建负载均衡的WebClient,请创建WebClient.Builder@Bean并使用@LoadBalanced限定符,如以下示例所示:
  1. @Configuration
  2. public class MyConfiguration {
  3.         @Bean
  4.         @LoadBalanced
  5.         public WebClient.Builder loadBalancedWebClientBuilder() {
  6.                 return WebClient.builder();
  7.         }
  8. }
  9. public class MyClass {
  10.     @Autowired
  11.     private WebClient.Builder webClientBuilder;
  12.     public Mono<String> doOtherStuff() {
  13.         return webClientBuilder.build().get().uri("http://stores/stores")
  14.                                         .retrieve().bodyToMono(String.class);
  15.     }
  16. }
复制代码
URI需要使用虚拟主机名(即服务名,而不是主机名)。Ribbon客户端用于创建完整的物理地址。


重要
如果要使用@LoadBalanced WebClient.Builder,则需要在类路径中有一个loadbalancer实现。建议您将org.springframework.cloud:spring-cloud-loadbalancer依赖项添加到项目中。然后,将在下面使用ReactiveLoadBalancer。或者,此功能也可以在spring-cloud-starter-netflix-ribbon上使用,但是该请求将由后台的非响应LoadBalancerClient处理。此外,spring-cloud-starter-netflix-ribbon已经处于维护模式,因此我们不建议您将其添加到新项目中。


在下面使用的ReactorLoadBalancer支持缓存。如果检测到cacheManager,将使用ServiceInstanceSupplier的缓存版本。如果没有,我们将从发现服务中检索实例,而不进行缓存。如果您使用ReactiveLoadBalancer,建议您在项目中启用缓存。
3.4.1重试失败的请求

可以配置负载均衡的RestTemplate以重试失败的请求。默认情况下,禁用此逻辑。您可以通过在应用程序的类路径中添加Spring重试来启用它。负载平衡的RestTemplate遵循与重试失败的请求有关的某些Ribbon配置值。您可以使用client.ribbon.MaxAutoRetries,client.ribbon.MaxAutoRetriesNextServer和client.ribbon.OkToRetryOnAllOperations属性。如果要通过对类路径使用Spring重试来禁用重试逻辑,则可以设置spring.cloud.loadbalancer.retry.enabled=false。有关这些属性的作用的说明,请参见Ribbon文档。
如果要在重试中实现BackOffPolicy,则需要创建LoadBalancedRetryFactory类型的bean并覆盖createBackOffPolicy方法:
  1. @Configuration
  2. public class MyConfiguration {
  3.     @Bean
  4.     LoadBalancedRetryFactory retryFactory() {
  5.         return new LoadBalancedRetryFactory() {
  6.             @Override
  7.             public BackOffPolicy createBackOffPolicy(String service) {
  8.                         return new ExponentialBackOffPolicy();
  9.                 }
  10.         };
  11.     }
  12. }
复制代码


前面示例中的client应替换为您的Ribbon客户名称。
如果要向重试功能中添加一个或多个RetryListener实现,则需要创建类型为LoadBalancedRetryListenerFactory的bean,并返回要用于给定服务的RetryListener数组,如以下示例所示:
  1. @Configuration
  2. public class MyConfiguration {
  3.     @Bean
  4.     LoadBalancedRetryListenerFactory retryListenerFactory() {
  5.         return new LoadBalancedRetryListenerFactory() {
  6.             @Override
  7.             public RetryListener[] createRetryListeners(String service) {
  8.                 return new RetryListener[]{new RetryListener() {
  9.                     @Override
  10.                     public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
  11.                         //TODO Do you business...
  12.                         return true;
  13.                     }
  14.                     @Override
  15.                      public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
  16.                         //TODO Do you business...
  17.                     }
  18.                     @Override
  19.                     public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback, Throwable throwable) {
  20.                         //TODO Do you business...
  21.                     }
  22.                 }};
  23.             }
  24.         };
  25.     }
  26. }
复制代码
3.5多个RestTemplate对象

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果您想要一个RestTemplate而不是负载均衡的,请创建一个RestTemplate bean并注入它。要访问负载均衡的RestTemplate,请在创建@Bean时使用@LoadBalanced限定符,如以下示例所示:
  1. @Configuration
  2. public class MyConfiguration {
  3.     @LoadBalanced
  4.     @Bean
  5.     RestTemplate loadBalanced() {
  6.         return new RestTemplate();
  7.     }
  8.     @Primary
  9.     @Bean
  10.     RestTemplate restTemplate() {
  11.         return new RestTemplate();
  12.     }
  13. }
  14. public class MyClass {
  15. @Autowired
  16. private RestTemplate restTemplate;
  17.     @Autowired
  18.     @LoadBalanced
  19.     private RestTemplate loadBalanced;
  20.     public String doOtherStuff() {
  21.         return loadBalanced.getForObject("http://stores/stores", String.class);
  22.     }
  23.     public String doStuff() {
  24.         return restTemplate.getForObject("https://example.com", String.class);
  25.     }
  26. }
复制代码


重要
注意,在前面的示例中,在普通的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。
3.6多个WebClient对象

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果要使WebClient负载不均衡,请创建一个WebClient bean并注入它。要访问负载均衡的WebClient,请在创建@Bean时使用@LoadBalanced限定符,如以下示例所示:
  1. @Configuration
  2. public class MyConfiguration {
  3.     @LoadBalanced
  4.     @Bean
  5.     WebClient.Builder loadBalanced() {
  6.         return WebClient.builder();
  7.     }
  8.     @Primary
  9.     @Bean
  10.     WebClient.Builder webClient() {
  11.         return WebClient.builder();
  12.     }
  13. }
  14. public class MyClass {
  15.     @Autowired
  16.     private WebClient.Builder webClientBuilder;
  17.     @Autowired
  18.     @LoadBalanced
  19.     private WebClient.Builder loadBalanced;
  20.     public Mono<String> doOtherStuff() {
  21.         return loadBalanced.build().get().uri("http://stores/stores")
  22.                                         .retrieve().bodyToMono(String.class);
  23.     }
  24.     public Mono<String> doStuff() {
  25.         return webClientBuilder.build().get().uri("http://example.com")
  26.                                         .retrieve().bodyToMono(String.class);
  27.     }
  28. }
复制代码
3.7 Spring WebFlux WebClient作为负载均衡器客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
3.7.1 Spring具有响应式负载均衡器的WebFlux WebClient

可以将WebClient配置为使用ReactiveLoadBalancer。如果将org.springframework.cloud:spring-cloud-loadbalancer添加到项目中,并且spring-webflux在类路径中,则会自动配置ReactorLoadBalancerExchangeFilterFunction。以下示例说明如何配置WebClient以在后台使用无功负载均衡器:
  1. public class MyClass {
  2.     @Autowired
  3.     private ReactorLoadBalancerExchangeFilterFunction lbFunction;
  4.     public Mono<String> doOtherStuff() {
  5.         return WebClient.builder().baseUrl("http://stores")
  6.             .filter(lbFunction)
  7.             .build()
  8.             .get()
  9.             .uri("/stores")
  10.             .retrieve()
  11.             .bodyToMono(String.class);
  12.     }
  13. }
复制代码
URI需要使用虚拟主机名(即服务名,而不是主机名)。ReactorLoadBalancerClient用于创建完整的物理地址。
3.7.2 Spring WebFlux WebClient,带有非反应式负载均衡器客户端

如果您的项目中没有org.springframework.cloud:spring-cloud-loadbalancer,但是确实有spring-cloud-starter-netflix-ribbon,则仍可以将WebClient与LoadBalancerClient结合使用。如果spring-webflux在类路径中,将自动配置LoadBalancerExchangeFilterFunction。但是请注意,这是在后台使用非反应性客户端。以下示例显示如何配置WebClient以使用负载均衡器:
  1. public class MyClass {
  2.     @Autowired
  3.     private LoadBalancerExchangeFilterFunction lbFunction;
  4.     public Mono<String> doOtherStuff() {
  5.         return WebClient.builder().baseUrl("http://stores")
  6.             .filter(lbFunction)
  7.             .build()
  8.             .get()
  9.             .uri("/stores")
  10.             .retrieve()
  11.             .bodyToMono(String.class);
  12.     }
  13. }
复制代码
URI需要使用虚拟主机名(即服务名,而不是主机名)。LoadBalancerClient用于创建完整的物理地址。
警告:现在不建议使用此方法。我们建议您将WebFlux与电抗性负载平衡器一起 使用。
3.7.3传递自己的Load-Balancer客户端配置

您还可以使用@LoadBalancerClient批注传递您自己的负载平衡器客户端配置,并传递负载平衡器客户端的名称和配置类,如下所示:
  1. @Configuration
  2. @LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class)
  3. public class MyConfiguration {
  4.         @Bean
  5.         @LoadBalanced
  6.         public WebClient.Builder loadBalancedWebClientBuilder() {
  7.                 return WebClient.builder();
  8.         }
  9. }
复制代码
也可以通过@LoadBalancerClients注释将多个配置(对于一个以上的负载均衡器客户端)一起传递,如下所示:
  1. @Configuration
  2. @LoadBalancerClients({@LoadBalancerClient(value = "stores", configuration = StoresLoadBalancerClientConfiguration.class), @LoadBalancerClient(value = "customers", configuration = CustomersLoadBalancerClientConfiguration.class)})
  3. public class MyConfiguration {
  4.         @Bean
  5.         @LoadBalanced
  6.         public WebClient.Builder loadBalancedWebClientBuilder() {
  7.                 return WebClient.builder();
  8.         }
  9. }
复制代码
3.8忽略网络接口

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
有时,忽略某些命名的网络接口很有用,以便可以将它们从服务发现注册中排除(例如,在Docker容器中运行时)。可以设置正则表达式列表,以使所需的网络接口被忽略。以下配置将忽略docker0接口以及所有以veth开头的接口:
application.yml。
  1. spring:
  2.   cloud:
  3.     inetutils:
  4.       ignoredInterfaces:
  5.         - docker0
  6.         - veth.*
复制代码
您还可以通过使用正则表达式列表来强制仅使用指定的网络地址,如以下示例所示:
bootstrap.yml。
  1. spring:
  2.   cloud:
  3.     inetutils:
  4.       preferredNetworks:
  5.         - 192.168
  6.         - 10.0
复制代码
您也可以只使用站点本地地址,如以下示例所示:.application.yml
  1. spring:
  2.   cloud:
  3.     inetutils:
  4.       useOnlySiteLocalInterfaces: true
复制代码
有关构成站点本地地址的详细信息,请参见Inet4Address.html.isSiteLocalAddress()。
3.9 HTTP客户端工厂

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
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的创建。
3.10启用的功能

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Commons提供了一个/features执行器端点。该端点返回类路径上可用的功能以及是否已启用它们。返回的信息包括功能类型,名称,版本和供应商。
3.10.1特征类型

“功能”有两种类型:抽象和命名。
抽象功能是定义接口或抽象类并创建实现的功能,例如DiscoveryClient,LoadBalancerClient或LockService。抽象类或接口用于在上下文中找到该类型的bean。显示的版本为bean.getClass().getPackage().getImplementationVersion()。
命名功能是没有实现的特定类的功能,例如“ Circuit Breaker”,“ API Gateway”,“ Spring Cloud Bus”等。这些功能需要一个名称和一个bean类型。
3.10.2声明功能

任何模块都可以声明任意数量的HasFeature beans,如以下示例所示:
  1. @Bean
  2. public HasFeatures commonsFeatures() {
  3.   return HasFeatures.abstractFeatures(DiscoveryClient.class, LoadBalancerClient.class);
  4. }
  5. @Bean
  6. public HasFeatures consulFeatures() {
  7.   return HasFeatures.namedFeatures(
  8.     new NamedFeature("Spring Cloud Bus", ConsulBusAutoConfiguration.class),
  9.     new NamedFeature("Circuit Breaker", HystrixCommandAspect.class));
  10. }
  11. @Bean
  12. HasFeatures localFeatures() {
  13.   return HasFeatures.builder()
  14.       .abstractFeature(Foo.class)
  15.       .namedFeature(new NamedFeature("Bar Feature", Bar.class))
  16.       .abstractFeature(Baz.class)
  17.       .build();
  18. }
复制代码
这些beans中的每一个都应放入受到适当保护的@Configuration中。
3.11 Spring Cloud兼容性验证

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
由于某些用户在设置Spring Cloud应用程序时遇到问题,我们决定添加兼容性验证机制。如果您当前的设置与Spring Cloud要求不兼容,则会断开,并附上一份报告,说明出了什么问题。
目前,我们验证哪个版本的Spring Boot已添加到您的类路径中。
报告范例
  1. ***************************
  2. APPLICATION FAILED TO START
  3. ***************************
  4. Description:
  5. Your project setup is incompatible with our requirements due to following reasons:
  6. - Spring Boot [2.1.0.RELEASE] is not compatible with this Spring Cloud release train
  7. Action:
  8. Consider applying the following actions:
  9. - Change Spring Boot version to one of the following versions [1.2.x, 1.3.x] .
  10. You can find the latest Spring Boot versions here [https://spring.io/projects/spring-boot#learn].
  11. 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属性。
第二部分 Spring Cloud Config

Greenwich SR5
Spring Cloud Config为分布式系统中的外部化配置提供服务器端和客户端支持。使用Config Server,您可以在中心位置管理所有环境中应用程序的外部属性。客户端和服务器上的概念都与Spring Environment和PropertySource抽象映射相同,因此它们非常适合Spring应用程序,但可以与以任何语言运行的任何应用程序一起使用。在应用程序从开发人员到测试人员再到生产人员的整个部署过程中,您可以管理这些环境之间的配置,并确保应用程序具有它们迁移时所需的一切。服务器存储后端的默认实现使用git,因此它轻松支持带标签的配置环境版本,并且可以通过各种工具来访问这些内容来管理内容。添加替代实现并将其插入Spring配置很容易。
4.快速入门

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
此快速入门介绍了如何使用Spring Cloud Config服务器的服务器和客户端。
首先,启动服务器,如下所示:
  1. $ cd spring-cloud-config-server
  2. $ ../mvnw spring-boot:run
复制代码
该服务器是Spring Boot应用程序,因此,如果愿意,可以从IDE运行它(主类是ConfigServerApplication)。
接下来尝试一个客户端,如下所示:
  1. $ curl localhost:8888/foo/development
  2. {"name":"foo","label":"master","propertySources":[
  3.   {"name":"https://github.com/scratches/config-repo/foo-development.properties","source":{"bar":"spam"}},
  4.   {"name":"https://github.com/scratches/config-repo/foo.properties","source":{"foo":"bar"}}
  5. ]}
复制代码
定位属性源的默认策略是克隆git存储库(位于spring.cloud.config.server.git.uri),并使用它来初始化小型SpringApplication。小型应用程序的Environment用于枚举属性源并将其发布在JSON端点上。
HTTP服务具有以下形式的资源:
  1. /{application}/{profile}[/{label}]
  2. /{application}-{profile}.yml
  3. /{label}/{application}-{profile}.yml
  4. /{application}-{profile}.properties
  5. /{label}/{application}-{profile}.properties
复制代码
application在SpringApplication中作为spring.config.name注入(在常规Spring Boot应用中通常是application),profile是有效配置文件(或逗号分隔)属性列表),而label是可选的git标签(默认为master。)
Spring Cloud Config服务器从各种来源获取远程客户端的配置。以下示例从git存储库(必须提供)中获取配置,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
复制代码
其他来源包括任何与JDBC兼容的数据库,Subversion,Hashicorp Vault,Credhub和本地文件系统。
4.1客户端使用

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要在应用程序中使用这些功能,您可以将其构建为依赖于spring-cloud-config-client的Spring Boot应用程序(例如,请参阅config-client或示例应用程序的测试用例)。添加依赖项最方便的方法是使用Spring Boot启动器org.springframework.cloud:spring-cloud-starter-config。还有一个Maven用户的父pom和BOM(spring-cloud-starter-parent),以及一个Gradle和Spring CLI用户的Spring IO版本管理属性文件。以下示例显示了典型的Maven配置:
pom.xml。
  1.    <parent>
  2.        <groupId>org.springframework.boot</groupId>
  3.        <artifactId>spring-boot-starter-parent</artifactId>
  4.        <version>{spring-boot-docs-version}</version>
  5.        <relativePath /> <!-- lookup parent from repository -->
  6.    </parent>
  7. <dependencyManagement>
  8.         <dependencies>
  9.                 <dependency>
  10.                         <groupId>org.springframework.cloud</groupId>
  11.                         <artifactId>spring-cloud-dependencies</artifactId>
  12.                         <version>{spring-cloud-version}</version>
  13.                         <type>pom</type>
  14.                         <scope>import</scope>
  15.                 </dependency>
  16.         </dependencies>
  17. </dependencyManagement>
  18. <dependencies>
  19.         <dependency>
  20.                 <groupId>org.springframework.cloud</groupId>
  21.                 <artifactId>spring-cloud-starter-config</artifactId>
  22.         </dependency>
  23.         <dependency>
  24.                 <groupId>org.springframework.boot</groupId>
  25.                 <artifactId>spring-boot-starter-test</artifactId>
  26.                 <scope>test</scope>
  27.         </dependency>
  28. </dependencies>
  29. <build>
  30.         <plugins>
  31.            <plugin>
  32.                <groupId>org.springframework.boot</groupId>
  33.                <artifactId>spring-boot-maven-plugin</artifactId>
  34.            </plugin>
  35.         </plugins>
  36. </build>
  37.    <!-- repositories also needed for snapshots and milestones -->
复制代码
现在,您可以创建一个标准的Spring Boot应用程序,例如以下HTTP服务器:
  1. @SpringBootApplication
  2. @RestController
  3. public class Application {
  4.     @RequestMapping("/")
  5.     public String home() {
  6.         return "Hello World!";
  7.     }
  8.     public static void main(String[] args) {
  9.         SpringApplication.run(Application.class, args);
  10.     }
  11. }
复制代码
当此HTTP服务器运行时,它将从端口8888上的默认本地配置服务器(如果正在运行)中获取外部配置。要修改启动行为,可以使用bootstrap.properties(类似于application.properties,但用于应用程序上下文的引导阶段),如以下示例所示:
  1. spring.cloud.config.uri: http://myconfigserver.com
复制代码
默认情况下,如果未设置应用程序名称,将使用application。要修改名称,可以将以下属性添加到bootstrap.properties文件中:
  1. spring.application.name: myapp
复制代码


设置属性${spring.application.name}时,请勿在您的应用名称前加上保留字application-,以防止解析正确的属性源时出现问题。
引导程序属性在/env端点中显示为高优先级属性源,如以下示例所示。
  1. $ curl localhost:8080/env
  2. {
  3.   "profiles":[],
  4.   "configService:https://github.com/spring-cloud-samples/config-repo/bar.properties":{"foo":"bar"},
  5.   "servletContextInitParams":{},
  6.   "systemProperties":{...},
  7.   ...
  8. }
复制代码
名为```configService:/的属性源包含值为bar的foo`属性,并且优先级最高。


属性源名称中的URL是git存储库,而不是配置服务器URL。
5. Spring Cloud Config服务器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Config服务器为外部配置(名称/值对或等效的YAML内容)提供了一个基于HTTP资源的API。通过使用@EnableConfigServer批注,服务器可嵌入到Spring Boot应用程序中。因此,以下应用程序是配置服务器:
ConfigServer.java。
  1. @SpringBootApplication
  2. @EnableConfigServer
  3. public class ConfigServer {
  4.   public static void main(String[] args) {
  5.     SpringApplication.run(ConfigServer.class, args);
  6.   }
  7. }
复制代码
像所有Spring Boot应用程序一样,它默认在端口8080上运行,但是您可以通过各种方式将其切换到更传统的端口8888。最简单的方法也是设置默认配置存储库,方法是使用spring.config.name=configserver(在Config Server jar中有configserver.yml)启动它。另一种方法是使用您自己的application.properties,如以下示例所示:
application.properties。
  1. server.port: 8888
  2. spring.cloud.config.server.git.uri: file://${user.home}/config-repo
复制代码
其中${user.home}/config-repo是包含YAML和属性文件的git存储库。


在Windows上,如果文件URL带有驱动器前缀(例如,file:///${user.home}/config-repo)是绝对的,则在文件URL中需要一个额外的“ /”。


以下清单显示了在前面的示例中创建git存储库的方法:$ cd $HOME $ mkdir config-repo $ cd config-repo $ git init . $ echo info.foo: bar > application.properties $ git add -A . $ git commit -m "Add application.properties"


将本地文件系统用于git存储库仅用于测试。您应该使用服务器在生产环境中托管配置存储库。


如果仅将文本文件保留在其中,则配置存储库的初始克隆可以快速有效。如果存储二进制文件(尤其是大文件),则可能会在首次配置请求时遇到延迟,或者在服务器中遇到内存不足错误。
5.1环境Repository

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您应该在哪里存储配置服务器的配置数据?控制此行为的策略是服务Environment对象的EnvironmentRepository。Environment是Spring Environment的域的浅表副本(包括propertySources作为主要特征)。Environment资源由三个变量参数化:
    {application},它映射到客户端的spring.application.name。{profile},它映射到客户端上的spring.profiles.active(以逗号分隔的列表)。{label},这是服务器端功能,标记了一组“版本化”的配置文件。
Repository实现通常类似于Spring Boot应用程序,从等于{application}参数的spring.config.name和等于{profiles}参数的spring.profiles.active加载配置文件。配置文件的优先规则也与常规Spring Boot应用程序中的规则相同:活动配置文件的优先级高于默认设置,并且,如果有多个配置文件,则最后一个优先(与向Map添加条目类似)。
以下示例客户端应用程序具有此引导程序配置:
bootstrap.yml。
  1. spring:
  2.   application:
  3.     name: foo
  4.   profiles:
  5.     active: dev,mysql
复制代码
(与Spring Boot应用程序一样,这些属性也可以由环境变量或命令行参数设置)。
如果存储库基于文件,则服务器从application.yml(在所有客户端之间共享)和foo.yml(以foo.yml优先)创建一个Environment。如果YAML文件中包含指向Spring配置文件的文档,则将以更高的优先级应用这些文件(按列出的配置文件的顺序)。如果存在特定于配置文件的YAML(或属性)文件,这些文件也将以比默认文件更高的优先级应用。较高的优先级会转换为Environment中较早列出的PropertySource。(这些相同的规则适用于独立的Spring Boot应用程序。)
您可以将spring.cloud.config.server.accept-empty设置为false,以便在未找到应用程序的情况下Server返回HTTP 404状态。默认情况下,此标志设置为true。
5.1.1 Git后端

EnvironmentRepository的默认实现使用Git后端,这对于管理升级和物理环境以及审核更改非常方便。要更改存储库的位置,可以在Config Server中设置spring.cloud.config.server.git.uri配置属性(例如,在application.yml中)。如果您使用file:前缀进行设置,则它应在本地存储库中运行,以便无需服务器即可快速轻松地开始使用。但是,在那种情况下,服务器直接在本地存储库上运行而无需克隆它(如果它不是裸露的,这并不重要,因为Config Server从不对“远程”存储库进行更改)。要扩展Config Server并使其高度可用,您需要使服务器的所有实例都指向同一存储库,因此仅共享文件系统可以工作。即使在那种情况下,最好对共享文件系统存储库使用ssh:协议,以便服务器可以克隆它并将本地工作副本用作缓存。
此存储库实现将HTTP资源的{label}参数映射到git标签(提交ID,分支名称或标记)。如果git分支或标记名称包含斜杠(/),则应使用特殊字符串(_)在HTTP URL中指定标签(以避免与其他URL路径产生歧义)。例如,如果标签为foo/bar,则替换斜杠将产生以下标签:foo(_)bar。特殊字符串(_)的包含内容也可以应用于{application}参数。如果您使用命令行客户端(例如curl),请注意URL中的括号-您应使用单引号(’’)将其从外壳中移出。
跳过SSL证书验证

可以通过将git.skipSslValidation属性设置为true(默认值为false)来禁用配置服务器对Git服务器的SSL证书的验证。
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://example.com/my/repo
  7.           skipSslValidation: true
复制代码
设置HTTP连接超时

您可以配置配置服务器将等待获取HTTP连接的时间(以秒为单位)。使用git.timeout属性。
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://example.com/my/repo
  7.           timeout: 4
复制代码
Git URI中的占位符

Spring Cloud Config服务器支持带有{application}和{profile}(如果需要的话还有{label})占位符的git存储库URL,但是请记住该标签始终用作git标签。因此,您可以使用类似于以下的结构来支持“ 每个应用程序一个存储库 ”策略:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/myorg/{application}
复制代码
您也可以使用类似的模式({profile})来支持“ 每个配置文件一个存储库 ”策略。
此外,在{application}参数中使用特殊字符串“(_)”可以启用对多个组织的支持,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/{application}
复制代码
其中在请求时以以下格式提供{application}:organization(_)application。
模式匹配和多个Repositories

Spring Cloud Config还通过在应用程序和概要文件名称上进行模式匹配来支持更复杂的需求。模式格式是以逗号分隔的{application}/{profile}名称列表,带有通配符(请注意,以通配符开头的模式可能需要加引号),如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
  7.           repos:
  8.             simple: https://github.com/simple/config-repo
  9.             special:
  10.               pattern: special*/dev*,*special*/dev*
  11.               uri: https://github.com/special/config-repo
  12.             local:
  13.               pattern: local*
  14.               uri: file:/home/configsvc/config-repo
复制代码
如果{application}/{profile}与任何模式都不匹配,它将使用在spring.cloud.config.server.git.uri下定义的默认URI。在上面的示例中,对于“ 简单 ”存储库,模式为simple/*(在所有配置文件中仅匹配一个名为simple的应用程序)。在“ 本地 ”库匹配,在所有配置文件(该/*后缀会自动添加到没有档案资料匹配的任何模式)local开头的所有应用程序名称。


的“ 单行 ”中所使用的短切“ 简单 ”的例子可以只用于如果唯一的属性被设置为URI。如果您需要设置其他任何内容(凭证,模式等),则需要使用完整表格。
回购中的pattern属性实际上是一个数组,因此您可以使用YAML数组(或属性文件中的[0],[1]等后缀)绑定到多个模式。如果要运行具有多个配置文件的应用程序,则可能需要这样做,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
  7.           repos:
  8.             development:
  9.               pattern:
  10.                 - '*/development'
  11.                 - '*/staging'
  12.               uri: https://github.com/development/config-repo
  13.             staging:
  14.               pattern:
  15.                 - '*/qa'
  16.                 - '*/production'
  17.               uri: https://github.com/staging/config-repo
复制代码


Spring Cloud猜测一个包含不以*结尾的配置文件的模式表示您实际上要匹配以该模式开头的配置文件列表(因此*/staging是["*/staging", "*/staging,*"]的快捷方式,等等)。例如,这很普遍,例如,您需要在本地的“ 开发 ”配置文件中运行应用程序,而又需要在远程的“ 云 ”配置文件中运行应用程序。
每个存储库还可以选择将配置文件存储在子目录中,用于搜索这些目录的模式可以指定为searchPaths。以下示例在顶层显示了一个配置文件:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
  7.           searchPaths: foo,bar*
复制代码
在前面的示例中,服务器在顶层和foo/子目录中以及名称以bar开头的任何子目录中搜索配置文件。
默认情况下,首次请求配置时,服务器会克隆远程存储库。可以将服务器配置为在启动时克隆存储库,如以下顶级示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://git/common/config-repo.git
  7.           repos:
  8.             team-a:
  9.                 pattern: team-a-*
  10.                 cloneOnStart: true
  11.                 uri: https://git/team-a/config-repo.git
  12.             team-b:
  13.                 pattern: team-b-*
  14.                 cloneOnStart: false
  15.                 uri: https://git/team-b/config-repo.git
  16.             team-c:
  17.                 pattern: team-c-*
  18.                 uri: https://git/team-a/config-repo.git
复制代码
在前面的示例中,服务器在接受任何请求之前会在启动时克隆team-a的config-repo。在请求从存储库进行配置之前,不会克隆所有其他存储库。


设置要在Config Server启动时克隆的存储库有助于在Config Server启动时快速识别配置错误的配置源(例如无效的存储库URI)。在未为配置源启用cloneOnStart的情况下,Config Server可能会以配置错误或无效的配置源成功启动,并且直到应用程序从该配置源请求配置时才检测到错误。
认证方式

要在远程存储库上使用HTTP基本认证,请分别添加username和password属性(不在URL中),如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
  7.           username: trolley
  8.           password: strongpassword
复制代码
如果您不使用HTTPS和用户凭据,则在将密钥存储在默认目录(~/.ssh)中且URI指向SSH位置(例如git@github.com:configuration/cloud-configuration)时,SSH也应立即可用。重要的是,~/.ssh/known_hosts文件中应包含Git服务器的条目,并且其格式应为ssh-rsa。不支持其他格式(例如ecdsa-sha2-nistp256)。为避免意外,您应确保Git服务器的known_hosts文件中仅存在一个条目,并且该条目与您提供给配置服务器的URL匹配。如果在URL中使用主机名,则要在known_hosts文件中完全使用该主机名(而不是IP)。使用JGit访问存储库,因此您找到的任何文档都应该适用。HTTPS代理设置可以在~/.git/config中设置,也可以(使用与其他JVM进程相同的方式)使用系统属性(-Dhttps.proxyHost和-Dhttps.proxyPort)进行设置。


如果您不知道~/.git目录在哪里,请使用git config --global来操纵设置(例如,git config --global http.sslVerify false)。
使用AWS CodeCommit进行身份验证

Spring Cloud Config服务器还支持AWS CodeCommit身份验证。从命令行使用Git时,AWS CodeCommit使用身份验证帮助程序。该帮助程序未与JGit库一起使用,因此,如果Git URI与AWS CodeCommit模式匹配,则会为AWS CodeCommit创建一个JGit CredentialProvider。AWS CodeCommit URI遵循以下模式://git-codecommit.                                       A                         W                                   S                            R                                  E                         G                         I                         O                         N                              .                      a                      m                      a                      z                      o                      n                      a                      w                      s                      .                      c                      o                      m                      /                          {AWS_REGION}.amazonaws.com/               AWSR?EGION.amazonaws.com/{repopath}。
如果您提供带有AWS CodeCommit URI的用户名和密码,则它们必须是提供对存储库访问权限的AWS accessKeyId和secretAccessKey。如果您未指定用户名和密码,则使用AWS Default Credential Provider链检索accessKeyId和secretAccessKey 。
如果您的Git URI与CodeCommit URI模式(如前所示)匹配,则必须在用户名和密码或默认凭据提供商链支持的位置之一中提供有效的AWS凭据。AWS EC2实例可以将IAM角色用于EC2实例。


aws-java-sdk-core jar是可选的依赖项。如果aws-java-sdk-core jar不在您的类路径中,则无论git服务器URI如何,都不会创建AWS Code Commit凭证提供程序。
使用属性进行Git SSH配置

默认情况下,当使用SSH URI连接到Git存储库时,Spring Cloud Config服务器使用的JGit库使用SSH配置文件,例如~/.ssh/known_hosts和/etc/ssh/ssh_config。在Cloud Foundry之类的云环境中,本地文件系统可能是临时的,或者不容易访问。在这种情况下,可以使用Java属性设置SSH配置。为了激活基于属性的SSH配置,必须将spring.cloud.config.server.git.ignoreLocalSshSettings属性设置为true,如以下示例所示:
  1.   spring:
  2.     cloud:
  3.       config:
  4.         server:
  5.           git:
  6.             uri: git@gitserver.com:team/repo1.git
  7.             ignoreLocalSshSettings: true
  8.             hostKey: someHostKey
  9.             hostKeyAlgorithm: ssh-rsa
  10.             privateKey: |
  11.                          -----BEGIN RSA PRIVATE KEY-----
  12.                          MIIEpgIBAAKCAQEAx4UbaDzY5xjW6hc9jwN0mX33XpTDVW9WqHp5AKaRbtAC3DqX
  13.                          IXFMPgw3K45jxRb93f8tv9vL3rD9CUG1Gv4FM+o7ds7FRES5RTjv2RT/JVNJCoqF
  14.                          ol8+ngLqRZCyBtQN7zYByWMRirPGoDUqdPYrj2yq+ObBBNhg5N+hOwKjjpzdj2Ud
  15.                          1l7R+wxIqmJo1IYyy16xS8WsjyQuyC0lL456qkd5BDZ0Ag8j2X9H9D5220Ln7s9i
  16.                          oezTipXipS7p7Jekf3Ywx6abJwOmB0rX79dV4qiNcGgzATnG1PkXxqt76VhcGa0W
  17.                          DDVHEEYGbSQ6hIGSh0I7BQun0aLRZojfE3gqHQIDAQABAoIBAQCZmGrk8BK6tXCd
  18.                          fY6yTiKxFzwb38IQP0ojIUWNrq0+9Xt+NsypviLHkXfXXCKKU4zUHeIGVRq5MN9b
  19.                          BO56/RrcQHHOoJdUWuOV2qMqJvPUtC0CpGkD+valhfD75MxoXU7s3FK7yjxy3rsG
  20.                          EmfA6tHV8/4a5umo5TqSd2YTm5B19AhRqiuUVI1wTB41DjULUGiMYrnYrhzQlVvj
  21.                          5MjnKTlYu3V8PoYDfv1GmxPPh6vlpafXEeEYN8VB97e5x3DGHjZ5UrurAmTLTdO8
  22.                          +AahyoKsIY612TkkQthJlt7FJAwnCGMgY6podzzvzICLFmmTXYiZ/28I4BX/mOSe
  23.                          pZVnfRixAoGBAO6Uiwt40/PKs53mCEWngslSCsh9oGAaLTf/XdvMns5VmuyyAyKG
  24.                          ti8Ol5wqBMi4GIUzjbgUvSUt+IowIrG3f5tN85wpjQ1UGVcpTnl5Qo9xaS1PFScQ
  25.                          xrtWZ9eNj2TsIAMp/svJsyGG3OibxfnuAIpSXNQiJPwRlW3irzpGgVx/AoGBANYW
  26.                          dnhshUcEHMJi3aXwR12OTDnaLoanVGLwLnkqLSYUZA7ZegpKq90UAuBdcEfgdpyi
  27.                          PhKpeaeIiAaNnFo8m9aoTKr+7I6/uMTlwrVnfrsVTZv3orxjwQV20YIBCVRKD1uX
  28.                          VhE0ozPZxwwKSPAFocpyWpGHGreGF1AIYBE9UBtjAoGBAI8bfPgJpyFyMiGBjO6z
  29.                          FwlJc/xlFqDusrcHL7abW5qq0L4v3R+FrJw3ZYufzLTVcKfdj6GelwJJO+8wBm+R
  30.                          gTKYJItEhT48duLIfTDyIpHGVm9+I1MGhh5zKuCqIhxIYr9jHloBB7kRm0rPvYY4
  31.                          VAykcNgyDvtAVODP+4m6JvhjAoGBALbtTqErKN47V0+JJpapLnF0KxGrqeGIjIRV
  32.                          cYA6V4WYGr7NeIfesecfOC356PyhgPfpcVyEztwlvwTKb3RzIT1TZN8fH4YBr6Ee
  33.                          KTbTjefRFhVUjQqnucAvfGi29f+9oE3Ei9f7wA+H35ocF6JvTYUsHNMIO/3gZ38N
  34.                          CPjyCMa9AoGBAMhsITNe3QcbsXAbdUR00dDsIFVROzyFJ2m40i4KCRM35bC/BIBs
  35.                          q0TY3we+ERB40U8Z2BvU61QuwaunJ2+uGadHo58VSVdggqAo0BSkH58innKKt96J
  36.                          69pcVH/4rmLbXdcmNYGm6iu+MlPQk4BUZknHSmVHIFdJ0EPupVaQ8RHT
  37.                          -----END RSA PRIVATE KEY-----
复制代码
下表描述了SSH配置属性。
表5.1。SSH配置Properties
Property名称备注
ignoreLocalSshSettings如果为true,请使用基于属性的SSH配置而不是基于文件的SSH配置。必须设置为spring.cloud.config.server.git.ignoreLocalSshSettings,而不是在存储库定义中。
私钥有效的SSH私钥。如果ignoreLocalSshSettings为true并且Git URI为SSH格式,则必须设置。
hostKey有效的SSH主机密钥。如果还设置了hostKeyAlgorithm,则必须设置。
hostKeyAlgorithmssh-dss, ssh-rsa, ecdsa-sha2-nistp256, ecdsa-sha2-nistp384, or ecdsa-sha2-nistp521之一。如果还设置了hostKey,则必须设置。
strictHostKeyCheckingtrue或false。如果为false,请忽略主机密钥错误。
knownHostsFile自定义.known_hosts文件的位置。
preferredAuthentications覆盖服务器身份验证方法顺序。如果服务器在publickey方法之前进行了键盘交互式身份验证,则应该可以避免登录提示。
Git搜索路径中的占位符

Spring Cloud Config服务器还支持带有{application}和{profile}(如果需要的话还有{label})占位符的搜索路径,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
  7.           searchPaths: '{application}'
复制代码
上面的清单导致在存储库中搜索与目录(以及顶层)同名的文件。通配符在带有占位符的搜索路径中也有效(搜索中包括任何匹配的目录)。
强制拉动Git Repositories

如前所述,Spring Cloud Config服务器会复制远程git存储库,以防本地副本变脏(例如,操作系统进程更改了文件夹内容),使得Spring Cloud Config服务器无法从远程更新本地副本。资料库。
要解决此问题,有一个force-pull属性,如果本地副本脏了,则可以使Spring Cloud Config服务器从远程存储库强制拉出,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
  7.           force-pull: true
复制代码
如果您有多个存储库配置,则可以为每个存储库配置force-pull属性,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://git/common/config-repo.git
  7.           force-pull: true
  8.           repos:
  9.             team-a:
  10.                 pattern: team-a-*
  11.                 uri: https://git/team-a/config-repo.git
  12.                 force-pull: true
  13.             team-b:
  14.                 pattern: team-b-*
  15.                 uri: https://git/team-b/config-repo.git
  16.                 force-pull: true
  17.             team-c:
  18.                 pattern: team-c-*
  19.                 uri: https://git/team-a/config-repo.git
复制代码


force-pull属性的默认值为false。
删除Git Repositories中未跟踪的分支

由于Spring Cloud Config服务器在检出分支到本地存储库(例如,通过标签获取属性)后具有远程git存储库的克隆,因此它将永久保留该分支,直到下一个服务器重新启动(这将创建新的本地存储库)。因此,可能会删除远程分支,但仍可获取其本地副本。而且,如果Spring Cloud Config服务器客户端服务以--spring.cloud.config.label=deletedRemoteBranch,master开头,它将从deletedRemoteBranch本地分支获取属性,而不是从master获取属性。
为了使本地存储库分支保持整洁并保持远程状态-可以设置deleteUntrackedBranches属性。这将使Spring Cloud Config服务器从本地存储库中强制删除未跟踪的分支。例:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         git:
  6.           uri: https://github.com/spring-cloud-samples/config-repo
  7.           deleteUntrackedBranches: true
复制代码


deleteUntrackedBranches属性的默认值为false。
Git刷新率

您可以使用spring.cloud.config.server.git.refreshRate控制配置服务器多久从Git后端获取更新的配置数据。以秒为单位指定此属性的值。默认情况下,该值为0,这意味着配置服务器将在每次请求时从Git存储库中获取更新的配置。
5.1.2版本控制后端文件系统使用



使用基于VCS的后端(git,svn),文件被检出或克隆到本地文件系统。默认情况下,它们以config-repo-为前缀放在系统临时目录中。例如,在Linux上,它可能是/tmp/config-repo-<randomid>。一些操作系统通常会清除临时目录。这可能导致意外行为,例如缺少属性。为避免此问题,请通过将spring.cloud.config.server.git.basedir或spring.cloud.config.server.svn.basedir设置为不在系统临时结构中的目录来更改Config Server使用的目录。
5.1.3文件系统后端

Config Server中还有一个“ 本机 ”配置文件,该配置文件不使用Git,而是从本地类路径或文件系统(您要使用spring.cloud.config.server.native.searchLocations指向的任何静态URL)加载配置文件。要使用本机配置文件,请使用spring.profiles.active=native启动Config Server。


请记住对文件资源使用file:前缀(默认情况下,没有前缀的通常是类路径)。与任何Spring Boot配置一样,您可以嵌入${}样式的环境占位符,但是请记住,Windows中的绝对路径需要额外的/(例如,file:///${user.home}/config-repo)。


searchLocations的默认值与本地Spring Boot应用程序(即[classpath:/, classpath:/config, file:./, file:./config])相同。这不会将application.properties从服务器公开给所有客户端,因为服务器中存在的所有属性源在被发送到客户端之前都已被删除。


文件系统后端非常适合快速入门和测试。要在生产环境中使用它,您需要确保文件系统可靠并且可以在Config Server的所有实例之间共享。
搜索位置可以包含{application},{profile}和{label}的占位符。这样,您可以隔离路径中的目录并选择一种对您有意义的策略(例如,每个应用程序的子目录或每个配置文件的子目录)。
如果在搜索位置中不使用占位符,则此存储库还将HTTP资源的{label}参数附加到搜索路径上的后缀,因此将从每个搜索位置与该名称相同的子目录加载属性文件。标签(在Spring环境中,带有标签的属性优先)。因此,没有占位符的默认行为与添加以/{label}/结尾的搜索位置相同。例如,file:/tmp/config与file:/tmp/config,file:/tmp/config/{label}相同。可以通过设置spring.cloud.config.server.native.addLabelLocations=false来禁用此行为。
5.1.4 Vault后端

Spring Cloud Config服务器还支持Vault作为后端。
Vault是用于安全访问机密的工具。秘密是您要严格控制访问权限的所有内容,例如API密钥,密码,证书和其他敏感信息。Vault为所有机密提供了统一的界面,同时提供了严格的访问控制并记录了详细的审核日志。
有关Vault的更多信息,请参见Vault快速入门指南。
要使配置服务器能够使用Vault后端,您可以使用vault配置文件运行配置服务器。例如,在配置服务器的application.properties中,您可以添加spring.profiles.active=vault。
默认情况下,配置服务器假定您的Vault服务器在http://127.0.0.1:8200下运行。它还假定后端的名称为secret,密钥为application。所有这些默认值都可以在配置服务器的application.properties中进行配置。下表描述了可配置的Vault属性:
名称默认值
host127.0.0.1
port8200
schemehttp
backendsecret
defaultKeyapplication
profileSeparator,
kvVersion1
skipSslValidationfalse
timeout5
namespacenull


重要
上表中的所有属性必须以spring.cloud.config.server.vault为前缀,或放置在复合配置的正确Vault部分中。
所有可配置的属性都可以在org.springframework.cloud.config.server.environment.VaultEnvironmentProperties中找到。
Vault 0.10.0引入了版本化的键值后端(k / v后端版本2),该后端公开了与早期版本不同的API,现在它需要在安装路径和实际上下文路径之间使用data/并包装data对象中的秘密。设置kvVersion=2将考虑到这一点。
(可选)支持Vault企业版X-Vault-Namespace标头。要将其发送到Vault,请设置namespace属性。
在配置服务器运行时,您可以向服务器发出HTTP请求以从Vault后端检索值。为此,您需要Vault服务器的令牌。
首先,将一些数据放入您的Vault中,如以下示例所示:
  1. $ vault kv put secret/application foo=bar baz=bam
  2. $ vault kv put secret/myapp foo=myappsbar
复制代码
其次,向配置服务器发出HTTP请求以检索值,如以下示例所示:
  1. $ curl -X "GET" "http://localhost:8888/myapp/default" -H "X-Config-Token: yourtoken"
复制代码
您应该看到类似于以下内容的响应:
  1. {
  2.    "name":"myapp",
  3.    "profiles":[
  4.       "default"
  5.    ],
  6.    "label":null,
  7.    "version":null,
  8.    "state":null,
  9.    "propertySources":[
  10.       {
  11.          "name":"vault:myapp",
  12.          "source":{
  13.             "foo":"myappsbar"
  14.          }
  15.       },
  16.       {
  17.          "name":"vault:application",
  18.          "source":{
  19.             "baz":"bam",
  20.             "foo":"bar"
  21.          }
  22.       }
  23.    ]
  24. }
复制代码
多个Properties来源

使用Vault时,可以为您的应用程序提供多个属性源。例如,假设您已将数据写入Vault中的以下路径:
  1. secret/myApp,dev
  2. secret/myApp
  3. secret/application,dev
  4. secret/application
复制代码
写入secret/application的Properties对使用Config Server的所有应用程序均可用。名称为myApp的应用程序将具有写入secret/myApp和secret/application的所有属性。当myApp启用了dev配置文件时,写入上述所有路径的属性将可用,列表中第一个路径中的属性优先于其他属性。
5.1.5通过代理访问后端

配置服务器可以通过HTTP或HTTPS代理访问Git或Vault后端。通过proxy.http和proxy.https下的设置,可以为Git或Vault控制此行为。这些设置是针对每个存储库的,因此,如果您使用组合环境存储库,则必须分别为组合中的每个后端配置代理设置。如果使用的网络需要HTTP和HTTPS URL分别使用代理服务器,则可以为单个后端配置HTTP和HTTPS代理设置。
下表描述了HTTP和HTTPS代理的代理配置属性。所有这些属性都必须以proxy.http或proxy.https作为前缀。
表5.2。代理配置Properties
Property名称备注
主办代理的主机。
港口用于访问代理的端口。
nonProxyHosts配置服务器应在代理外部访问的所有主机。如果同时为proxy.http.nonProxyHosts和proxy.https.nonProxyHosts提供了值,则将使用proxy.http值。
用户名用来验证代理的用户名。如果同时为proxy.http.username和proxy.https.username提供了值,则将使用proxy.http值。
密码用来验证代理的密码。如果同时为proxy.http.password和proxy.https.password提供了值,则将使用proxy.http值。
以下配置使用HTTPS代理访问Git存储库。
  1. spring:
  2.   profiles:
  3.     active: git
  4.   cloud:
  5.     config:
  6.       server:
  7.         git:
  8.           uri: https://github.com/spring-cloud-samples/config-repo
  9.           proxy:
  10.             https:
  11.               host: my-proxy.host.io
  12.               password: myproxypassword
  13.               port: '3128'
  14.               username: myproxyusername
  15.               nonProxyHosts: example.com
复制代码
5.1.6与所有应用程序共享配置

所有应用程序之间的共享配置根据您采用的方法而异,如以下主题所述:
    称为“基于文件的Repositories”部分称为“ Vault服务器”的部分
基于文件的Repositories

使用基于文件(git,svn和本机)的存储库,所有客户端应用程序之间共享文件名称为application*(application.properties,application.yml,application-*.properties等)的资源。您可以使用具有这些文件名的资源来配置全局默认值,并在必要时使它们被应用程序特定的文件覆盖。
#_property_overrides [属性覆盖]功能也可以用于设置全局默认值,允许使用占位符应用程序在本地覆盖它们。


使用“ 本机 ”配置文件(本地文件系统后端),您应该使用不属于服务器自身配置的显式搜索位置。否则,默认搜索位置中的application*资源将被删除,因为它们是服务器的一部分。
Vault服务器

将Vault用作后端时,可以通过将配置放在secret/application中来与所有应用程序共享配置。例如,如果您运行以下Vault命令,则所有使用配置服务器的应用程序都将具有可用的属性foo和baz:
  1. $ vault write secret/application foo=bar baz=bam
复制代码
CredHub服务器

将CredHub用作后端时,可以通过将配置放在/application/中或将其放在应用程序的default配置文件中来与所有应用程序共享配置。例如,如果您运行以下CredHub命令,则使用配置服务器的所有应用程序将具有对它们可用的属性shared.color1和shared.color2:
  1. credhub set --name "/application/profile/master/shared" --type=json
  2. value: {"shared.color1": "blue", "shared.color": "red"}
  3. credhub set --name "/my-app/default/master/more-shared" --type=json
  4. value: {"shared.word1": "hello", "shared.word2": "world"}
复制代码
5.1.7 JDBC后端

Spring Cloud Config服务器支持JDBC(关系数据库)作为配置属性的后端。您可以通过向类路径中添加spring-jdbc并使用jdbc配置文件或添加类型为JdbcEnvironmentRepository的bean来启用此功能。如果您在类路径上包括正确的依赖项(有关更多详细信息,请参见用户指南),Spring Boot将配置数据源。
数据库需要有一个名为PROPERTIES的表,该表具有名为APPLICATION,PROFILE和LABEL的列(通常具有Environment的含义),以及KEY和VALUE,用于Properties样式的键和值对。Java中所有字段的类型均为String,因此您可以根据需要将它们设置为VARCHAR。Property值的行为与来自名为{application}-{profile}.properties的Spring Boot属性文件的值的行为相同,包括所有加密和解密,它们将用作后处理步骤(也就是说,在存储库中直接执行)。
5.1.8 CredHub后端

Spring Cloud Config服务器支持CredHub作为配置属性的后端。您可以通过向Spring CredHub添加依赖项来启用此功能。
pom.xml。
  1. <dependencies>
  2.         <dependency>
  3.                 <groupId>org.springframework.credhub</groupId>
  4.                 <artifactId>spring-credhub-starter</artifactId>
  5.         </dependency>
  6. </dependencies>
复制代码
以下配置使用双向TLS访问CredHub:
  1. spring:
  2.   profiles:
  3.     active: credhub
  4.   cloud:
  5.     config:
  6.       server:
  7.         credhub:
  8.           url: https://credhub:8844
复制代码
属性应存储为JSON,例如:
  1. credhub set --name "/demo-app/default/master/toggles" --type=json
  2. value: {"toggle.button": "blue", "toggle.link": "red"}
  3. credhub set --name "/demo-app/default/master/abs" --type=json
  4. value: {"marketing.enabled": true, "external.enabled": false}
复制代码
名称为spring.cloud.config.name=demo-app的所有客户端应用程序将具有以下属性:
  1. {
  2.     toggle.button: "blue",
  3.     toggle.link: "red",
  4.     marketing.enabled: true,
  5.     external.enabled: false
  6. }
复制代码


如果未指定配置文件,将使用default,而未指定标签时,将使用master作为默认值。注意:添加到application的值将由所有应用程序共享。
OAuth 2.0

您可以使用UAA作为提供程序通过OAuth 2.0进行身份验证。
pom.xml。
  1. <dependencies>
  2.         <dependency>
  3.                 <groupId>org.springframework.security</groupId>
  4.                 <artifactId>spring-security-config</artifactId>
  5.         </dependency>
  6.         <dependency>
  7.                 <groupId>org.springframework.security</groupId>
  8.                 <artifactId>spring-security-oauth2-client</artifactId>
  9.         </dependency>
  10. </dependencies>
复制代码
以下配置使用OAuth 2.0和UAA访问CredHub:
  1. spring:
  2.   profiles:
  3.     active: credhub
  4.   cloud:
  5.     config:
  6.       server:
  7.         credhub:
  8.           url: https://credhub:8844
  9.           oauth2:
  10.             registration-id: credhub-client
  11.   security:
  12.     oauth2:
  13.       client:
  14.         registration:
  15.           credhub-client:
  16.             provider: uaa
  17.             client-id: credhub_config_server
  18.             client-secret: asecret
  19.             authorization-grant-type: client_credentials
  20.         provider:
  21.           uaa:
  22.             token-uri: https://uaa:8443/oauth/token
复制代码


使用的UAA客户ID的范围应为credhub.read。
5.1.9复合环境Repositories

在某些情况下,您可能希望从多个环境存储库中提取配置数据。为此,您可以在配置服务器的应用程序属性或YAML文件中启用composite配置文件。例如,如果要从Subversion存储库以及两个Git存储库中提取配置数据,则可以为配置服务器设置以下属性:
  1. spring:
  2.   profiles:
  3.     active: composite
  4.   cloud:
  5.     config:
  6.       server:
  7.         composite:
  8.         -
  9.           type: svn
  10.           uri: file:///path/to/svn/repo
  11.         -
  12.           type: git
  13.           uri: file:///path/to/rex/git/repo
  14.         -
  15.           type: git
  16.           uri: file:///path/to/walter/git/repo
复制代码
使用此配置,优先级由composite键下的存储库列出顺序确定。在上面的示例中,首先列出了Subversion存储库,因此在Subversion存储库中找到的值将覆盖在一个Git存储库中为同一属性找到的值。在rex Git存储库中找到的值将在walter Git存储库中为相同属性找到的值之前使用。
如果只想从每种不同类型的存储库中提取配置数据,则可以在配置服务器的应用程序属性或YAML文件中启用相应的配置文件,而不启用composite配置文件。例如,如果要从单个Git存储库和单个HashiCorp Vault服务器中提取配置数据,则可以为配置服务器设置以下属性:
  1. spring:
  2.   profiles:
  3.     active: git, vault
  4.   cloud:
  5.     config:
  6.       server:
  7.         git:
  8.           uri: file:///path/to/git/repo
  9.           order: 2
  10.         vault:
  11.           host: 127.0.0.1
  12.           port: 8200
  13.           order: 1
复制代码
使用此配置,可以通过order属性确定优先级。您可以使用order属性为所有存储库指定优先级顺序。order属性的数值越低,优先级越高。存储库的优先级顺序有助于解决包含相同属性值的存储库之间的任何潜在冲突。


如果您的复合环境包括上一个示例中的Vault服务器,则在对配置服务器的每个请求中都必须包含Vault令牌。请参阅Vault后端。


从环境存储库中检索值时,任何类型的故障都会导致整个组合环境的故障。


使用复合环境时,所有存储库都包含相同的标签很重要。如果您的环境与前面的示例中的环境类似,并且您请求带有master标签的配置数据,但是Subversion存储库不包含名为master的分支,则整个请求将失败。
定制复合环境Repositories

除了使用Spring Cloud中的一个环境存储库之外,您还可以提供自己的EnvironmentRepository bean作为复合环境的一部分。为此,您的bean必须实现EnvironmentRepository接口。如果要在复合环境中控制自定义EnvironmentRepository的优先级,则还应该实现Ordered接口并覆盖getOrdered方法。如果未实现Ordered接口,则EnvironmentRepository的优先级最低。
5.1.10 Property覆盖

Config Server具有“ 替代 ”功能,使操作员可以为所有应用程序提供配置属性。应用程序使用常规的Spring Boot钩子不会意外更改重写的属性。要声明覆盖,请将名称/值对的映射添加到spring.cloud.config.server.overrides,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         overrides:
  6.           foo: bar
复制代码
前面的示例使作为配置客户端的所有应用程序读取foo=bar,而与它们自己的配置无关。


配置系统不能强制应用程序以任何特定方式使用配置数据。因此,覆盖无法执行。但是,它们确实为Spring Cloud Config客户端提供了有用的默认行为。


通常,可以使用反斜杠(\)来转义$或{,以逃避(并在客户端上解析)具有${}的Spring环境占位符。例如,除非应用程序提供自己的app.foo,否则\${app.foo:bar}解析为bar。


在YAML中,您不需要转义反斜杠本身。但是,在属性文件中,在服务器上配置替代时,确实需要转义反斜杠。
您可以通过在远程存储库中设置spring.cloud.config.overrideNone=true标志(默认为false),使客户端中所有替代的优先级更像默认值,让应用程序在环境变量或系统属性中提供自己的值。
5.2健康指标

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Config Server带有运行状况指示器,用于检查配置的EnvironmentRepository是否正常工作。默认情况下,它会向EnvironmentRepository询问名为app的应用程序,default配置文件以及EnvironmentRepository实现提供的默认标签。
您可以配置运行状况指示器以检查更多应用程序以及自定义配置文件和自定义标签,如以下示例所示:
  1. spring:
  2.   cloud:
  3.     config:
  4.       server:
  5.         health:
  6.           repositories:
  7.             myservice:
  8.               label: mylabel
  9.             myservice-dev:
  10.               name: myservice
  11.               profiles: development
复制代码
您可以通过设置spring.cloud.config.server.health.enabled=false禁用运行状况指示器。
5.3安全性

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您可以用对您有意义的任何方式来保护Config Server(从物理网络安全到OAuth2承载令牌),因为Spring Security和Spring Boot为许多安全措施提供了支持。
要使用默认的Spring Boot配置的HTTP基本安全性,请在类路径上包含Spring Security(例如,通过spring-boot-starter-security)。缺省值为user用户名和随机生成的密码。随机密码在实践中没有用,因此我们建议您配置密码(通过设置spring.security.user.password)并对其进行加密(有关如何操作的说明,请参见下文)。
5.4加密和解密

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看


重要
要使用加密和解密功能,您需要在JVM中安装完整功能的JCE(默认情况下不包括)。您可以从Oracle 下载“ Java密码学扩展(JCE)无限强度辖区策略文件 ”并按照安装说明进行操作(本质上,您需要用下载的JRE lib / security目录替换这两个策略文件)。
如果远程属性源包含加密的内容(值以{cipher}开头),则在通过HTTP发送给客户端之前,将对它们进行解密。此设置的主要优点是,当属性值处于“ 静止 ”状态时(例如,在git存储库中),不需要使用纯文本格式。如果无法解密某个值,则将其从属性源中删除,并使用相同的密钥添加一个附加属性,但附加前缀为invalid和一个表示“ 不适用 ”的值(通常为<n/a>)。这很大程度上是为了防止将密文用作密码并意外泄漏。
如果为配置客户端应用程序设置了远程配置存储库,则它可能包含与以下内容类似的application.yml:
application.yml。
  1. spring:
  2.   datasource:
  3.     username: dbuser
  4.     password: '{cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ'
复制代码
.properties文件中的加密值不能用引号引起来。否则,该值不会解密。以下示例显示了有效的值:
application.properties。
  1. spring.datasource.username: dbuser
  2. spring.datasource.password: {cipher}FKSAJDFGYOS8F7GLHAKERGFHLSAJ
复制代码
您可以安全地将此纯文本推送到共享的git存储库,并且秘密密码仍然受到保护。
服务器还公开/encrypt和/decrypt端点(假设这些端点是安全的,并且只能由授权代理访问)。如果您编辑远程配置文件,则可以使用Config Server通过POST到/encrypt端点来加密值,如以下示例所示:
  1. $ curl localhost:8888/encrypt -d mysecret
  2. 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
复制代码


如果您加密的值中包含需要URL编码的字符,则应对curl使用--data-urlencode选项以确保它们正确编码。


确保不要在加密值中包含任何curl命令统计信息。将值输出到文件可以帮助避免此问题。
也可以通过/decrypt使用反向操作(前提是服务器配置了对称密钥或完整密钥对),如以下示例所示:
  1. $ curl localhost:8888/decrypt -d 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
  2. mysecret
复制代码


如果使用curl进行测试,请使用--data-urlencode(而不是-d)或设置显式的Content-Type: text/plain以确保在有特殊字符时,curl可以正确编码数据(“ +”特别棘手)。
在将加密的值放入YAML或属性文件之前,以及将其提交并将其推送到远程(可能不安全)存储之前,请获取加密的值并添加{cipher}前缀。
/encrypt和/decrypt端点也都接受/*/{application}/{profiles}形式的路径,当客户端调用主应用程序时,可用于按应用程序(名称)和配置文件控制密码。环境资源。


要以这种精细的方式控制密码,您还必须提供类型为TextEncryptorLocator的@Bean,以按名称和配置文件创建不同的加密器。默认情况下不提供(所有加密使用相同的密钥)。
spring命令行客户端(安装了Spring Cloud CLI扩展名)也可以用于加密和解密,如以下示例所示:
  1. $ spring encrypt mysecret --key foo
  2. 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
  3. $ spring decrypt --key foo 682bc583f4641835fa2db009355293665d2647dade3375c0ee201de2a49f7bda
  4. mysecret
复制代码
要使用文件中的密钥(例如用于加密的RSA公钥),请在密钥值前添加“ @”并提供文件路径,如以下示例所示:
  1. $ spring encrypt mysecret --key @${HOME}/.ssh/id_rsa.pub
  2. AQAjPgt3eFZQXwt8tsHAVv/QHiY5sI2dRcR+...
复制代码


--key参数是强制性的(尽管前缀为--)。
5.5密钥管理

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Config Server可以使用对称(共享)密钥或非对称密钥(RSA密钥对)。非对称选择在安全性方面优越,但使用对称密钥通常更方便,因为它是在bootstrap.properties中配置的单个属性值。
要配置对称密钥,您需要将encrypt.key设置为秘密字符串(或使用ENCRYPT_KEY环境变量将其保留在纯文本配置文件之外)。


您无法使用encrypt.key配置非对称密钥。
要配置非对称密钥,请使用密钥库(例如,由JDK附带的keytool实用程序创建的密钥库)。密钥库属性为encrypt.keyStore.*,其中*等于
Property描述
encrypt.keyStore.locationContains a Resource location
encrypt.keyStore.passwordHolds the password that unlocks the keystore
encrypt.keyStore.aliasIdentifies which key in the store to use
encrypt.keyStore.typeThe type of KeyStore to create. Defaults to jks.
加密是使用公钥完成的,解密需要私钥。因此,原则上,如果只想加密(并准备用私钥在本地解密值),则只能在服务器中配置公钥。实际上,您可能不希望在本地进行解密,因为它会将密钥管理过程分布在所有客户端上,而不是将其集中在服务器上。另一方面,如果您的配置服务器相对不安全并且只有少数客户端需要加密的属性,那么它可能是一个有用的选项。
5.6创建密钥库进行测试

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要创建用于测试的密钥库,可以使用类似于以下内容的命令:
  1. $ keytool -genkeypair -alias mytestkey -keyalg RSA \
  2.   -dname "CN=Web Server,OU=Unit,O=Organization,L=City,S=State,C=US" \
  3.   -keypass changeme -keystore server.jks -storepass letmein
复制代码


使用JDK 11或更高版本时,使用上述命令时可能会收到以下警告。在这种情况下,您可能需要确保keypass和storepass值匹配。
  1. Warning:  Different store and key passwords not supported for PKCS12 KeyStores. Ignoring user-specified -keypass value.
复制代码
将server.jks文件放入类路径中(例如),然后在您的bootstrap.yml中,为Config Server创建以下设置:
  1. encrypt:
  2.   keyStore:
  3.     location: classpath:/server.jks
  4.     password: letmein
  5.     alias: mytestkey
  6.     secret: changeme
复制代码
5.7使用多个按键和按键旋转

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
除了加密属性值中的{cipher}前缀外,Config Server在(Base64编码的)密文开始之前查找零个或多个{name:value}前缀。密钥被传递到TextEncryptorLocator,后者可以执行为密码找到TextEncryptor所需的任何逻辑。如果已配置密钥库(encrypt.keystore.location),则默认定位器将查找具有key前缀提供的别名的密钥,其密文类似于以下内容:
  1. foo:
  2.   bar: `{cipher}{key:testkey}...`
复制代码
定位器查找名为“ testkey”的键。也可以通过在前缀中使用{secret:…}值来提供机密。但是,如果未提供,则默认为使用密钥库密码(这是在构建密钥库且未指定密钥时得到的密码)。如果确实提供了机密,则还应该使用自定义SecretLocator对机密进行加密。
当密钥仅用于加密几个字节的配置数据时(也就是说,它们未在其他地方使用),从密码的角度讲,几乎不需要旋转密钥。但是,您有时可能需要更改密钥(例如,在发生安全漏洞时)。在这种情况下,所有客户端都需要更改其源配置文件(例如,在git中),并在所有密码中使用新的{key:…}前缀。请注意,客户端需要首先检查Config Server密钥库中的密钥别名是否可用。


如果要让Config Server处理所有加密以及解密,则还可以将{name:value}前缀添加为发布到/encrypt端点的纯文本。
5.8服务加密的Properties

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
有时,您希望客户端在本地解密配置,而不是在服务器中进行解密。在这种情况下,如果提供encrypt.*配置来查找密钥,则仍然可以拥有/encrypt和/decrypt端点,但是需要通过放置{10 /来显式关闭对传出属性的解密。 bootstrap.[yml|properties]中的}。如果您不关心端点,则在不配置键或启用标志的情况下都可以使用。
6.提供其他格式

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
来自环境端点的默认JSON格式非常适合Spring应用程序使用,因为它直接映射到Environment抽象。如果愿意,可以通过在资源路径中添加后缀(“ .yml”,“。yaml”或“ .properties”)来使用与YAML或Java属性相同的数据。对于不关心JSON终结点的结构或它们提供的额外元数据的应用程序来说,这可能很有用(例如,不使用Spring的应用程序可能会受益于此方法的简单性)。
YAML和属性表示形式还有一个附加标志(提供为名为resolvePlaceholders的布尔查询参数),用于指示应在输出中解析源文档中的占位符(以标准Spring ${…}格式)在渲染之前,如果可能的话。对于不了解Spring占位符约定的消费者来说,这是一个有用的功能。


使用YAML或属性格式存在一些限制,主要是与元数据的丢失有关。例如,JSON被构造为属性源的有序列表,其名称与该源相关。即使值的来源有多个来源,YAML和属性形式也会合并到一个映射中,并且原始来源文件的名称也会丢失。同样,YAML表示也不一定是后备存储库中YAML源的忠实表示。它由一系列平面属性来源构成,并且必须对密钥的形式进行假设。
7.提供纯文本

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
除了使用Environment抽象(或YAML或属性格式的抽象表示之一)之外,您的应用程序可能需要根据其环境量身定制的通用纯文本配置文件。Config Server通过位于/{application}/{profile}/{label}/{path}的附加终结点提供了这些终结点,其中application,profile和label与常规环境终结点具有相同的含义,但是path是指向以下环境的路径文件名(例如log.xml)。该端点的源文件与环境端点的定位方式相同。属性和YAML文件使用相同的搜索路径。但是,不是汇总所有匹配资源,而是仅返回第一个要匹配的资源。
找到资源后,可通过对提供的应用程序名称,配置文件和标签使用有效的Environment来解析常规格式(${…})的占位符。通过这种方式,资源端点与环境端点紧密集成在一起。考虑以下用于GIT或SVN存储库的示例:
  1. application.yml
  2. nginx.conf
复制代码
nginx.conf如下所示:
  1. server {
  2.     listen              80;
  3.     server_name         ${nginx.server.name};
  4. }
复制代码
和application.yml像这样:
  1. nginx:
  2.   server:
  3.     name: example.com
  4. ---
  5. spring:
  6.   profiles: development
  7. nginx:
  8.   server:
  9.     name: develop.com
复制代码
/foo/default/master/nginx.conf资源可能如下:
  1. server {
  2.     listen              80;
  3.     server_name         example.com;
  4. }
复制代码
和/foo/development/master/nginx.conf像这样:
  1. server {
  2.     listen              80;
  3.     server_name         develop.com;
  4. }
复制代码


与用于环境配置的源文件一样,profile用于解析文件名。因此,如果要使用特定于配置文件的文件,可以通过名为logback-development.xml(优先于logback.xml)的文件来解析/*/development/*/logback.xml。


如果不想提供label并让服务器使用默认标签,则可以提供useDefaultLabel请求参数。因此,default配置文件的前面的示例可以是/foo/default/nginx.conf?useDefaultLabel。
8.嵌入配置服务器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Config Server最好作为独立应用程序运行。但是,如果需要,可以将其嵌入另一个应用程序。为此,请使用@EnableConfigServer批注。在这种情况下,名为spring.cloud.config.server.bootstrap的可选属性会很有用。它是一个标志,用于指示服务器是否应从其自己的远程存储库中进行配置。默认情况下,该标志为关闭状态,因为它会延迟启动。但是,当嵌入到另一个应用程序中时,以与其他任何应用程序相同的方式进行初始化是有意义的。将spring.cloud.config.server.bootstrap设置为true时,还必须使用复合环境存储库配置。例如
  1. spring:
  2.   application:
  3.     name: configserver
  4.   profiles:
  5.     active: composite
  6.   cloud:
  7.     config:
  8.       server:
  9.         composite:
  10.           - type: native
  11.             search-locations: ${HOME}/Desktop/config
  12.         bootstrap: true
复制代码


如果使用引导标志,则配置服务器需要在bootstrap.yml中配置其名称和存储库URI。
要更改服务器端点的位置,可以(可选)设置spring.cloud.config.server.prefix(例如,/config)以在前缀下提供资源。前缀应以/开头,但不能以/结尾。它应用于Config Server中的@RequestMappings(即,在Spring Boot server.servletPath和server.contextPath前缀之下)。
如果要直接从后端存储库(而不是从配置服务器)读取应用程序的配置,则基本上需要没有端点的嵌入式配置服务器。您可以不使用@EnableConfigServer注释(设置为spring.cloud.config.server.bootstrap=true)来完全关闭端点。
9.推送通知和Spring Cloud Bus

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
许多源代码存储库提供程序(例如Github,Gitlab,Gitea,Gitee,Gogs或Bitbucket)都通过Webhook通知您存储库中的更改。您可以通过提供者的用户界面将Webhook配置为URL和您感兴趣的一组事件。例如,Github使用POST到Webhook,其JSON主体包含提交列表和设置为push的标头(X-Github-Event)。如果在spring-cloud-config-monitor库上添加依赖项并在Config Server中激活Spring Cloud Bus,则会启用/monitor端点。
激活Webhook后,配置服务器将发送一个针对它认为可能已更改的应用程序的RefreshRemoteApplicationEvent。变化检测可以被策略化。但是,默认情况下,它会查找与应用程序名称匹配的文件中的更改(例如,foo.properties面向foo应用程序,而application.properties面向所有应用程序)。当您要覆盖此行为时,使用的策略是PropertyPathNotificationExtractor,该策略接受请求标头和正文作为参数,并返回已更改文件路径的列表。
默认配置可以与Github,Gitlab,Gitea,Gitee,Gogs或Bitbucket一起使用。除了来自Github,Gitlab,Gitee或Bitbucket的JSON通知之外,您还可以通过使用path={application}模式的形式编码的正文参数POST到/monitor来触发更改通知。这样做会向匹配{application}模式(可以包含通配符)的应用程序广播。


仅当在配置服务器和客户端应用程序中都激活了spring-cloud-bus时,才发送RefreshRemoteApplicationEvent。


默认配置还检测本地git存储库中的文件系统更改。在这种情况下,不使用Webhook。但是,一旦您编辑配置文件,就会广播刷新。
10. Spring Cloud Config客户

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Boot应用程序可以立即利用Spring Config Server(或应用程序开发人员提供的其他外部属性源)。它还选择了与Environment更改事件相关的一些其他有用功能。
10.1配置第一个引导程序

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
在类路径上具有Spring Cloud Config客户端的任何应用程序的默认行为如下:配置客户端启动时,它将绑定到配置服务器(通过spring.cloud.config.uri引导程序配置属性)并初始化Spring Environment(带有远程资源来源)。
此行为的最终结果是,所有要使用Config Server的客户端应用程序都需要一个bootstrap.yml(或环境变量),其服务器地址设置为spring.cloud.config.uri(默认为“ http:// localhost” :8888“)。
10.2发现第一引导程序

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果使用DiscoveryClient实现,例如Spring Cloud Netflix和Eureka Service Discovery或Spring Cloud Consul,则可以让Config Server向Discovery Service注册。但是,在默认的“ Config First ”模式下,客户端无法利用注册。
如果您更喜欢使用DiscoveryClient来查找配置服务器,则可以通过设置spring.cloud.config.discovery.enabled=true(默认值为false)来进行。这样做的最终结果是,所有客户端应用程序都需要具有适当发现配置的bootstrap.yml(或环境变量)。例如,对于Spring Cloud Netflix,您需要定义Eureka服务器地址(例如,在eureka.client.serviceUrl.defaultZone中)。使用此选项的价格是启动时需要进行额外的网络往返,以查找服务注册。好处是,只要发现服务是固定点,配置服务器就可以更改其坐标。默认服务ID是configserver,但是您可以通过设置spring.cloud.config.discovery.serviceId在客户端上(以及在服务器上,以一种通常的服务方式,例如通过设置spring.application.name)来更改该ID。
发现客户端实现均支持某种元数据映射(例如,对于Eureka,我们有eureka.instance.metadataMap)。Config Server的某些其他属性可能需要在其服务注册元数据中进行配置,以便客户端可以正确连接。如果Config Server受HTTP Basic保护,则可以将凭据配置为user和password。另外,如果Config Server具有上下文路径,则可以设置configPath。例如,以下YAML文件适用于作为Eureka客户端的Config Server:
bootstrap.yml。
  1. eureka:
  2.   instance:
  3.     ...
  4.     metadataMap:
  5.       user: osufhalskjrtl
  6.       password: lviuhlszvaorhvlo5847
  7.       configPath: /config
复制代码
10.3配置客户端快速失败

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
在某些情况下,如果服务无法连接到Config Server,您可能希望启动失败。如果这是期望的行为,请设置引导程序配置属性spring.cloud.config.fail-fast=true,以使客户端因Exception而停止。
10.4配置客户端重试

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果您希望配置服务器在您的应用程序启动时偶尔会不可用,则可以使其在失败后继续尝试。首先,您需要设置spring.cloud.config.fail-fast=true。然后,您需要将spring-retry和spring-boot-starter-aop添加到类路径中。默认行为是重试六次,初始回退间隔为1000ms,随后的回退的指数乘数为1.1。您可以通过设置spring.cloud.config.retry.*配置属性来配置这些属性(和其他属性)。


要完全控制重试行为,请添加ID为configServerRetryInterceptor的@Bean类型的@Bean。Spring重试有一个RetryInterceptorBuilder,它支持创建一个。
10.5查找远程配置资源

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Config Service提供来自/{application}/{profile}/{label}的属性源,其中客户端应用程序中的默认绑定如下:
    “名称” = ${spring.application.name}“个人资料” = ${spring.profiles.active}(实际上是Environment.getActiveProfiles())“ label” =“大师”


设置属性${spring.application.name}时,请勿在您的应用名称前加上保留字application-,以防止解析正确的属性源时出现问题。
您可以通过设置spring.cloud.config.*(其中*为name,profile或label)来覆盖所有参数。label对于回滚到以前的配置版本很有用。使用默认的Config Server实现,它可以是git标签,分支名称或提交ID。标签也可以以逗号分隔的列表形式提供。在这种情况下,列表中的项目将一一尝试直到成功为止。在要素分支上工作时,此行为可能很有用。例如,您可能想使配置标签与分支对齐,但使其成为可选(在这种情况下,请使用spring.cloud.config.label=myfeature,develop)。
10.6为配置服务器指定多个地址

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
为确保在部署了Config Server的多个实例时并希望不时有一个或多个实例不可用时的高可用性,可以指定多个URL(作为spring.cloud.config.uri属性下的逗号分隔列表)或您的所有实例都在服务注册表中注册,例如Eureka(如果使用Discovery-First Bootstrap模式)。请注意,只有在未运行Config Server时(即,应用程序退出时)或发生连接超时时,这样做才能确保高可用性。例如,如果Config Server返回500(内部服务器错误)响应,或者Config Client从Config Server收到401(由于凭据错误或其他原因),则Config Client不会尝试从其他URL提取属性。此类错误表示用户问题,而不是可用性问题。
如果您在Config Server上使用HTTP基本安全性,则仅当将凭据嵌入在spring.cloud.config.uri属性下指定的每个URL中时,当前才有可能支持per-Config Server身份验证凭据。如果使用任何其他类型的安全性机制,则(当前)不能支持每台配置服务器的身份验证和授权。
10.7配置超时

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果要配置超时阈值:
    可以使用属性spring.cloud.config.request-read-timeout配置读取超时。可以使用属性spring.cloud.config.request-connect-timeout配置连接超时。
10.8安全性

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果在服务器上使用HTTP基本安全性,则客户端需要知道密码(如果不是默认用户名,则需要用户名)。您可以通过配置服务器URI或通过单独的用户名和密码属性来指定用户名和密码,如以下示例所示:
bootstrap.yml。
  1. spring:
  2.   cloud:
  3.     config:
  4.      uri: https://user:secret@myconfig.mycompany.com
复制代码
以下示例显示了传递相同信息的另一种方法:
bootstrap.yml。
  1. spring:
  2.   cloud:
  3.     config:
  4.      uri: https://myconfig.mycompany.com
  5.      username: user
  6.      password: secret
复制代码
spring.cloud.config.password和spring.cloud.config.username值会覆盖URI中提供的任何内容。
如果您在Cloud Foundry上部署应用程序,则提供密码的最佳方法是通过服务凭据(例如URI中的密码),因为它不需要在配置文件中。以下示例在本地工作,并且适用于名为configserver的Cloud Foundry上的用户提供的服务:
bootstrap.yml。
  1. spring:
  2.   cloud:
  3.     config:
  4.      uri: ${vcap.services.configserver.credentials.uri:http://user:password@localhost:8888}
复制代码
如果您使用另一种形式的安全性,则可能需要向ConfigServicePropertySourceLocator提供一个RestTemplate(例如,通过在引导上下文中进行抓取并将其注入)。ConfigServicePropertySourceLocator提供一个{848 /}(例如,通过在引导上下文中进行抓取并将其注入)。
10.8.1健康指标

Config Client提供Spring Boot运行状况指示器,该指示器尝试从Config Server加载配置。可以通过设置health.config.enabled=false禁用运行状况指示器。由于性能原因,响应也被缓存。默认的生存时间为5分钟。要更改该值,请设置health.config.time-to-live属性(以毫秒为单位)。
10.8.2提供自定义的RestTemplate

在某些情况下,您可能需要自定义来自客户端对配置服务器的请求。通常,这样做涉及传递特殊的Authorization标头以验证对服务器的请求。提供自定义RestTemplate:
    创建一个新的配置bean,实现为PropertySourceLocator,如以下示例所示:
CustomConfigServiceBootstrapConfiguration.java。
  1. @Configuration
  2. public class CustomConfigServiceBootstrapConfiguration {
  3.     @Bean
  4.     public ConfigServicePropertySourceLocator configServicePropertySourceLocator() {
  5.         ConfigClientProperties clientProperties = configClientProperties();
  6.        ConfigServicePropertySourceLocator configServicePropertySourceLocator =  new ConfigServicePropertySourceLocator(clientProperties);
  7.         configServicePropertySourceLocator.setRestTemplate(customRestTemplate(clientProperties));
  8.         return configServicePropertySourceLocator;
  9.     }
  10. }
复制代码
    在resources/META-INF中,创建一个名为spring.factories的文件并指定您的自定义配置,如以下示例所示:
spring.factories.
  1. org.springframework.cloud.bootstrap.BootstrapConfiguration = com.my.config.client.CustomConfigServiceBootstrapConfiguration
复制代码
10.8.3 Vault

当使用Vault作为配置服务器的后端时,客户端需要为服务器提供令牌以从Vault中检索值。可以通过在bootstrap.yml中设置spring.cloud.config.token在客户端中提供此令牌,如以下示例所示:
bootstrap.yml。
  1. spring:
  2.   cloud:
  3.     config:
  4.       token: YourVaultToken
复制代码
10.9 Vault中的嵌套键

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Vault支持将键嵌套在Vault中存储的值中的功能,如以下示例所示:
  1. echo -n '{"appA": {"secret": "appAsecret"}, "bar": "baz"}' | vault write secret/myapp -
复制代码
此命令将JSON对象写入您的Vault。要访问Spring中的这些值,您将使用传统的dot(.)批注,如以下示例所示
  1. @Value("${appA.secret}")
  2. String name = "World";
复制代码
前面的代码会将name变量的值设置为appAsecret。
第三部分 Spring Cloud Netflix

Greenwich SR5
该项目通过自动配置并绑定到Spring环境和其他Spring编程模型习惯用法,为Spring Boot应用提供了Netflix OSS集成。使用一些简单的批注,您可以快速启用和配置应用程序内部的通用模式,并使用经过测试的Netflix组件构建大型分布式系统。提供的模式包括服务发现(Eureka),断路器(Hystrix),智能路由(Zuul)和客户端负载平衡(Ribbon)。
11.服务发现:Eureka个客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
服务发现是基于微服务的体系结构的主要宗旨之一。尝试手动配置每个客户端或某种形式的约定可能很困难并且很脆弱。Eureka是Netflix Service Discovery服务器和客户端。可以将服务器配置和部署为高可用性,每个服务器将有关已注册服务的状态复制到其他服务器。
11.1如何包括Eureka客户

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要将Eureka客户端包括在您的项目中,请使用启动器,其组ID为org.springframework.cloud,工件ID为spring-cloud-starter-netflix-eureka-client。有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面。
11.2向Eureka注册

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
当客户端向Eureka注册时,它会提供有关其自身的元数据-例如主机,端口,运行状况指示器URL,主页和其他详细信息。Eureka从属于服务的每个实例接收心跳消息。如果心跳在可配置的时间表上进行故障转移,则通常会将实例从注册表中删除。
以下示例显示了最小的Eureka客户端应用程序:
  1. @SpringBootApplication
  2. @RestController
  3. public class Application {
  4.     @RequestMapping("/")
  5.     public String home() {
  6.         return "Hello world";
  7.     }
  8.     public static void main(String[] args) {
  9.         new SpringApplicationBuilder(Application.class).web(true).run(args);
  10.     }
  11. }
复制代码
请注意,前面的示例显示了普通的Spring Boot应用程序。通过在类路径上使用spring-cloud-starter-netflix-eureka-client,您的应用程序将自动在Eureka服务器中注册。如下例所示,需要进行配置才能找到Eureka服务器:
application.yml。
  1. eureka:
  2.   client:
  3.     serviceUrl:
  4.       defaultZone: http://localhost:8761/eureka/
复制代码
在前面的示例中,defaultZone是一个魔术字符串后备值,它为任何不表达首选项的客户端提供服务URL(换句话说,这是一个有用的默认值)。


defaultZone属性区分大小写,并且需要使用驼峰式大小写,因为serviceUrl属性是Map<String, String>。因此,defaultZone属性不遵循default-zone的常规Spring Boot蛇形惯例。
默认应用程序名称(即服务ID),虚拟主机和非安全端口(从Environment获取)分别为${spring.application.name},${spring.application.name}和${server.port}。
在类路径上具有spring-cloud-starter-netflix-eureka-client可使应用程序同时进入Eureka “ 实例 ”(即,它自己注册)和“ 客户端 ”(它可以查询注册表以定位其他服务)。实例行为由eureka.instance.*配置键驱动,但是如果确保您的应用程序具有spring.application.name的值(这是Eureka服务ID或VIP的默认值),则默认值很好。
有关可配置选项的更多详细信息,请参见EurekaInstanceConfigBean和EurekaClientConfigBean。
要禁用Eureka Discovery Client,可以将eureka.client.enabled设置为false。当spring.cloud.discovery.enabled设置为false时,Eureka Discovery Client也将被禁用。
11.3通过Eureka服务器进行身份验证

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果其中一个eureka.client.serviceUrl.defaultZone URL内嵌了凭据,则HTTP基本身份验证会自动添加到您的eureka客户端(卷曲样式,如下:http://user:password@localhost:8761/eureka)。对于更复杂的需求,您可以创建类型为DiscoveryClientOptionalArgs的@Bean并将ClientFilter实例注入其中,所有这些实例都应用于从客户端到服务器的调用。


由于Eureka中的限制,无法支持每服务器的基本身份验证凭据,因此仅使用找到的第一组凭据。
11.4状态页和运行状况指示器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Eureka实例的状态页和运行状况指示器分别默认为/info和/health,这是Spring Boot Actuator应用程序中有用端点的默认位置。即使您使用非默认上下文路径或Servlet路径(例如server.servletPath=/custom),也需要更改这些内容,即使对于Actuator应用程序也是如此。下面的示例显示两个设置的默认值:
application.yml。
  1. eureka:
  2.   instance:
  3.     statusPageUrlPath: ${server.servletPath}/info
  4.     healthCheckUrlPath: ${server.servletPath}/health
复制代码
这些链接显示在客户端使用的元数据中,并在某些情况下用于确定是否将请求发送到您的应用程序,因此,如果请求准确,将很有帮助。


在Dalston中,还需要在更改该管理上下文路径时设置状态和运行状况检查URL。从Edgware开始就删除了此要求。
11.5注册安全的应用程序

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果您希望通过HTTPS与您的应用进行联系,则可以在EurekaInstanceConfig中设置两个标志:
    eureka.instance.[nonSecurePortEnabled]=[false]eureka.instance.[securePortEnabled]=[true]
这样做会使Eureka发布实例信息,该实例信息显示出对安全通信的明确偏好。对于以这种方式配置的服务,Spring Cloud DiscoveryClient始终返回以https开头的URI。同样,以这种方式配置服务时,Eureka(本机)实例信息具有安全的运行状况检查URL。
由于Eureka在内部工作的方式,它仍然会为状态和主页发布非安全URL,除非您也明确地覆盖了它们。您可以使用占位符来配置eureka实例URL,如以下示例所示:
application.yml。
  1. eureka:
  2.   instance:
  3.     statusPageUrl: https://${eureka.hostname}/info
  4.     healthCheckUrl: https://${eureka.hostname}/health
  5.     homePageUrl: https://${eureka.hostname}/
复制代码
(请注意,${eureka.hostname}是本机占位符,仅在Eureka的更高版本中可用。您也可以使用Spring占位符来实现相同的目的,例如,使用${eureka.instance.hostName}。)


如果您的应用程序在代理后面运行,并且SSL终止在代理中(例如,如果您在Cloud Foundry或其他平台中作为服务运行),则需要确保拦截代理的“ 转发 ”标头并由应用处理。如果嵌入在Spring Boot应用程序中的Tomcat容器具有针对’X-Forwarded-\ *'标头的显式配置,则此操作自动发生。应用程序提供的指向自身的链接错误(错误的主机,端口或协议)表明此配置错误。
11.6 Eureka的健康检查

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
默认情况下,Eureka使用客户端心跳来确定客户端是否启动。除非另有说明,否则发现客户端不会根据Spring Boot Actuator传播应用程序的当前运行状况检查状态。因此,在成功注册之后,Eureka始终宣布该应用程序处于“启动”状态。可以通过启用Eureka运行状况检查来更改此行为,这将导致应用程序状态传播到Eureka。结果,所有其他应用程序都不会将流量发送到处于“ UP”状态以外的其他状态的应用程序。以下示例显示如何为客户端启用运行状况检查:
application.yml。
  1. eureka:
  2.   client:
  3.     healthcheck:
  4.       enabled: true
复制代码


eureka.client.healthcheck.enabled=true仅应在application.yml中设置。在bootstrap.yml中设置该值会导致不良的副作用,例如在Eureka中以UNKNOWN状态注册。
如果您需要对运行状况检查进行更多控制,请考虑实施自己的com.netflix.appinfo.HealthCheckHandler。
11.7 Eureka实例和客户端的元数据

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
值得花费一些时间来了解Eureka元数据的工作方式,因此您可以在平台上使用有意义的方式来使用它。有用于信息的标准元数据,例如主机名,IP地址,端口号,状态页和运行状况检查。这些都发布在服务注册表中,并由客户端用于以直接方式联系服务。可以将其他元数据添加到eureka.instance.metadataMap中的实例注册中,并且可以在远程客户端中访问此元数据。通常,除非使客户端知道元数据的含义,否则其他元数据不会更改客户端的行为。有几种特殊情况,在本文档的后面部分进行介绍,其中Spring Cloud已经为元数据映射分配了含义。
11.7.1在Cloud Foundry上使用Eureka

Cloud Foundry具有全局路由器,因此同一应用程序的所有实例都具有相同的主机名(其他具有类似体系结构的PaaS解决方案具有相同的排列)。这不一定是使用Eureka的障碍。但是,如果您使用路由器(建议或什至是强制性的,具体取决于平台的设置方式),则需要显式设置主机名和端口号(安全或不安全),以便它们使用路由器。您可能还希望使用实例元数据,以便可以区分客户端上的实例(例如,在自定义负载平衡器中)。默认情况下,eureka.instance.instanceId为vcap.application.instance_id,如以下示例所示:
application.yml。
  1. eureka:
  2.   instance:
  3.     hostname: ${vcap.application.uris[0]}
  4.     nonSecurePort: 80
复制代码
根据在Cloud Foundry实例中设置安全规则的方式,您可能可以注册并使用主机VM的IP地址进行直接的服务到服务的调用。Pivotal Web服务(PWS)尚不提供此功能。
11.7.2在AWS上使用Eureka

如果计划将应用程序部署到AWS云,则必须将Eureka实例配置为可感知AWS。您可以通过如下自定义EurekaInstanceConfigBean来实现:
  1. @Bean
  2. @Profile("!default")
  3. public EurekaInstanceConfigBean eurekaInstanceConfig(InetUtils inetUtils) {
  4.   EurekaInstanceConfigBean b = new EurekaInstanceConfigBean(inetUtils);
  5.   AmazonInfo info = AmazonInfo.Builder.newBuilder().autoBuild("eureka");
  6.   b.setDataCenterInfo(info);
  7.   return b;
  8. }
复制代码
11.7.3更改Eureka实例ID

一个普通的Netflix Eureka实例注册的ID等于其主机名(即,每个主机仅提供一项服务)。Spring Cloud Eureka提供了明智的默认值,其定义如下:
  1. ${spring.cloud.client.hostname}:${spring.application.name}:${spring.application.instance_id:${server.port}}}
复制代码
一个示例是myhost:myappname:8080。
通过使用Spring Cloud,可以通过在eureka.instance.instanceId中提供唯一标识符来覆盖此值,如以下示例所示:
application.yml。
  1. eureka:
  2.   instance:
  3.     instanceId: ${spring.application.name}:${vcap.application.instance_id:${spring.application.instance_id:${random.value}}}
复制代码
通过前面示例中显示的元数据和在本地主机上部署的多个服务实例,在其中插入随机值以使实例唯一。在Cloud Foundry中,vcap.application.instance_id是在Spring Boot应用程序中自动填充的,因此不需要随机值。
11.8使用EurekaClient

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
一旦拥有作为发现客户端的应用程序,就可以使用它从Eureka服务器发现服务实例。一种方法是使用本地com.netflix.discovery.EurekaClient(而不是Spring Cloud DiscoveryClient),如以下示例所示:
  1. @Autowired
  2. private EurekaClient discoveryClient;
  3. public String serviceUrl() {
  4.     InstanceInfo instance = discoveryClient.getNextServerFromEureka("STORES", false);
  5.     return instance.getHomePageUrl();
  6. }
复制代码


请勿在@PostConstruct方法或@Scheduled方法(或可能尚未启动ApplicationContext的任何地方)中使用EurekaClient。它是通过SmartLifecycle(带有phase=0)进行初始化的,因此最早可以依靠它的是处于更高阶段的另一个SmartLifecycle。
11.8.1没有Jersey的EurekaClient

默认情况下,EurekaClient使用Jersey进行HTTP通信。如果希望避免来自Jersey的依赖关系,可以将其从依赖关系中排除。Spring Cloud基于Spring RestTemplate自动配置传输客户端。以下示例显示Jersey被排除在外:
  1. <dependency>
  2.     <groupId>org.springframework.cloud</groupId>
  3.     <artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
  4.     <exclusions>
  5.         <exclusion>
  6.             <groupId>com.sun.jersey</groupId>
  7.             <artifactId>jersey-client</artifactId>
  8.         </exclusion>
  9.         <exclusion>
  10.             <groupId>com.sun.jersey</groupId>
  11.             <artifactId>jersey-core</artifactId>
  12.         </exclusion>
  13.         <exclusion>
  14.             <groupId>com.sun.jersey.contribs</groupId>
  15.             <artifactId>jersey-apache-client4</artifactId>
  16.         </exclusion>
  17.     </exclusions>
  18. </dependency>
复制代码
11.9替代本机Netflix EurekaClient

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您无需使用原始Netflix EurekaClient。而且,通常在某种包装器后面使用它会更方便。Spring Cloud 通过逻辑Eureka服务标识符(VIP)而非物理URL 支持Feign(REST客户端生成器)和Spring RestTemplate。要使用固定的物理服务器列表配置Ribbon,可以将<client>.ribbon.listOfServers设置为以逗号分隔的物理地址(或主机名)列表,其中<client>是客户端的ID。
您还可以使用org.springframework.cloud.client.discovery.DiscoveryClient,它为发现客户端提供一个简单的API(非Netflix专用),如以下示例所示:
  1. @Autowired
  2. private DiscoveryClient discoveryClient;
  3. public String serviceUrl() {
  4.     List<ServiceInstance> list = discoveryClient.getInstances("STORES");
  5.     if (list != null && list.size() > 0 ) {
  6.         return list.get(0).getUri();
  7.     }
  8.     return null;
  9. }
复制代码
11.10为什么注册服务这么慢?

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
成为实例还涉及到注册表的定期心跳(通过客户端的serviceUrl),默认持续时间为30秒。直到实例,服务器和客户端在其本地缓存中都具有相同的元数据后,客户端才能发现该服务(因此可能需要3个心跳)。您可以通过设置eureka.instance.leaseRenewalIntervalInSeconds来更改周期。将其设置为小于30的值可以加快使客户端连接到其他服务的过程。在生产中,最好使用默认值,因为服务器中的内部计算对租约续订期进行了假设。
11.11区域

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果您已将Eureka客户端部署到多个区域,则您可能希望这些客户端在尝试使用其他区域中的服务之前先使用同一区域中的服务。要进行设置,您需要正确配置Eureka客户端。
首先,您需要确保已将Eureka服务器部署到每个区域,并且它们彼此对等。有关 更多信息,请参见区域和区域部分。
接下来,您需要告诉Eureka服务位于哪个区域。您可以使用metadataMap属性来做到这一点。例如,如果将service 1部署到zone 1和zone 2上,则需要在service 1中设置以下Eureka属性:
1区服务1
  1. eureka.instance.metadataMap.zone = zone1
  2. eureka.client.preferSameZoneEureka = true
复制代码
2区服务1
  1. eureka.instance.metadataMap.zone = zone2
  2. eureka.client.preferSameZoneEureka = true
复制代码
11.12刷新Eureka个客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
默认情况下,EurekaClient bean是可刷新的,这意味着Eureka客户端属性可以更改和刷新。发生刷新时,客户端将从Eureka服务器中注销,并且可能会在短暂的时间内不提供给定服务的所有实例。消除这种情况的一种方法是禁用刷新Eureka客户端的功能。为此,请设置eureka.client.refresh.enable=false。
12.服务发现:Eureka服务器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
本节介绍如何设置Eureka服务器。
12.1如何包括Eureka服务器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要将Eureka服务器包含在您的项目中,请使用启动器,其组ID为org.springframework.cloud,工件ID为spring-cloud-starter-netflix-eureka-server。有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面。


如果您的项目已经使用Thymeleaf作为模板引擎,则Eureka服务器的Freemarker模板可能无法正确加载。在这种情况下,必须手动配置模板加载器:
application.yml。
  1. spring:
  2.   freemarker:
  3.     template-loader-path: classpath:/templates/
  4.     prefer-file-system-access: false
复制代码
12.2如何运行Eureka服务器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
以下示例显示了最小的Eureka服务器:
  1. @SpringBootApplication
  2. @EnableEurekaServer
  3. public class Application {
  4.     public static void main(String[] args) {
  5.         new SpringApplicationBuilder(Application.class).web(true).run(args);
  6.     }
  7. }
复制代码
该服务器具有一个主页,其中包含UI和HTTP API端点,用于/eureka/*下的常规Eureka功能。
以下链接提供了一些Eureka背景知识:磁通电容器和google小组讨论。


由于Gradle的依赖性解析规则以及缺少父bom功能,取决于spring-cloud-starter-netflix-eureka-server可能会导致应用程序启动失败。要解决此问题,请添加Spring Boot Gradle插件并按如下所示导入Spring Cloud Starter父Bom:build.gradle。buildscript { dependencies { classpath("org.springframework.boot:spring-boot-gradle-plugin:{spring-boot-docs-version}") } } apply plugin: "spring-boot" dependencyManagement { imports { mavenBom "org.springframework.cloud:spring-cloud-dependencies:{spring-cloud-version}" } }
12.3高可用性,区域和区域

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Eureka服务器没有后端存储,但是注册表中的所有服务实例都必须发送心跳信号以使其注册保持最新(因此可以在内存中完成)。客户端还具有Eureka注册的内存缓存(因此,对于每个对服务的请求,它们都不必转到注册表)。
默认情况下,每个Eureka服务器也是Eureka客户端,并且需要(至少一个)服务URL来定位对等方。如果您不提供该服务,则该服务将运行并运行,但是它将使您的日志充满关于无法向对等方注册的噪音。
另请参阅下面的内容,了解客户端在Ribbon中对区域和区域的支持。
12.4独立模式

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
只要存在某种监视器或弹性运行时(例如Cloud Foundry),两个缓存(客户端和服务器)和心跳的组合就可以使独立的Eureka服务器对故障具有相当的恢复能力。在独立模式下,您可能希望关闭客户端行为,以使其不会继续尝试并无法到达其对等对象。下面的示例演示如何关闭客户端行为:
application.yml(独立Eureka服务器)。
  1. server:
  2.   port: 8761
  3. eureka:
  4.   instance:
  5.     hostname: localhost
  6.   client:
  7.     registerWithEureka: false
  8.     fetchRegistry: false
  9.     serviceUrl:
  10.       defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
复制代码
请注意,serviceUrl指向与本地实例相同的主机。
12.5同行意识

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
通过运行多个实例并要求它们彼此注册,可以使Eureka更具弹性和可用性。实际上,这是默认行为,因此要使其正常工作,您需要做的就是向对等体添加有效的serviceUrl,如以下示例所示:
application.yml(两个对等感知Eureka服务器)。
  1. ---
  2. spring:
  3.   profiles: peer1
  4. eureka:
  5.   instance:
  6.     hostname: peer1
  7.   client:
  8.     serviceUrl:
  9.       defaultZone: http://peer2/eureka/
  10. ---
  11. spring:
  12.   profiles: peer2
  13. eureka:
  14.   instance:
  15.     hostname: peer2
  16.   client:
  17.     serviceUrl:
  18.       defaultZone: http://peer1/eureka/
复制代码
在前面的示例中,我们有一个YAML文件,该文件可以通过在不同的Spring配置文件中运行,在两个主机(peer1和peer2)上运行同一服务器。您可以通过操纵/etc/hosts解析主机名来使用此配置来测试单个主机上的对等感知(在生产环境中这样做没有太大价值)。实际上,如果您在知道其主机名的计算机上运行,则不需要eureka.instance.hostname(默认情况下,使用java.net.InetAddress进行查找)。
您可以将多个对等方添加到系统,并且只要它们都通过至少一个边缘相互连接,它们就可以在彼此之间同步注册。如果对等方在物理上是分开的(在一个数据中心内部或在多个数据中心之间),则该系统原则上可以解决“ 裂脑 ”型故障。您可以将多个对等方添加到系统中,并且只要它们都直接相互连接,它们就可以在彼此之间同步注册。
application.yml(三个对等感知Eureka服务器)。
  1. eureka:
  2.   client:
  3.     serviceUrl:
  4.       defaultZone: http://peer1/eureka/,http://peer2/eureka/,http://peer3/eureka/
  5. ---
  6. spring:
  7.   profiles: peer1
  8. eureka:
  9.   instance:
  10.     hostname: peer1
  11. ---
  12. spring:
  13.   profiles: peer2
  14. eureka:
  15.   instance:
  16.     hostname: peer2
  17. ---
  18. spring:
  19.   profiles: peer3
  20. eureka:
  21.   instance:
  22.     hostname: peer3
复制代码
12.6何时首选IP地址

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
在某些情况下,Eureka最好公布服务的IP地址而不是主机名。将eureka.instance.preferIpAddress设置为true,并且当应用程序向eureka注册时,它将使用其IP地址而不是其主机名。


如果Java无法确定主机名,则IP地址将发送到Eureka。设置主机名的唯一明确方法是设置eureka.instance.hostname属性。您可以在运行时使用环境变量(例如,eureka.instance.hostname=${HOST_NAME})设置主机名。
12.7保护Eureka服务器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您只需通过spring-boot-starter-security将Spring Security添加到服务器的类路径中即可保护Eureka服务器。默认情况下,当Spring Security在类路径上时,它将要求在每次向应用程序发送请求时都发送有效的CSRF令牌。Eureka客户通常不会拥有有效的跨站点请求伪造(CSRF)令牌,您需要为/eureka/**端点禁用此要求。例如:
  1. @EnableWebSecurity
  2. class WebSecurityConfig extends WebSecurityConfigurerAdapter {
  3.     @Override
  4.     protected void configure(HttpSecurity http) throws Exception {
  5.         http.csrf().ignoringAntMatchers("/eureka/**");
  6.         super.configure(http);
  7.     }
  8. }
复制代码
有关CSRF的更多信息,请参见Spring Security文档。
可以在Spring Cloud示例存储库中找到Eureka演示服务器。
12.8 JDK 11支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Eureka服务器所依赖的JAXB模块已在JDK 11中删除。如果要在运行Eureka服务器时使用JDK 11,则必须在POM或Gradle文件中包括这些依赖项。
  1. <dependency>
  2.         <groupId>org.glassfish.jaxb</groupId>
  3.         <artifactId>jaxb-runtime</artifactId>
  4. </dependency>
复制代码
13.断路器:Hystrix个客户

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Netflix创建了一个名为Hystrix的库,该库实现了断路器模式。在微服务架构中,通常有多个服务调用层,如以下示例所示:
图13.1。微服务图
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JsRltcgC-1591774641413)(https://www.springcloud.cc/images/Hystrix.png)]
较低级别的服务中的服务故障可能会导致级联故障,直至用户。在metrics.rollingStats.timeInMilliseconds定义的滚动窗口中,当对特定服务的调用超过circuitBreaker.requestVolumeThreshold(默认:20个请求)并且失败百分比大于circuitBreaker.errorThresholdPercentage(默认:> 50%)时(默认:10秒) ),则电路断开并且无法进行呼叫。在错误和断路的情况下,开发人员可以提供备用功能。
图13.2 Hystrix后备可防止级联故障


开路可停止级联故障,并让不堪重负的服务时间得以恢复。后备可以是另一个受Hystrix保护的呼叫,静态数据或合理的空值。可以将回退链接在一起,以便第一个回退进行其他业务调用,然后回退到静态数据。
13.1如何包括Hystrix

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要将Hystrix包含在您的项目中,请使用起始者,其组ID为org.springframework.cloud,工件ID为spring-cloud-starter-netflix-hystrix。有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面。
以下示例显示了具有Hystrix断路器的最小Eureka服务器:
  1. @SpringBootApplication
  2. @EnableCircuitBreaker
  3. public class Application {
  4.     public static void main(String[] args) {
  5.         new SpringApplicationBuilder(Application.class).web(true).run(args);
  6.     }
  7. }
  8. @Component
  9. public class StoreIntegration {
  10.     @HystrixCommand(fallbackMethod = "defaultStores")
  11.     public Object getStores(Map<String, Object> parameters) {
  12.         //do stuff that might fail
  13.     }
  14.     public Object defaultStores(Map<String, Object> parameters) {
  15.         return /* something useful */;
  16.     }
  17. }
复制代码
@HystrixCommand由一个名为“ javanica ”的Netflix contrib库提供。Spring Cloud将带有注释的Spring beans自动包装在与Hystrix断路器连接的代理中。断路器计算何时断开和闭合电路,以及在发生故障时应采取的措施。
要配置@HystrixCommand,可以将commandProperties属性与@HystrixProperty批注一起使用。有关 更多详细信息,请参见 此处。有关 可用属性的详细信息,请参见Hystrix Wiki。
13.2传播安全上下文或使用Spring范围

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果要将某些线程本地上下文传播到@HystrixCommand中,则默认声明无效,因为默认声明在线程池中执行命令(如果超时)。通过要求Hystrix使用不同的“ 隔离策略 ”,可以通过配置或直接在批注中切换Hystrix来使用与调用方相同的线程。下面的示例演示了如何在注释中设置线程:
  1. @HystrixCommand(fallbackMethod = "stubMyService",
  2.     commandProperties = {
  3.       @HystrixProperty(name="execution.isolation.strategy", value="SEMAPHORE")
  4.     }
  5. )
  6. ...
复制代码
如果使用@SessionScope或@RequestScope,则同样适用。如果遇到运行时异常,提示它找不到范围内的上下文,则需要使用同一线程。
您还可以选择将hystrix.shareSecurityContext属性设置为true。这样做会自动配置一个Hystrix并发策略插件挂钩,以将SecurityContext从您的主线程转移到Hystrix命令所使用的那个线程。Hystrix不允许注册多个Hystrix并发策略,因此可以通过将自己的HystrixConcurrencyStrategy声明为Spring bean来使用扩展机制。Spring Cloud在Spring上下文中寻找您的实现,并将其包装在自己的插件中。
13.3健康指标

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
连接的断路器的状态也显示在调用应用程序的/health端点中,如以下示例所示:
  1. {
  2.     "hystrix": {
  3.         "openCircuitBreakers": [
  4.             "StoreIntegration::getStoresByLocationLink"
  5.         ],
  6.         "status": "CIRCUIT_OPEN"
  7.     },
  8.     "status": "UP"
  9. }
复制代码
13.4 Hystrix指标流

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要启用Hystrix指标流,请包括对spring-boot-starter-actuator的依赖性并设置management.endpoints.web.exposure.include: hystrix.stream。这样做将/actuator/hystrix.stream作为管理端点公开,如以下示例所示:
  1.     <dependency>
  2.         <groupId>org.springframework.boot</groupId>
  3.         <artifactId>spring-boot-starter-actuator</artifactId>
  4.     </dependency>
复制代码
14.断路器:Hystrix仪表板

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Hystrix的主要好处之一是它收集的有关每个HystrixCommand的一组度量。Hystrix仪表板以有效的方式显示每个断路器的运行状况。
图14.1 Hystrix资讯主页
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-76ZsaKuw-1591774641413)(https://www.springcloud.cc/images/Hystrix.png)]
15. Hystrix超时和Ribbon客户

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
当使用包裹Ribbon客户端的Hystrix命令时,您要确保Hystrix超时配置为比配置的Ribbon超时更长,包括可能进行的任何重试。例如,如果您的Ribbon连接超时是一秒钟,并且Ribbon客户端可能重试了3次请求,则Hystrix超时应该稍微超过3秒。
15.1如何包括Hystrix信息中心

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要将Hystrix仪表板包含在您的项目中,请使用启动器,其组ID为org.springframework.cloud,工件ID为spring-cloud-starter-netflix-hystrix-dashboard。有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面。
要运行Hystrix信息中心,请用@EnableHystrixDashboard注释Spring Boot主类。然后访问/hystrix,并将仪表板指向Hystrix客户端应用程序中单个实例的/hystrix.stream端点。


连接到使用HTTPS的/hystrix.stream端点时,JVM必须信任服务器使用的证书。如果证书不受信任,则必须将证书导入JVM,以使Hystrix仪表板成功连接到流终结点。
15.2 Turbine

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
从系统的整体运行状况来看,查看单个实例的Hystrix数据不是很有用。Turbine是一个应用程序,它将所有相关的/hystrix.stream端点聚合到一个组合的/turbine.stream中,以便在Hystrix仪表板中使用。个别实例通过Eureka定位。运行Turbine需要使用@EnableTurbine注释对您的主类进行注释(例如,通过使用spring-cloud-starter-netflix-turbine设置类路径)。Turbine 1 Wiki中记录的所有配置属性均适用。唯一的区别是turbine.instanceUrlSuffix不需要预先添加的端口,因为除非turbine.instanceInsertPort=false,否则它将自动处理。


默认情况下,Turbine通过在Eureka中查找其hostName和port条目,然后将/hystrix.stream附加到已注册实例上来查找/hystrix.stream端点。如果实例的元数据包含management.port,则使用它代替/hystrix.stream端点的port值。默认情况下,名为management.port的元数据条目等于management.port配置属性。可以使用以下配置覆盖它:
  1. eureka:
  2.   instance:
  3.     metadata-map:
  4.       management.port: ${management.port:8081}
复制代码
turbine.appConfig配置密钥是Eureka serviceId的列表,涡轮使用它们来查找实例。然后,在Hystrix仪表板中使用涡轮流,其URL类似于以下内容:
  1. https://my.turbine.server:8080/turbine.stream?cluster=CLUSTERNAME
复制代码
如果名称为default,则可以省略cluster参数。cluster参数必须与turbine.aggregator.clusterConfig中的条目匹配。从Eureka返回的值是大写的。因此,如果存在一个向Eureka注册的名为customers的应用程序,则以下示例可用:
  1. turbine:
  2.   aggregator:
  3.     clusterConfig: CUSTOMERS
  4.   appConfig: customers
复制代码
如果您需要自定义Turbine应该使用哪些集群名称(因为您不想在turbine.aggregator.clusterConfig配置中存储集群名称),请提供类型为TurbineClustersProvider的bean。
clusterName可以通过turbine.clusterNameExpression中的SPEL表达式进行自定义,其中根目录为InstanceInfo的实例。默认值为appName,这意味着Eureka serviceId成为群集密钥(即,客户的InstanceInfo的appName为CUSTOMERS)。一个不同的示例是turbine.clusterNameExpression=aSGName,它从AWS ASG名称获取集群名称。以下清单显示了另一个示例:
  1. turbine:
  2.   aggregator:
  3.     clusterConfig: SYSTEM,USER
  4.   appConfig: customers,stores,ui,admin
  5.   clusterNameExpression: metadata['cluster']
复制代码
在前面的示例中,来自四个服务的群集名称是从它们的元数据映射中拉出的,并且期望其值包括SYSTEM和USER。
要将“ 默认 ”群集用于所有应用程序,您需要一个字符串文字表达式(如果在YAML中,也要使用单引号和双引号进行转义):
  1. turbine:
  2.   appConfig: customers,stores
  3.   clusterNameExpression: "'default'"
复制代码
Spring Cloud提供了spring-cloud-starter-netflix-turbine,它具有运行Turbine服务器所需的所有依赖关系。要添加Turbine,请创建一个Spring Boot应用程序并使用@EnableTurbine对其进行注释。


默认情况下,Spring Cloud允许Turbine使用主机和端口以允许每个主机,每个集群多个进程。如果你想建立在本地Netflix的行为Turbine,以使每台主机的多个进程,每簇(关键实例ID是主机名),集合turbine.combineHostPort=false。
15.2.1集群端点

在某些情况下,其他应用程序了解在Turbine中配置了哪些custers可能会很有用。为此,您可以使用/clusters端点,该端点将返回所有已配置集群的JSON数组。
GET /集群。
  1. [
  2.   {
  3.     "name": "RACES",
  4.     "link": "http://localhost:8383/turbine.stream?cluster=RACES"
  5.   },
  6.   {
  7.     "name": "WEB",
  8.     "link": "http://localhost:8383/turbine.stream?cluster=WEB"
  9.   }
  10. ]
复制代码
可以通过将turbine.endpoints.clusters.enabled设置为false来禁用此端点。
15.3 Turbine Stream

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
在某些环境中(例如在PaaS设置中),从所有分布式Hystrix命令中提取指标的经典Turbine模型不起作用。在这种情况下,您可能想让Hystrix命令将指标推送到Turbine。Spring Cloud通过消息传递实现了这一点。要在客户端上执行此操作,请向spring-cloud-netflix-hystrix-stream和您选择的spring-cloud-starter-stream-*添加一个依赖项。有关代理以及如何配置客户端凭据的详细信息,请参见Spring Cloud Stream文档。对于本地代理,它应该开箱即用。
在服务器端,创建一个Spring Boot应用程序,并用@EnableTurbineStream对其进行注释。Turbine Stream服务器需要使用Spring Webflux,因此,spring-boot-starter-webflux必须包含在您的项目中。将spring-cloud-starter-netflix-turbine-stream添加到您的应用程序时,默认包含spring-boot-starter-webflux。
然后,您可以将Hystrix仪表板指向Turbine Stream服务器,而不是单独的Hystrix流。如果Turbine Stream在myhost的端口8989上运行,则将http://myhost:8989放在Hystrix仪表板的流输入字段中。电路以其各自的serviceId为前缀,后跟一个点(.),然后是电路名称。
Spring Cloud提供了spring-cloud-starter-netflix-turbine-stream,其中包含使Turbine Stream服务器运行所需的所有依赖项。然后,您可以添加您选择的流绑定程序,例如spring-cloud-starter-stream-rabbit。
Turbine Stream服务器还支持cluster参数。与Turbine服务器不同,Turbine Stream使用eureka serviceIds作为群集名称,并且这些名称不可配置。
如果Turbine Stream服务器在my.turbine.server的端口8989上运行,并且您的环境中有两个eureka serviceId customers和products,则以下URL将在Turbine Stream服务器上可用。default和空群集名称将提供Turbine Stream服务器接收的所有度量。
  1. https://my.turbine.sever:8989/turbine.stream?cluster=customers
  2. https://my.turbine.sever:8989/turbine.stream?cluster=products
  3. https://my.turbine.sever:8989/turbine.stream?cluster=default
  4. https://my.turbine.sever:8989/turbine.stream
复制代码
因此,您可以将eureka serviceIds用作Turbine仪表板(或任何兼容的仪表板)的群集名称。您无需为Turbine Stream服务器配置任何属性,例如turbine.appConfig,turbine.clusterNameExpression和turbine.aggregator.clusterConfig。


Turbine Stream服务器使用Spring Cloud Stream从配置的输入通道中收集所有度量。这意味着它不会从每个实例中主动收集Hystrix指标。它仅可以提供每个实例已经收集到输入通道中的度量。
16.客户端负载平衡器:Ribbon

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Ribbon是一种客户端负载平衡器,可让您对HTTP和TCP客户端的行为进行大量控制。Feign已使用Ribbon,因此,如果使用@FeignClient,则本节也适用。
Ribbon中的中心概念是指定客户的概念。每个负载均衡器都是组件的一部分,这些组件可以一起工作以按需联系远程服务器,并且该组件具有您作为应用程序开发人员提供的名称(例如,使用@FeignClient批注)。根据需要,Spring Cloud通过使用RibbonClientConfiguration为每个命名的客户端创建一个新的集合作为ApplicationContext。其中包含ILoadBalancer,RestClient和ServerListFilter。
16.1如何包括Ribbon

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要将Ribbon包含在您的项目中,请使用起始者,其组ID为org.springframework.cloud,工件ID为spring-cloud-starter-netflix-ribbon。有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面。
16.2自定义Ribbon客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您可以使用<client>.ribbon.*中的外部属性来配置Ribbon客户端的某些位,这与本地使用Netflix API相似,不同之处在于可以使用Spring Boot配置文件。可以将本机选项检查为CommonClientConfigKey(功能区核心的一部分)中的静态字段。
Spring Cloud还允许您通过使用@RibbonClient声明其他配置(在RibbonClientConfiguration之上)来完全控制客户端,如以下示例所示:
  1. @Configuration
  2. @RibbonClient(name = "custom", configuration = CustomConfiguration.class)
  3. public class TestConfiguration {
  4. }
复制代码
在这种情况下,客户端由RibbonClientConfiguration中已有的组件以及CustomConfiguration中的任何组件组成(其中后者通常会覆盖前者)。


CustomConfiguration类必须是@Configuration类,但请注意,对于主应用程序上下文,它不在@ComponentScan中。否则,它由所有@RibbonClients共享。如果您使用@ComponentScan(或@SpringBootApplication),则需要采取措施避免将其包括在内(例如,可以将其放在单独的,不重叠的程序包中,或指定要在@ComponentScan)。
下表显示了Spring Cloud Netflix默认为Ribbon提供的beans:
Bean类型Bean名称班级名称
IClientConfigribbonClientConfigDefaultClientConfigImpl
IRuleribbonRuleZoneAvoidanceRule
IPingribbonPingDummyPing
ServerList<Server>ribbonServerListConfigurationBasedServerList
ServerListFilter<Server>ribbonServerListFilterZonePreferenceServerListFilter
ILoadBalancerribbonLoadBalancerZoneAwareLoadBalancer
ServerListUpdaterribbonServerListUpdaterPollingServerListUpdater
创建其中一种类型的bean并将其放置在@RibbonClient配置中(例如上述FooConfiguration),您可以覆盖所描述的每个beans,如以下示例所示:
  1. @Configuration
  2. protected static class FooConfiguration {
  3.         @Bean
  4.         public ZonePreferenceServerListFilter serverListFilter() {
  5.                 ZonePreferenceServerListFilter filter = new ZonePreferenceServerListFilter();
  6.                 filter.setZone("myTestZone");
  7.                 return filter;
  8.         }
  9.         @Bean
  10.         public IPing ribbonPing() {
  11.                 return new PingUrl();
  12.         }
  13. }
复制代码
上一示例中的include语句将NoOpPing替换为PingUrl,并提供了自定义serverListFilter。
16.3为所有Ribbon客户端自定义默认值

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
通过使用@RibbonClients批注并注册默认配置,可以为所有Ribbon客户端提供默认配置,如以下示例所示:
  1. @RibbonClients(defaultConfiguration = DefaultRibbonConfig.class)
  2. public class RibbonClientDefaultConfigurationTestsConfig {
  3.         public static class BazServiceList extends ConfigurationBasedServerList {
  4.                 public BazServiceList(IClientConfig config) {
  5.                         super.initWithNiwsConfig(config);
  6.                 }
  7.         }
  8. }
  9. @Configuration
  10. class DefaultRibbonConfig {
  11.         @Bean
  12.         public IRule ribbonRule() {
  13.                 return new BestAvailableRule();
  14.         }
  15.         @Bean
  16.         public IPing ribbonPing() {
  17.                 return new PingUrl();
  18.         }
  19.         @Bean
  20.         public ServerList<Server> ribbonServerList(IClientConfig config) {
  21.                 return new RibbonClientDefaultConfigurationTestsConfig.BazServiceList(config);
  22.         }
  23.         @Bean
  24.         public ServerListSubsetFilter serverListFilter() {
  25.                 ServerListSubsetFilter filter = new ServerListSubsetFilter();
  26.                 return filter;
  27.         }
  28. }
复制代码
16.4通过设置Properties自定义Ribbon客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
从版本1.2.0开始,Spring Cloud Netflix现在支持通过将属性设置为与Ribbon文档兼容来自定义Ribbon客户端。
这使您可以在启动时在不同环境中更改行为。
以下列表显示了受支持的属性>:
    <clientName>.ribbon.NFLoadBalancerClassName:应实施ILoadBalancer<clientName>.ribbon.NFLoadBalancerRuleClassName:应实施IRule<clientName>.ribbon.NFLoadBalancerPingClassName:应实施IPing<clientName>.ribbon.NIWSServerListClassName:应实施ServerList<clientName>.ribbon.NIWSServerListFilterClassName:应实施ServerListFilter


这些属性中定义的类优先于使用@RibbonClient(configuration=MyRibbonConfig.class)定义的beans和Spring Cloud Netflix提供的默认值。
要为名为users的服务名称设置IRule,可以设置以下属性:
application.yml。
  1. users:
  2.   ribbon:
  3.     NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
  4.     NFLoadBalancerRuleClassName: com.netflix.loadbalancer.WeightedResponseTimeRule
复制代码
有关Ribbon提供的实现,请参见Ribbon文档。
16.5将Ribbon与Eureka一起使用

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
当Eureka与Ribbon结合使用时(也就是说,两者都在类路径上),ribbonServerList被扩展名DiscoveryEnabledNIWSServerList覆盖,这将填充{71中的服务器列表/}。它还用NIWSDiscoveryPing替换了IPing接口,该接口委托给Eureka确定服务器是否启动。默认安装的ServerList是DomainExtractingServerList。其目的是不使用AWS AMI元数据(这就是Netflix所依赖的)使元数据可用于负载均衡器。默认情况下,服务器列表是使用实例元数据中提供的“ zone ”信息构建的(因此,在远程客户端上,设置为eureka.instance.metadataMap.zone)。如果缺少该字段,并且设置了approximateZoneFromHostname标志,则它可以使用服务器主机名中的域名作为该区域的代理。一旦区域信息可用,就可以在ServerListFilter中使用它。默认情况下,它用于在与客户端相同的区域中定位服务器,因为默认值为ZonePreferenceServerListFilter。默认情况下,以与远程实例相同的方式(即通过eureka.instance.metadataMap.zone)确定客户端的区域。


设置客户端区域的传统“ archaius ”方法是通过名为“ @zone”的配置属性。如果可用,Spring Cloud优先于所有其他设置使用该设置(请注意,该键必须在YAML配置中用引号引起来)。


如果没有其他区域数据源,则根据客户端配置(而不是实例配置)进行猜测。我们取eureka.client.availabilityZones(这是从区域名称到区域列表的映射),然后为实例自己的区域拉出第一个区域(即eureka.client.region,其默认值为“ us-east-1” ”,以与本机Netflix兼容)。
16.6示例:如何在没有Eureka的情况下使用Ribbon

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Eureka是一种抽象发现远程服务器的便捷方法,因此您不必在客户端中对它们的URL进行硬编码。但是,如果您不想使用Eureka,则Ribbon和Feign也可以使用。假设您为“商店”声明了@RibbonClient,并且Eureka未被使用(甚至不在类路径上)。Ribbon客户端默认为配置的服务器列表。您可以提供以下配置:
application.yml。
  1. stores:
  2.   ribbon:
  3.     listOfServers: example.com,google.com
复制代码
16.7示例:在Ribbon中禁用Eureka

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
将ribbon.eureka.enabled属性设置为false会显式禁用Ribbon中的Eureka,如以下示例所示:
application.yml。
  1. ribbon:
  2.   eureka:
  3.    enabled: false
复制代码
16.8直接使用Ribbon API

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您也可以直接使用LoadBalancerClient,如以下示例所示:
  1. public class MyClass {
  2.     @Autowired
  3.     private LoadBalancerClient loadBalancer;
  4.     public void doStuff() {
  5.         ServiceInstance instance = loadBalancer.choose("stores");
  6.         URI storesUri = URI.create(String.format("http://%s:%s", instance.getHost(), instance.getPort()));
  7.         // ... do something with the URI
  8.     }
  9. }
复制代码
16.9 Ribbon配置的缓存

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
每个Ribbon命名的客户端都有一个相应的子应用程序上下文,Spring Cloud维护该上下文。该应用程序上下文在对命名客户端的第一个请求上延迟加载。通过指定Ribbon客户端的名称,可以更改此延迟加载行为,以代替在启动时急于加载这些子应用程序上下文,如以下示例所示:
application.yml。
  1. ribbon:
  2.   eager-load:
  3.     enabled: true
  4.     clients: client1, client2, client3
复制代码
16.10如何配置Hystrix线程池

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果将zuul.ribbonIsolationStrategy更改为THREAD,则Hystrix的线程隔离策略将用于所有路由。在这种情况下,HystrixThreadPoolKey默认设置为RibbonCommand。这意味着所有路由的HystrixCommands在相同的Hystrix线程池中执行。可以使用以下配置更改此行为:
application.yml。
  1. zuul:
  2.   threadPool:
  3.     useSeparateThreadPools: true
复制代码
前面的示例导致在每个路由的Hystrix线程池中执行HystrixCommands。
在这种情况下,默认HystrixThreadPoolKey与每个路由的服务ID相同。要将前缀添加到HystrixThreadPoolKey,请将zuul.threadPool.threadPoolKeyPrefix设置为要添加的值,如以下示例所示:
application.yml。
  1. zuul:
  2.   threadPool:
  3.     useSeparateThreadPools: true
  4.     threadPoolKeyPrefix: zuulgw
复制代码
16.11如何提供Ribbon的IRule的密钥

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果您需要提供自己的IRule实现来处理诸如“ canary ”测试之类的特殊路由要求,请将一些信息传递给IRule的choose方法。
com.netflix.loadbalancer.IRule.java。
  1. public interface IRule{
  2.     public Server choose(Object key);
  3.          :
复制代码
您可以提供一些信息,供您的IRule实现用来选择目标服务器,如以下示例所示:
  1. RequestContext.getCurrentContext()
  2.               .set(FilterConstants.LOAD_BALANCER_KEY, "canary-test");
复制代码
如果您使用密钥FilterConstants.LOAD_BALANCER_KEY将任何对象放入RequestContext中,则该对象将传递到IRule实现的choose方法中。上例中显示的代码必须在执行RibbonRoutingFilter之前执行。Zuul的前置过滤器是执行此操作的最佳位置。您可以通过预过滤器中的RequestContext访问HTTP标头和查询参数,因此可以用来确定传递到Ribbon的LOAD_BALANCER_KEY。如果没有在RequestContext中用LOAD_BALANCER_KEY放置任何值,则将空值作为choose方法的参数传递。
17.外部配置:Archaius

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Archaius是Netflix客户端配置库。它是所有Netflix OSS组件用于配置的库。Archaius是Apache Commons Configuration项目的扩展。它允许通过轮询源以进行更改或通过将源将更改推送到客户端来更新配置。Archaius使用Dynamic Property类作为属性的句柄,如以下示例所示:
Archaius示例。
  1. class ArchaiusTest {
  2.     DynamicStringProperty myprop = DynamicPropertyFactory
  3.             .getInstance()
  4.             .getStringProperty("my.prop");
  5.     void doSomething() {
  6.         OtherClass.someMethod(myprop.get());
  7.     }
  8. }
复制代码
Archaius具有自己的一组配置文件和加载优先级。Spring应用程序通常不应该直接使用Archaius,但是仍然需要本地配置Netflix工具。Spring Cloud具有Spring环境桥,因此Archaius可以从Spring环境读取属性。该桥允许Spring Boot项目使用常规配置工具链,同时允许它们按记录的方式配置Netflix工具(大部分情况下)。
18.路由器和过滤器:Zuul

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
路由是微服务架构不可或缺的一部分。例如,/可能被映射到您的web应用程序,/api/users被映射到用户服务,/api/shop被映射到商店服务。 Zuul是Netflix的基于JVM的路由器和服务器端负载平衡器。
Netflix将Zuul用于以下用途:
    认证方式见解压力测试金丝雀测试动态路由服务迁移减载安全静态响应处理主动/主动流量管理
Zuul的规则引擎可使用几乎所有JVM语言编写规则和过滤器,并内置对Java和Groovy的支持。


配置属性zuul.max.host.connections已被两个新属性zuul.host.maxTotalConnections和zuul.host.maxPerRouteConnections取代,它们分别默认为200和20。


所有路由的默认Hystrix隔离模式(ExecutionIsolationStrategy)为SEMAPHORE。如果首选该隔离模式,则可以将zuul.ribbonIsolationStrategy更改为THREAD。
18.1如何包含Zuul

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要将Zuul包含在您的项目中,请使用组ID为org.springframework.cloud和工件ID为spring-cloud-starter-netflix-zuul的启动程序。有关使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面。
18.2嵌入式Zuul反向代理

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud已创建嵌入式Zuul代理,以简化UI应用程序要对一个或多个后端服务进行代理调用的常见用例的开发。此功能对于用户界面代理所需的后端服务很有用,从而避免了为所有后端独立管理CORS和身份验证问题的需求。
要启用它,请用@EnableZuulProxy注释Spring Boot主类。这样做会导致将本地呼叫转发到适当的服务。按照约定,ID为users的服务从位于/users的代理接收请求(前缀被去除)。代理使用Ribbon来定位要通过发现转发到的实例。所有请求均在hystrix命令中执行,因此失败以Hystrix指标显示。一旦电路断开,代理就不会尝试与服务联系。


Zuul入门程序不包含发现客户端,因此,对于基于服务ID的路由,您还需要在类路径上提供其中之一(Eureka是一种选择)。
要跳过自动添加服务的过程,请将zuul.ignored-services设置为服务ID模式的列表。如果服务与被忽略但已包含在显式配置的路由映射中的模式匹配,则将其忽略,如以下示例所示:
application.yml。
  1. zuul:
  2.   ignoredServices: '*'
  3.   routes:
  4.     users: /myusers/**
复制代码
在前面的示例中,users,所有服务均被忽略。
要增加或更改代理路由,可以添加外部配置,如下所示:
application.yml。
  1. zuul:
  2.   routes:
  3.     users: /myusers/**
复制代码
前面的示例意味着对/myusers的HTTP调用被转发到users服务(例如,/myusers/101被转发到/101)。
要对路由进行更细粒度的控制,可以分别指定路径和serviceId,如下所示:
application.yml。
  1. zuul:
  2.   routes:
  3.     users:
  4.       path: /myusers/**
  5.       serviceId: users_service
复制代码
前面的示例意味着对/myusers的HTTP调用将转发到users_service服务。路由必须具有可以指定为蚂蚁样式模式的path,因此/myusers/*仅匹配一个级别,而/myusers/**则分层匹配。
后端的位置可以指定为serviceId(用于发现服务)或url(用于物理位置),如以下示例所示:
application.yml。
  1. zuul:
  2.   routes:
  3.     users:
  4.       path: /myusers/**
  5.       url: https://example.com/users_service
复制代码
这些简单的url路由不会作为HystrixCommand来执行,也不会使用Ribbon对多个URL进行负载均衡。为了实现这些目标,可以使用静态服务器列表指定一个serviceId,如下所示:
application.yml。
  1. zuul:
  2.   routes:
  3.     echo:
  4.       path: /myusers/**
  5.       serviceId: myusers-service
  6.       stripPrefix: true
  7. hystrix:
  8.   command:
  9.     myusers-service:
  10.       execution:
  11.         isolation:
  12.           thread:
  13.             timeoutInMilliseconds: ...
  14. myusers-service:
  15.   ribbon:
  16.     NIWSServerListClassName: com.netflix.loadbalancer.ConfigurationBasedServerList
  17.     listOfServers: https://example1.com,http://example2.com
  18.     ConnectTimeout: 1000
  19.     ReadTimeout: 3000
  20.     MaxTotalHttpConnections: 500
  21.     MaxConnectionsPerHost: 100
复制代码
另一种方法是指定服务路由并为serviceId配置Ribbon客户端(这样做需要在Ribbon中禁用Eureka支持- 有关更多信息,请参见上文),如下所示例:
application.yml。
  1. zuul:
  2.   routes:
  3.     users:
  4.       path: /myusers/**
  5.       serviceId: users
  6. ribbon:
  7.   eureka:
  8.     enabled: false
  9. users:
  10.   ribbon:
  11.     listOfServers: example.com,google.com
复制代码
您可以使用regexmapper在serviceId和路由之间提供约定。它使用正则表达式命名组从serviceId中提取变量,并将其注入到路由模式中,如以下示例所示:
ApplicationConfiguration.java。
  1. @Bean
  2. public PatternServiceRouteMapper serviceRouteMapper() {
  3.     return new PatternServiceRouteMapper(
  4.         "(?<name>^.+)-(?<version>v.+$)",
  5.         "${version}/${name}");
  6. }
复制代码
前面的示例意味着myusers-v1中的serviceId被映射到路由/v1/myusers/**。可以接受任何正则表达式,但是所有命名组必须同时存在于servicePattern和routePattern中。如果servicePattern与serviceId不匹配,则使用默认行为。在前面的示例中,myusers中的serviceId被映射到“ / myusers / **”路由(未检测到版本)。默认情况下,此功能是禁用的,仅适用于发现的服务。
要为所有映射添加前缀,请将zuul.prefix设置为一个值,例如/api。默认情况下,在转发请求之前,将从请求中删除代理前缀(您可以使用zuul.stripPrefix=false将此行为关闭)。您还可以关闭从单个路由中剥离特定于服务的前缀,如以下示例所示:
application.yml。
  1. zuul:
  2.   routes:
  3.     users:
  4.       path: /myusers/**
  5.       stripPrefix: false
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FmtKY3Dh-1591774641423)(https://www.springcloud.cc/images/note.png)]
zuul.stripPrefix仅适用于zuul.prefix中设置的前缀。它对给定路由的path中定义的前缀没有任何影响。
在前面的示例中,对/myusers/101的请求被转发到users服务上的/myusers/101。
zuul.routes条目实际上绑定到类型为ZuulProperties的对象。如果查看该对象的属性,则可以看到它也有一个retryable标志。将该标志设置为true,以使Ribbon客户端自动重试失败的请求。当您需要修改使用Ribbon客户端配置的重试操作的参数时,也可以将该标志设置为true。
默认情况下,X-Forwarded-Host标头被添加到转发的请求中。要关闭它,请设置zuul.addProxyHeaders = false。默认情况下,前缀路径被剥离,并且后端请求使用X-Forwarded-Prefix标头(在前面显示的示例中为/myusers)。
如果设置默认路由(/),则带有@EnableZuulProxy的应用程序可以充当独立服务器。例如,zuul.route.home: /会将所有流量(“ / **”)路由到“ home”服务。
如果需要更细粒度的忽略,则可以指定要忽略的特定模式。这些模式在路线定位过程开始时进行评估,这意味着模式中应包含前缀以保证匹配。被忽略的模式跨越所有服务,并取代任何其他路由规范。以下示例显示了如何创建忽略的模式:
application.yml。
  1. zuul:
  2.   ignoredPatterns: /**/admin/**
  3.   routes:
  4.     users: /myusers/**
复制代码
前面的示例意味着所有呼叫(例如/myusers/101)都被转发到users服务上的/101。但是,包括/admin/在内的呼叫无法解决。


如果您需要保留路由的顺序,则需要使用YAML文件,因为使用属性文件时顺序会丢失。以下示例显示了这样的YAML文件:
application.yml。
  1. zuul:
  2.   routes:
  3.     users:
  4.       path: /myusers/**
  5.     legacy:
  6.       path: /**
复制代码
如果要使用属性文件,则legacy路径可能最终位于users路径的前面,从而导致users路径不可访问。
18.3 Zuul HTTP客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Zuul使用的默认HTTP客户端现在由Apache HTTP客户端支持,而不是已弃用的Ribbon RestClient。要使用RestClient或okhttp3.OkHttpClient,请分别设置ribbon.restclient.enabled=true或ribbon.okhttp.enabled=true。如果要自定义Apache HTTP客户端或OK HTTP客户端,请提供类型为ClosableHttpClient或OkHttpClient的bean。
18.4 Cookie和敏感标题

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您可以在同一系统中的服务之间共享标头,但您可能不希望敏感标头泄漏到下游到外部服务器中。您可以在路由配置中指定忽略的标头列表。Cookies发挥着特殊的作用,因为它们在浏览器中具有定义明确的语义,并且始终将它们视为敏感内容。如果代理的使用者是浏览器,那么下游服务的cookie也会给用户带来麻烦,因为它们都混杂在一起(所有下游服务看起来都来自同一位置)。
如果您对服务的设计很谨慎(例如,如果只有一个下游服务设置cookie),则可以让它们从后端一直流到调用者。另外,如果您的代理设置了cookie,并且您的所有后端服务都在同一系统中,则很自然地简单地共享它们(例如,使用Spring Session将它们链接到某些共享状态)。除此之外,由下游服务设置的任何cookie可能对调用者都无用,因此建议您将(至少)Set-Cookie和Cookie设置为敏感的标头,用于那些没有使用的路由您网域的一部分。即使对于属于您网域的路由,在让Cookie在它们和代理之间流动之前,也应仔细考虑其含义。
可以将敏感头配置为每个路由的逗号分隔列表,如以下示例所示:
application.yml。
  1. zuul:
  2.   routes:
  3.     users:
  4.       path: /myusers/**
  5.       sensitiveHeaders: Cookie,Set-Cookie,Authorization
  6.       url: https://downstream
复制代码


这是sensitiveHeaders的默认值,因此除非您希望其与众不同,否则无需进行设置。这是Spring Cloud Netflix 1.1中的新功能(在1.0中,用户无法控制标题,并且所有cookie都双向流动)。
sensitiveHeaders是黑名单,默认值不为空。因此,要使Zuul发送所有标头(ignored除外),必须将其显式设置为空列表。如果要将Cookie或授权标头传递到后端,则必须这样做。以下示例显示如何使用sensitiveHeaders:
application.yml。
  1. zuul:
  2.   routes:
  3.     users:
  4.       path: /myusers/**
  5.       sensitiveHeaders:
  6.       url: https://downstream
复制代码
您还可以通过设置zuul.sensitiveHeaders来设置敏感标题。如果在路由上设置了sensitiveHeaders,它将覆盖全局sensitiveHeaders设置。
18.5忽略标题

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
除了路由敏感的标头之外,您还可以为与下游服务交互期间应丢弃的值(请求和响应)设置一个名为zuul.ignoredHeaders的全局值。默认情况下,如果Spring Security不在类路径中,则它们为空。否则,它们将初始化为Spring Security指定的一组众所周知的“ 安全性 ”标头(例如,涉及缓存)。在这种情况下的假设是,下游服务也可以添加这些标头,但是我们需要来自代理的值。要在类路径上有Spring Security时不丢弃这些众所周知的安全标头,可以将zuul.ignoreSecurityHeaders设置为false。如果您在Spring Security中禁用了HTTP安全响应标头,并希望由下游服务提供值,则这样做很有用。
18.6管理端点

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
默认情况下,如果将@EnableZuulProxy与Spring Boot Actuator结合使用,则将启用两个附加端点:
    路线筛选器
18.6.1路由端点

在/routes处的路由端点的GET返回已映射路由的列表:
GET /路线。
  1. {
  2.   /stores/**: "http://localhost:8081"
  3. }
复制代码
可以通过将?format=details查询字符串添加到/routes来请求其他路由详细信息。这样做会产生以下输出:
获取/ routes / details。
  1. {
  2.   "/stores/**": {
  3.     "id": "stores",
  4.     "fullPath": "/stores/**",
  5.     "location": "http://localhost:8081",
  6.     "path": "/**",
  7.     "prefix": "/stores",
  8.     "retryable": false,
  9.     "customSensitiveHeaders": false,
  10.     "prefixStripped": true
  11.   }
  12. }
复制代码
POST至/routes强制刷新现有路由(例如,当服务目录中发生更改时)。您可以通过将endpoints.routes.enabled设置为false来禁用此端点。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4tcYT0V4-1591774641425)(https://www.springcloud.cc/images/note.png)]
路由应该自动响应服务目录中的更改,但是从POST到/routes是强制更改立即进行的一种方法。
18.6.2过滤器端点

过滤器端点/filters的GET按类型返回Zuul过滤器的映射。对于地图中的每种过滤器类型,您将获得该类型的所有过滤器的列表以及它们的详细信息。
18.7扼杀模式和局部Forwards

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
迁移现有应用程序或API时,常见的模式是“ 勒死 ”旧的端点,并用不同的实现方式慢慢替换它们。Zuul代理是一个有用的工具,因为您可以使用它来处理来自旧端点的客户端的所有流量,但可以将一些请求重定向到新请求。
以下示例显示“ 扼杀 ”方案的配置详细信息:
application.yml。
  1. zuul:
  2.   routes:
  3.     first:
  4.       path: /first/**
  5.       url: https://first.example.com
  6.     second:
  7.       path: /second/**
  8.       url: forward:/second
  9.     third:
  10.       path: /third/**
  11.       url: forward:/3rd
  12.     legacy:
  13.       path: /**
  14.       url: https://legacy.example.com
复制代码
在前面的示例中,我们扼杀了“ legacy ”应用程序,该应用程序映射到与其他模式之一不匹配的所有请求。/first/**中的路径已使用外部URL提取到新服务中。/second/**中的路径被转发,以便可以在本地处理(例如,使用普通Spring @RequestMapping)。/third/**中的路径也被转发,但是前缀不同(/third/foo被转发到/3rd/foo)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LNB3d0PE-1591774641426)(https://www.springcloud.cc/images/note.png)]
被忽略的模式不会被完全忽略,它们不会由代理处理(因此它们也可以在本地有效转发)。
18.8通过Zuul上传文件

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果使用@EnableZuulProxy,则可以使用代理路径上载文件,只要文件很小,它就可以正常工作。对于大文件,有一个替代路径可以绕过“ / zuul / *”中的Spring DispatcherServlet(以避免进行多部分处理)。换句话说,如果您拥有zuul.routes.customers=/customers/**,则可以将POST大文件复制到/zuul/customers/*。Servlet路径通过zuul.servletPath外部化。如果代理路由将您带到Ribbon负载均衡器,则超大文件也需要提高超时设置,如以下示例所示:
application.yml。
  1. hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds: 60000
  2. ribbon:
  3.   ConnectTimeout: 3000
  4.   ReadTimeout: 60000
复制代码
请注意,要使流技术处理大文件,您需要在请求中使用分块编码(某些浏览器默认不这样做),如以下示例所示:
  1. $ curl -v -H "Transfer-Encoding: chunked" \
  2.     -F "file=@mylarge.iso" localhost:9999/zuul/simple/file
复制代码
18.9查询字符串编码

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
在处理传入请求时,查询参数将被解码,以便可以在Zuul过滤器中进行修改。然后将它们重新编码,在路由过滤器中重建后端请求。例如,如果结果是使用Javascript的encodeURIComponent()方法编码的,则结果可能不同于原始输入。虽然这在大多数情况下不会引起问题,但某些web服务器可能对复杂查询字符串的编码很挑剔。
要强制对查询字符串进行原始编码,可以将特殊标志传递给ZuulProperties,以便使用HttpServletRequest::getQueryString方法按原样使用查询字符串,如以下示例所示:
application.yml。
  1. zuul:
  2.   forceOriginalQueryStringEncoding: true
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-tzxmTOru-1591774641426)(https://www.springcloud.cc/images/note.png)]
该特殊标志仅适用于SimpleHostRoutingFilter。另外,您松开了使用RequestContext.getCurrentContext().setRequestQueryParams(someOverriddenParameters)轻松覆盖查询参数的功能,因为现在直接在原始HttpServletRequest上获取了查询字符串。
18.10请求URI编码

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
在处理传入请求时,在将请求URI与路由匹配之前,先对其进行解码。然后在路由过滤器中重建后端请求时,将对请求URI进行重新编码。如果您的URI包含编码的“ /”字符,则可能导致某些意外行为。
要使用原始请求URI,可以将特殊标志传递给’ZuulProperties’,以便使用HttpServletRequest::getRequestURI方法按原样使用URI,如以下示例所示:
application.yml。
  1. zuul:
  2.   decodeUrl: false
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1Bk5tAzi-1591774641427)(https://www.springcloud.cc/images/note.png)]
如果使用requestURI RequestContext属性覆盖请求URI,并且此标志设置为false,则将不对在请求上下文中设置的URL进行编码。确保URL已被编码是您的责任。
18.11普通嵌入式Zuul

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果使用@EnableZuulServer(而不是@EnableZuulProxy),则也可以运行Zuul服务器而不进行代理或有选择地打开代理平台的某些部分。您添加到类型为ZuulFilter的应用程序中的所有beans都会自动安装(与@EnableZuulProxy一样),但是不会自动添加任何代理过滤器。
在这种情况下,仍然可以通过配置“ zuul.routes。*”来指定进入Zuul服务器的路由,但是没有服务发现也没有代理。因此,“ serviceId”和“ url”设置将被忽略。以下示例将“ / api / **”中的所有路径映射到Zuul过滤器链:
application.yml。
  1. zuul:
  2.   routes:
  3.     api: /api/**
复制代码
18.12禁用Zuul过滤器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud的Zuul带有多个ZuulFilter beans,默认情况下在代理和服务器模式下都启用。有关可以启用的过滤器列表,请参见Zuul过滤器包。如果要禁用一个,请设置zuul.<SimpleClassName>.<filterType>.disable=true。按照惯例,filters之后的软件包是Zuul过滤器类型。例如,要禁用org.springframework.cloud.netflix.zuul.filters.post.SendResponseFilter,请设置zuul.SendResponseFilter.post.disable=true。
18.13为路线提供Hystrix后备

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
当Zuul中给定路由的电路跳闸时,可以通过创建类型为FallbackProvider的bean提供回退响应。在此bean中,您需要指定回退的路由ID,并提供一个ClientHttpResponse作为回退的路由。以下示例显示了一个相对简单的FallbackProvider实现:
  1. class MyFallbackProvider implements FallbackProvider {
  2.     @Override
  3.     public String getRoute() {
  4.         return "customers";
  5.     }
  6.     @Override
  7.     public ClientHttpResponse fallbackResponse(String route, final Throwable cause) {
  8.         if (cause instanceof HystrixTimeoutException) {
  9.             return response(HttpStatus.GATEWAY_TIMEOUT);
  10.         } else {
  11.             return response(HttpStatus.INTERNAL_SERVER_ERROR);
  12.         }
  13.     }
  14.     private ClientHttpResponse response(final HttpStatus status) {
  15.         return new ClientHttpResponse() {
  16.             @Override
  17.             public HttpStatus getStatusCode() throws IOException {
  18.                 return status;
  19.             }
  20.             @Override
  21.             public int getRawStatusCode() throws IOException {
  22.                 return status.value();
  23.             }
  24.             @Override
  25.             public String getStatusText() throws IOException {
  26.                 return status.getReasonPhrase();
  27.             }
  28.             @Override
  29.             public void close() {
  30.             }
  31.             @Override
  32.             public InputStream getBody() throws IOException {
  33.                 return new ByteArrayInputStream("fallback".getBytes());
  34.             }
  35.             @Override
  36.             public HttpHeaders getHeaders() {
  37.                 HttpHeaders headers = new HttpHeaders();
  38.                 headers.setContentType(MediaType.APPLICATION_JSON);
  39.                 return headers;
  40.             }
  41.         };
  42.     }
  43. }
复制代码
以下示例显示了上一个示例的路由配置可能如何显示:
  1. zuul:
  2.   routes:
  3.     customers: /customers/**
复制代码
如果您想为所有路由提供默认后备,则可以创建类型为FallbackProvider的bean,并让getRoute方法返回*或null,如以下示例:
  1. class MyFallbackProvider implements FallbackProvider {
  2.     @Override
  3.     public String getRoute() {
  4.         return "*";
  5.     }
  6.     @Override
  7.     public ClientHttpResponse fallbackResponse(String route, Throwable throwable) {
  8.         return new ClientHttpResponse() {
  9.             @Override
  10.             public HttpStatus getStatusCode() throws IOException {
  11.                 return HttpStatus.OK;
  12.             }
  13.             @Override
  14.             public int getRawStatusCode() throws IOException {
  15.                 return 200;
  16.             }
  17.             @Override
  18.             public String getStatusText() throws IOException {
  19.                 return "OK";
  20.             }
  21.             @Override
  22.             public void close() {
  23.             }
  24.             @Override
  25.             public InputStream getBody() throws IOException {
  26.                 return new ByteArrayInputStream("fallback".getBytes());
  27.             }
  28.             @Override
  29.             public HttpHeaders getHeaders() {
  30.                 HttpHeaders headers = new HttpHeaders();
  31.                 headers.setContentType(MediaType.APPLICATION_JSON);
  32.                 return headers;
  33.             }
  34.         };
  35.     }
  36. }
复制代码
18.14 Zuul超时

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果要为通过Zuul代理的请求配置套接字超时和读取超时,则根据您的配置,有两个选项:
    如果Zuul使用服务发现,则需要使用ribbon.ReadTimeout和ribbon.SocketTimeout Ribbon属性配置这些超时。
如果通过指定URL配置了Zuul路由,则需要使用zuul.host.connect-timeout-millis和zuul.host.socket-timeout-millis。
18.15重写Location标头

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果Zuul在web应用程序的前面,则当web应用程序通过HTTP状态代码3XX重定向时,您可能需要重新编写Location标头。否则,浏览器将重定向到web应用程序的URL,而不是Zuul URL。您可以配置LocationRewriteFilter Zuul过滤器,将Location标头重写为Zuul的URL。它还添加回去的全局前缀和特定于路由的前缀。以下示例通过使用Spring配置文件添加过滤器:
  1. import org.springframework.cloud.netflix.zuul.filters.post.LocationRewriteFilter;
  2. ...
  3. @Configuration
  4. @EnableZuulProxy
  5. public class ZuulConfig {
  6.     @Bean
  7.     public LocationRewriteFilter locationRewriteFilter() {
  8.         return new LocationRewriteFilter();
  9.     }
  10. }
复制代码


警告
小心使用此过滤器。筛选器作用于所有3XX响应代码的Location头,这可能并不适用于所有情况,例如将用户重定向到外部URL时。
18.16启用跨源请求

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
默认情况下,Zuul将所有跨源请求(CORS)路由到服务。如果您希望Zuul处理这些请求,可以通过提供自定义WebMvcConfigurer bean来完成:
  1. @Bean
  2. public WebMvcConfigurer corsConfigurer() {
  3.     return new WebMvcConfigurer() {
  4.         public void addCorsMappings(CorsRegistry registry) {
  5.             registry.addMapping("/path-1/**")
  6.                     .allowedOrigins("https://allowed-origin.com")
  7.                     .allowedMethods("GET", "POST");
  8.         }
  9.     };
  10. }
复制代码
在上面的示例中,我们允许https://allowed-origin.com中的GET和POST方法将跨域请求发送到以path-1开头的端点。您可以使用/**映射将CORS配置应用于特定的路径模式或整个应用程序的全局路径。您可以通过此配置来自定义属性:allowedOrigins,allowedMethods,allowedHeaders,exposedHeaders,allowCredentials和maxAge。
18.17指标

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Zuul将在执行器指标终结点下提供指标,以解决路由请求时可能发生的任何故障。可以通过点击/actuator/metrics来查看这些指标。指标的名称格式为ZUUL::EXCEPTION:errorCause:statusCode。
18.18 Zuul开发人员指南

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
有关Zuul的工作原理的一般概述,请参见Zuul Wiki。
18.18.1 Zuul Servlet

Zuul被实现为Servlet。对于一般情况,Zuul已嵌入Spring调度机制中。这使Spring MVC可以控制路由。在这种情况下,Zuul缓冲请求。如果需要通过Zuul而不缓冲请求(例如,用于大文件上传),则Servlet也将安装在Spring Dispatcher之外。缺省情况下,该servlet的地址为/zuul。可以使用zuul.servlet-path属性更改此路径。
18.18.2 Zuul RequestContext

要在过滤器之间传递信息,Zuul使用RequestContext。其数据保存在每个请求专用的ThreadLocal中。有关在何处路由请求,错误以及实际的HttpServletRequest和HttpServletResponse的信息存储在此处。RequestContext扩展了ConcurrentHashMap,因此任何内容都可以存储在上下文中。FilterConstants包含Spring Cloud Netflix安装的过滤器使用的密钥(稍后会详细介绍)。
18.18.3 @EnableZuulProxy与@EnableZuulServer

Spring Cloud Netflix安装了许多过滤器,具体取决于启用了Zuul的注释。@EnableZuulProxy是@EnableZuulServer的超集。换句话说,@EnableZuulProxy包含@EnableZuulServer安装的所有筛选器。“ 代理 ”中的其他过滤器启用路由功能。如果您想使用“ 空白 ” Zuul,则应使用@EnableZuulServer。
18.18.4 @EnableZuulServer过滤器

@EnableZuulServer创建一个SimpleRouteLocator,该文件从Spring Boot配置文件中加载路由定义。
已安装以下过滤器(按常规方式Spring Beans):

  • 前置过滤器:
      ServletDetectionFilter:检测请求是否通过Spring分派器进行。设置键为FilterConstants.IS_DISPATCHER_SERVLET_REQUEST_KEY的布尔值。FormBodyWrapperFilter:解析表单数据并为下游请求重新编码。DebugFilter:如果设置了debug请求参数,则将RequestContext.setDebugRouting()和RequestContext.setDebugRequest()设置为true。*路由过滤器:SendForwardFilter:使用Servlet RequestDispatcher的Forwards请求。转发位置存储在RequestContext属性FilterConstants.FORWARD_TO_KEY中。这对于转发到当前应用程序中的端点很有用。

  • 帖子过滤器:
      SendResponseFilter:将代理请求的响应写入当前响应。

  • 错误过滤器:
      SendErrorFilter:如果RequestContext.getThrowable()不为空,则Forwards至/error(默认)。您可以通过设置error.path属性来更改默认转发路径(/error)。

18.18.5 @EnableZuulProxy过滤器

创建一个DiscoveryClientRouteLocator,它从DiscoveryClient(例如Eureka)以及属性中加载路由定义。从DiscoveryClient为每个serviceId创建一条路由。添加新服务后,将刷新路由。
除了前面描述的过滤器之外,还安装了以下过滤器(常规Spring Beans):

  • 前置过滤器:
      PreDecorationFilter:根据提供的RouteLocator确定路线和路线。它还为下游请求设置了各种与代理相关的标头。

  • 路线过滤器:

    • RibbonRoutingFilter:使用Ribbon,Hystrix和可插拔的HTTP客户端发送请求。在RequestContext属性FilterConstants.SERVICE_ID_KEY中可以找到服务ID。此过滤器可以使用不同的HTTP客户端:
        Apache HttpClient:默认客户端。Squareup OkHttpClient v3:通过在类路径上放置com.squareup.okhttp3:okhttp库并设置ribbon.okhttp.enabled=true来启用。Netflix Ribbon HTTP客户端:通过设置ribbon.restclient.enabled=true启用。该客户端具有局限性,包括不支持PATCH方法,但是还具有内置的重试功能。
      SimpleHostRoutingFilter:通过Apache HttpClient将请求发送到预定的URL。可在RequestContext.getRouteHost()中找到URL。

18.18.6自定义Zuul过滤器示例

下面的大多数“如何编写”示例都包含在示例Zuul过滤器项目中。在该存储库中也有一些处理请求或响应正文的示例。
本节包括以下示例:
    名为“如何编写预过滤器”的部分名为“如何编写路由过滤器”的部分名为“如何编写后置过滤器”的部分
如何编写前置过滤器

前置过滤器可在RequestContext中设置数据,以便在下游的过滤器中使用。主要用例是设置路由过滤器所需的信息。以下示例显示了Zuul前置过滤器:
  1. public class QueryParamPreFilter extends ZuulFilter {
  2.         @Override
  3.         public int filterOrder() {
  4.                 return PRE_DECORATION_FILTER_ORDER - 1; // run before PreDecoration
  5.         }
  6.         @Override
  7.         public String filterType() {
  8.                 return PRE_TYPE;
  9.         }
  10.         @Override
  11.         public boolean shouldFilter() {
  12.                 RequestContext ctx = RequestContext.getCurrentContext();
  13.                 return !ctx.containsKey(FORWARD_TO_KEY) // a filter has already forwarded
  14.                                 && !ctx.containsKey(SERVICE_ID_KEY); // a filter has already determined serviceId
  15.         }
  16.     @Override
  17.     public Object run() {
  18.         RequestContext ctx = RequestContext.getCurrentContext();
  19.                 HttpServletRequest request = ctx.getRequest();
  20.                 if (request.getParameter("sample") != null) {
  21.                     // put the serviceId in `RequestContext`
  22.                     ctx.put(SERVICE_ID_KEY, request.getParameter("foo"));
  23.             }
  24.         return null;
  25.     }
  26. }
复制代码
前面的过滤器从sample请求参数中填充SERVICE_ID_KEY。实际上,您不应该执行这种直接映射。而是应从sample的值中查找服务ID。
现在已填充SERVICE_ID_KEY,PreDecorationFilter将不运行,而RibbonRoutingFilter将运行。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2G4tVY3F-1591774641429)(https://www.springcloud.cc/images/tip.png)]
如果要路由到完整URL,请致电ctx.setRouteHost(url)。
要修改路由过滤器转发到的路径,请设置REQUEST_URI_KEY。
如何编写路由过滤器

路由过滤器在预过滤器之后运行,并向其他服务发出请求。这里的许多工作是将请求和响应数据与客户端所需的模型相互转换。以下示例显示了Zuul路由过滤器:
  1. public class OkHttpRoutingFilter extends ZuulFilter {
  2.         @Autowired
  3.         private ProxyRequestHelper helper;
  4.         @Override
  5.         public String filterType() {
  6.                 return ROUTE_TYPE;
  7.         }
  8.         @Override
  9.         public int filterOrder() {
  10.                 return SIMPLE_HOST_ROUTING_FILTER_ORDER - 1;
  11.         }
  12.         @Override
  13.         public boolean shouldFilter() {
  14.                 return RequestContext.getCurrentContext().getRouteHost() != null
  15.                                 && RequestContext.getCurrentContext().sendZuulResponse();
  16.         }
  17.     @Override
  18.     public Object run() {
  19.                 OkHttpClient httpClient = new OkHttpClient.Builder()
  20.                                 // customize
  21.                                 .build();
  22.                 RequestContext context = RequestContext.getCurrentContext();
  23.                 HttpServletRequest request = context.getRequest();
  24.                 String method = request.getMethod();
  25.                 String uri = this.helper.buildZuulRequestURI(request);
  26.                 Headers.Builder headers = new Headers.Builder();
  27.                 Enumeration<String> headerNames = request.getHeaderNames();
  28.                 while (headerNames.hasMoreElements()) {
  29.                         String name = headerNames.nextElement();
  30.                         Enumeration<String> values = request.getHeaders(name);
  31.                         while (values.hasMoreElements()) {
  32.                                 String value = values.nextElement();
  33.                                 headers.add(name, value);
  34.                         }
  35.                 }
  36.                 InputStream inputStream = request.getInputStream();
  37.                 RequestBody requestBody = null;
  38.                 if (inputStream != null && HttpMethod.permitsRequestBody(method)) {
  39.                         MediaType mediaType = null;
  40.                         if (headers.get("Content-Type") != null) {
  41.                                 mediaType = MediaType.parse(headers.get("Content-Type"));
  42.                         }
  43.                         requestBody = RequestBody.create(mediaType, StreamUtils.copyToByteArray(inputStream));
  44.                 }
  45.                 Request.Builder builder = new Request.Builder()
  46.                                 .headers(headers.build())
  47.                                 .url(uri)
  48.                                 .method(method, requestBody);
  49.                 Response response = httpClient.newCall(builder.build()).execute();
  50.                 LinkedMultiValueMap<String, String> responseHeaders = new LinkedMultiValueMap<>();
  51.                 for (Map.Entry<String, List<String>> entry : response.headers().toMultimap().entrySet()) {
  52.                         responseHeaders.put(entry.getKey(), entry.getValue());
  53.                 }
  54.                 this.helper.setResponse(response.code(), response.body().byteStream(),
  55.                                 responseHeaders);
  56.                 context.setRouteHost(null); // prevent SimpleHostRoutingFilter from running
  57.                 return null;
  58.     }
  59. }
复制代码
前面的过滤器将Servlet请求信息转换为OkHttp3请求信息,执行HTTP请求,并将OkHttp3响应信息转换为Servlet响应。
如何编写帖子过滤器

后置过滤器通常操纵响应。以下过滤器将随机UUID添加为X-Sample标头:
  1. public class AddResponseHeaderFilter extends ZuulFilter {
  2.         @Override
  3.         public String filterType() {
  4.                 return POST_TYPE;
  5.         }
  6.         @Override
  7.         public int filterOrder() {
  8.                 return SEND_RESPONSE_FILTER_ORDER - 1;
  9.         }
  10.         @Override
  11.         public boolean shouldFilter() {
  12.                 return true;
  13.         }
  14.         @Override
  15.         public Object run() {
  16.                 RequestContext context = RequestContext.getCurrentContext();
  17.             HttpServletResponse servletResponse = context.getResponse();
  18.                 servletResponse.addHeader("X-Sample", UUID.randomUUID().toString());
  19.                 return null;
  20.         }
  21. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qnWIWqoF-1591774641429)(https://www.springcloud.cc/images/note.png)]
其他操作,例如转换响应主体,则更加复杂且计算量大。
18.18.7 Zuul错误的工作方式

如果在Zuul过滤器生命周期的任何部分抛出异常,则将执行错误过滤器。仅当RequestContext.getThrowable()不是null时才运行SendErrorFilter。然后,它在请求中设置特定的javax.servlet.error.*属性,并将请求转发到Spring Boot错误页面。
18.18.8 Zuul急切的应用程序上下文加载

Zuul内部使用Ribbon来调用远程URL。默认情况下,Ribbon客户端在第一次调用时被Spring Cloud延迟加载。可以使用以下配置为Zuul更改此行为,这会导致在应用程序启动时急于加载与子Ribbon相关的应用程序上下文。以下示例显示了如何启用即时加载:
application.yml。
  1. zuul:
  2.   ribbon:
  3.     eager-load:
  4.       enabled: true
复制代码
19. Polyglot支持Sidecar

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您是否要使用非JVM语言来利用Eureka,Ribbon和Config Server?Spring Cloud Netflix Sidecar的灵感来自Netflix Prana。它包括一个HTTP API,用于获取给定服务的所有实例(按主机和端口)。您也可以通过嵌入式Zuul代理来代理服务调用,该代理从Eureka获取其路由条目。可以直接通过主机查找或通过Zuul代理访问Spring Cloud Config服务器。非JVM应用程序应实施运行状况检查,以便Sidecar可以向Eureka报告应用程序是启动还是关闭。
要在项目中包含Sidecar,请使用组ID为org.springframework.cloud且工件ID为spring-cloud-netflix-sidecar的依赖项。
要启用Sidecar,请使用@EnableSidecar创建一个Spring Boot应用程序。该注释包括@EnableCircuitBreaker,@EnableDiscoveryClient和@EnableZuulProxy。在与非JVM应用程序相同的主机上运行结果应用程序。
要配置侧车,请将sidecar.port和sidecar.health-uri添加到application.yml。sidecar.port属性是非JVM应用程序侦听的端口。这样Sidecar可以正确地向Eureka注册应用程序。sidecar.secure-port-enabled选项提供了一种启用流量安全端口的方法。sidecar.health-uri是在非JVM应用程序上可访问的URI,它模仿Spring Boot运行状况指示器。它应该返回类似于以下内容的JSON文档:
health-uri-document。
  1. {
  2.   "status":"UP"
  3. }
复制代码
以下application.yml示例显示了Sidecar应用程序的示例配置:
application.yml。
  1. server:
  2.   port: 5678
  3. spring:
  4.   application:
  5.     name: sidecar
  6. sidecar:
  7.   port: 8000
  8.   health-uri: http://localhost:8000/health.json
复制代码
DiscoveryClient.getInstances()方法的API为/hosts/{serviceId}。以下针对/hosts/customers的示例响应在不同的主机上返回两个实例:
/ hosts / customers。
  1. [
  2.     {
  3.         "host": "myhost",
  4.         "port": 9000,
  5.         "uri": "http://myhost:9000",
  6.         "serviceId": "CUSTOMERS",
  7.         "secure": false
  8.     },
  9.     {
  10.         "host": "myhost2",
  11.         "port": 9000,
  12.         "uri": "http://myhost2:9000",
  13.         "serviceId": "CUSTOMERS",
  14.         "secure": false
  15.     }
  16. ]
复制代码
非JVM应用程序(如果Sidecar位于端口5678上)可通过http://localhost:5678/hosts/{serviceId}访问此API。
Zuul代理会自动将Eureka中已知的每个服务的路由添加到/<serviceId>,因此可以在/customers中使用客户服务。非JVM应用程序可以在http://localhost:5678/customers上访问客户服务(假设Sidecar正在侦听5678端口)。
如果Config Server已向Eureka注册,则非JVM应用程序可以通过Zuul代理对其进行访问。如果ConfigServer的serviceId为configserver并且Sidecar在端口5678上,则可以在http:// localhost:5678 / configserver上对其进行访问。
非JVM应用程序可以利用Config Server返回YAML文档的功能。例如,调用https://sidecar.local.spring.io:5678/configserver/default-master.yml 可能会导致YAML文档类似于以下内容:
  1. eureka:
  2.   client:
  3.     serviceUrl:
  4.       defaultZone: http://localhost:8761/eureka/
  5.   password: password
  6. info:
  7.   description: Spring Cloud Samples
  8.   url: https://github.com/spring-cloud-samples
复制代码
要在使用HTTP时使运行状况检查请求接受所有证书,请将sidecar.accept-all-ssl-certificates设置为`true。
20.重试失败的请求

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Netflix提供了多种发出HTTP请求的方式。您可以使用负载均衡的RestTemplate,Ribbon或Feign。无论您如何选择创建HTTP请求,始终都有一个请求失败的机会。当请求失败时,您可能希望自动重试该请求。为此,在使用Sping Cloud Netflix时,您需要在应用程序的类路径中包含Spring重试。如果存在Spring重试,则负载平衡的RestTemplates,Feign和Zuul会自动重试任何失败的请求(假设您的配置允许这样做)。
20.1退避政策

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
默认情况下,重试请求时不使用任何退避策略。如果要配置退避策略,则需要创建类型为LoadBalancedRetryFactory的bean并为给定服务覆盖createBackOffPolicy方法,如以下示例所示:
  1. @Configuration
  2. public class MyConfiguration {
  3.     @Bean
  4.     LoadBalancedRetryFactory retryFactory() {
  5.         return new LoadBalancedRetryFactory() {
  6.             @Override
  7.             public BackOffPolicy createBackOffPolicy(String service) {
  8.                 return new ExponentialBackOffPolicy();
  9.             }
  10.         };
  11.     }
  12. }
复制代码
20.2配置

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
将Ribbon与Spring重试一起使用时,可以通过配置某些Ribbon属性来控制重试功能。为此,请设置client.ribbon.MaxAutoRetries,client.ribbon.MaxAutoRetriesNextServer和client.ribbon.OkToRetryOnAllOperations属性。有关这些属性的作用的说明,请参见Ribbon文档。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dEvjBdQz-1591774641430)(https://www.springcloud.cc/images/warning.png)]
启用client.ribbon.OkToRetryOnAllOperations包括重试POST请求,由于请求正文的缓冲,这可能会对服务器资源产生影响。
此外,当响应中返回某些状态代码时,您可能想重试请求。您可以通过设置clientName.ribbon.retryableStatusCodes属性来列出希望Ribbon客户端重试的响应代码,如以下示例所示:
  1. clientName:
  2.   ribbon:
  3.     retryableStatusCodes: 404,502
复制代码
您也可以创建类型为LoadBalancedRetryPolicy的bean,并实现retryableStatusCode方法以根据状态码重试请求。
20.2.1 Zuul

您可以通过将zuul.retryable设置为false来关闭Zuul的重试功能。您还可以通过将zuul.routes.routename.retryable设置为false来逐个路由禁用重试功能。
21. HTTP客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Netflix会自动为您创建Ribbon,Feign和Zuul使用的HTTP客户端。但是,您也可以根据需要提供自定义的HTTP客户端。为此,如果使用的是Apache Http Cient,则可以创建类型为ClosableHttpClient的bean,如果使用的是OK HTTP,则可以创建类型为OkHttpClient的bean。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-PYlsye59-1591774641431)(https://www.springcloud.cc/images/note.png)]
创建自己的HTTP客户端时,您还负责为这些客户端实施正确的连接管理策略。这样做不当会导致资源管理问题。
22.维护模式下的模块

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
将模块置于维护模式意味着Spring Cloud团队将不再向模块添加新功能。我们将修复阻止程序错误和安全性问题,还将考虑并审查社区的一些小请求。
自Greenwich 发布列车全面上市以来,我们打算继续为这些模块提供至少一年的支持。
以下Spring Cloud Netflix模块和相应的启动器将进入维护模式:
    spring-cloud-netflix-archaiusspring-cloud-netflix-hystrix-contractspring-cloud-netflix-hystrix-dashboardspring-cloud-netflix-hystrix-streamspring-cloud-netflix-hystrixspring-cloud-netflix-ribbonspring-cloud-netflix-turbine-streamspring-cloud-netflix-turbinespring-cloud-netflix-zuul
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-1xMYDEmh-1591774641431)(https://www.springcloud.cc/images/note.png)]
这不包括Eureka或并发限制模块。

第四部分 Spring Cloud OpenFeign

Greenwich SR5
该项目通过自动配置并绑定到Spring环境和其他Spring编程模型习惯用法,为Spring Boot应用提供了OpenFeign集成。
23.声明式REST客户端:Feign

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Feign是声明性的web服务客户端。它使编写web服务客户端更加容易。要使用Feign,请创建一个接口并对其进行注释。它具有可插入的注释支持,包括Feign注释和JAX-RS注释。Feign还支持可插拔编码器和解码器。Spring Cloud添加了对Spring MVC注释的支持,并支持使用Spring Web中默认使用的同一HttpMessageConverters。Spring Cloud集成了Ribbon和Eureka以在使用Feign时提供负载平衡的http客户端。
23.1如何包括Feign

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要将Feign包含在您的项目中,请将启动器与组org.springframework.cloud和工件ID spring-cloud-starter-openfeign一起使用。有关 使用当前Spring Cloud版本Train设置构建系统的详细信息,请参见Spring Cloud项目页面。
示例spring boot应用
  1. @SpringBootApplication
  2. @EnableFeignClients
  3. public class Application {
  4.     public static void main(String[] args) {
  5.         SpringApplication.run(Application.class, args);
  6.     }
  7. }
复制代码
StoreClient.java。
  1. @FeignClient("stores")
  2. public interface StoreClient {
  3.     @RequestMapping(method = RequestMethod.GET, value = "/stores")
  4.     List<Store> getStores();
  5.     @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
  6.     Store update(@PathVariable("storeId") Long storeId, Store store);
  7. }
复制代码
在@FeignClient批注中,字符串值(上面的“ stores”)是一个任意的客户端名称,用于创建Ribbon负载均衡器(请参见下面的Ribbon support的详细信息)。您还可以使用url属性(绝对值或仅是主机名)来指定URL。在应用程序上下文中,bean的名称是接口的标准名称。要指定自己的别名值,可以使用@FeignClient批注的qualifier值。
上面的Ribbon客户端将希望发现“商店”服务的物理地址。如果您的应用程序是Eureka客户端,则它将在Eureka服务注册表中解析该服务。如果您不想使用Eureka,则可以简单地在外部配置中配置服务器列表(例如,参见 上文)。
23.2覆盖Feign默认值

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud的Feign支持中的中心概念是指定客户的概念。每个虚拟客户端都是组件的一部分,这些组件可以一起工作以按需联系远程服务器,并且该组件的名称是您使用@FeignClient批注将其指定为应用程序开发人员的。Spring Cloud根据需要使用FeignClientsConfiguration为每个命名客户端创建一个新的合奏作为ApplicationContext。其中包含feign.Decoder,feign.Encoder和feign.Contract。通过使用@FeignClient批注的contextId属性,可以覆盖该集合的名称。
Spring Cloud使您可以通过使用@FeignClient声明其他配置(在FeignClientsConfiguration之上)来完全控制假客户端。例:
  1. @FeignClient(name = "stores", configuration = FooConfiguration.class)
  2. public interface StoreClient {
  3.     //..
  4. }
复制代码
在这种情况下,客户端由FeignClientsConfiguration中已有的组件以及FooConfiguration中的任何组件组成(其中后者将覆盖前者)。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IJJXzRUi-1591774641432)(https://www.springcloud.cc/images/note.png)]
FooConfiguration不需要用@Configuration进行注释。但是,如果是的话,请注意将其从任何可能包含此配置的@ComponentScan中排除,因为它将成为feign.Decoder,feign.Encoder,feign.Contract等的默认来源,指定时。可以通过将其与任何@ComponentScan或@SpringBootApplication放在单独的,不重叠的包中来避免这种情况,也可以在@ComponentScan中将其明确排除在外。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EaPBPB0I-1591774641433)(https://www.springcloud.cc/images/note.png)]
现在不推荐使用serviceId属性,而推荐使用name属性。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mOyGhQOd-1591774641433)(https://www.springcloud.cc/images/note.png)]
除了更改ApplicationContext集合的名称之外,还使用@FeignClient批注的contextId属性,它会覆盖客户端名称的别名,并将其用作配置名称的一部分bean为该客户端创建的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-VRZ1ZupC-1591774641434)(https://www.springcloud.cc/images/warning.png)]
以前,使用url属性不需要name属性。现在需要使用name。
name和url属性中支持占位符。
  1. @FeignClient(name = "${feign.name}", url = "${feign.url}")
  2. public interface StoreClient {
  3.     //..
  4. }
复制代码
Spring Cloud Netflix默认提供以下beans伪装(BeanType beanName:ClassName):
    Decoder feignDecoder:ResponseEntityDecoder(包装SpringDecoder)Encoder feignEncoder:SpringEncoderLogger feignLogger:Slf4jLoggerContract feignContract:SpringMvcContractFeign.Builder feignBuilder:HystrixFeign.BuilderClient feignClient:如果启用了Ribbon,则它是LoadBalancerFeignClient,否则使用默认的伪装客户端。
可以通过分别将feign.okhttp.enabled或feign.httpclient.enabled设置为true并将其放在类路径中来使用OkHttpClient和ApacheHttpClient虚拟客户端。您可以自定义HTTP客户端,方法是在使用Apache时提供ClosableHttpClient的bean,在使用OK HTTP时提供OkHttpClient。
Spring Cloud Netflix 默认情况下不会为伪装提供以下beans,但仍会从应用程序上下文中查找以下类型的beans以创建伪装客户端:
    Logger.LevelRetryerErrorDecoderRequest.OptionsCollection<RequestInterceptor>SetterFactory
创建其中一种类型的bean并将其放置在@FeignClient配置中(例如上述FooConfiguration),您可以覆盖上述的每个beans。例:
  1. @Configuration
  2. public class FooConfiguration {
  3.     @Bean
  4.     public Contract feignContract() {
  5.         return new feign.Contract.Default();
  6.     }
  7.     @Bean
  8.     public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
  9.         return new BasicAuthRequestInterceptor("user", "password");
  10.     }
  11. }
复制代码
这将SpringMvcContract替换为feign.Contract.Default,并将RequestInterceptor添加到RequestInterceptor的集合中。
@FeignClient也可以使用配置属性进行配置。
application.yml
  1. feign:
  2.   client:
  3.     config:
  4.       feignName:
  5.         connectTimeout: 5000
  6.         readTimeout: 5000
  7.         loggerLevel: full
  8.         errorDecoder: com.example.SimpleErrorDecoder
  9.         retryer: com.example.SimpleRetryer
  10.         requestInterceptors:
  11.           - com.example.FooRequestInterceptor
  12.           - com.example.BarRequestInterceptor
  13.         decode404: false
  14.         encoder: com.example.SimpleEncoder
  15.         decoder: com.example.SimpleDecoder
  16.         contract: com.example.SimpleContract
复制代码
可以按照与上述类似的方式在@EnableFeignClients属性defaultConfiguration中指定默认配置。不同之处在于此配置将适用于所有伪客户端。
如果您希望使用配置属性来配置所有@FeignClient,则可以使用default虚拟名称创建配置属性。
application.yml
  1. feign:
  2.   client:
  3.     config:
  4.       default:
  5.         connectTimeout: 5000
  6.         readTimeout: 5000
  7.         loggerLevel: basic
复制代码
如果我们同时创建@Configuration bean和配置属性,则配置属性将获胜。它将覆盖@Configuration值。但是,如果要将优先级更改为@Configuration,可以将feign.client.default-to-properties更改为false。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zds2O9e0-1591774641435)(https://www.springcloud.cc/images/note.png)]
如果您需要在RequestInterceptors you will need to either set the thread isolation strategy for Hystrix to SEMAPHORE中使用ThreadLocal绑定变量,或者在Feign中禁用Hystrix。
application.yml
  1. # To disable Hystrix in Feign
  2. feign:
  3.   hystrix:
  4.     enabled: false
  5. # To set thread isolation to SEMAPHORE
  6. hystrix:
  7.   command:
  8.     default:
  9.       execution:
  10.         isolation:
  11.           strategy: SEMAPHORE
复制代码
如果我们要创建多个具有相同名称或URL的伪装客户端,以便它们指向同一台服务器,但每个客户端使用不同的自定义配置,则必须使用@FeignClient的contextId属性,以避免这些配置beans的名称冲突。
  1. @FeignClient(contextId = "fooClient", name = "stores", configuration = FooConfiguration.class)
  2. public interface FooClient {
  3.     //..
  4. }
  5. @FeignClient(contextId = "barClient", name = "stores", configuration = BarConfiguration.class)
  6. public interface BarClient {
  7.     //..
  8. }
复制代码
23.3手动创建Feign客户

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
在某些情况下,可能有必要使用上述方法无法实现的方式自定义Feign客户。在这种情况下,您可以使用Feign Builder API创建客户端 。下面是一个示例,该示例创建两个具有相同接口的Feign客户端,但为每个客户端配置一个单独的请求拦截器。
  1. @Import(FeignClientsConfiguration.class)
  2. class FooController {
  3.         private FooClient fooClient;
  4.         private FooClient adminClient;
  5.             @Autowired
  6.         public FooController(Decoder decoder, Encoder encoder, Client client, Contract contract) {
  7.                 this.fooClient = Feign.builder().client(client)
  8.                                 .encoder(encoder)
  9.                                 .decoder(decoder)
  10.                                 .contract(contract)
  11.                                 .requestInterceptor(new BasicAuthRequestInterceptor("user", "user"))
  12.                                 .target(FooClient.class, "http://PROD-SVC");
  13.                 this.adminClient = Feign.builder().client(client)
  14.                                 .encoder(encoder)
  15.                                 .decoder(decoder)
  16.                                 .contract(contract)
  17.                                 .requestInterceptor(new BasicAuthRequestInterceptor("admin", "admin"))
  18.                                 .target(FooClient.class, "http://PROD-SVC");
  19.     }
  20. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TxsoIMaz-1591774641435)(https://www.springcloud.cc/images/note.png)]
在上面的示例中,FeignClientsConfiguration.class是Spring Cloud Netflix提供的默认配置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-vYUywCnJ-1591774641436)(https://www.springcloud.cc/images/note.png)]
PROD-SVC是客户将向其请求的服务的名称。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-TLJ0Euxs-1591774641436)(https://www.springcloud.cc/images/note.png)]
Feign Contract对象定义在接口上有效的注释和值。自动连线的Contract bean提供对SpringMVC注释的支持,而不是默认的Feign本机注释。
23.4 Feign Hystrix支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如果Hystrix在类路径上并且在feign.hystrix.enabled=true上,则Feign将使用断路器包装所有方法。还可以返回com.netflix.hystrix.HystrixCommand。这使您可以使用反应性模式(通过调用.toObservable()或.observe()或异步使用(通过调用.queue())。
要基于每个客户端禁用Hystrix支持,请创建具有{prototype“范围的普通Feign.Builder,例如:
  1. @Configuration
  2. public class FooConfiguration {
  3.             @Bean
  4.         @Scope("prototype")
  5.         public Feign.Builder feignBuilder() {
  6.                 return Feign.builder();
  7.         }
  8. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ZiuD4JLF-1591774641437)(https://www.springcloud.cc/images/warning.png)]
在Spring Cloud Dalston发行版之前,如果Hystrix在类路径Feign上,则默认情况下会将所有方法包装在断路器中。Spring Cloud Dalston中对此默认行为进行了更改,以支持选择加入方法。
23.5 Feign Hystrix后备

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Hystrix支持回退的概念:当它们的电路断开或出现错误时执行的默认代码路径。要为给定的@FeignClient启用回退,请将fallback属性设置为实现回退的类名称。您还需要将实现声明为Spring bean。
  1. @FeignClient(name = "hello", fallback = HystrixClientFallback.class)
  2. protected interface HystrixClient {
  3.     @RequestMapping(method = RequestMethod.GET, value = "/hello")
  4.     Hello iFailSometimes();
  5. }
  6. static class HystrixClientFallback implements HystrixClient {
  7.     @Override
  8.     public Hello iFailSometimes() {
  9.         return new Hello("fallback");
  10.     }
  11. }
复制代码
如果需要访问引起后备触发器的原因,则可以使用@FeignClient中的fallbackFactory属性。
  1. @FeignClient(name = "hello", fallbackFactory = HystrixClientFallbackFactory.class)
  2. protected interface HystrixClient {
  3.         @RequestMapping(method = RequestMethod.GET, value = "/hello")
  4.         Hello iFailSometimes();
  5. }
  6. @Component
  7. static class HystrixClientFallbackFactory implements FallbackFactory<HystrixClient> {
  8.         @Override
  9.         public HystrixClient create(Throwable cause) {
  10.                 return new HystrixClient() {
  11.                         @Override
  12.                         public Hello iFailSometimes() {
  13.                                 return new Hello("fallback; reason was: " + cause.getMessage());
  14.                         }
  15.                 };
  16.         }
  17. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-69tzcYDX-1591774641438)(https://www.springcloud.cc/images/warning.png)]
Feign中的后备实现以及Hystrix后备如何工作存在局限性。返回com.netflix.hystrix.HystrixCommand和rx.Observable的方法当前不支持后备。
23.6 Feign和@Primary

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
当将Feign与后退Hystrix一起使用时,ApplicationContext中有多个相同类型的beans。这将导致@Autowired无法正常工作,因为没有一个bean或标记为主要的一个。要解决此问题,Spring Cloud Netflix将所有Feign实例标记为@Primary,因此Spring Framework将知道要插入哪个bean。在某些情况下,这可能不是理想的。要关闭此行为,请将@FeignClient的primary属性设置为false。
  1. @FeignClient(name = "hello", primary = false)
  2. public interface HelloClient {
  3.         // methods here
  4. }
复制代码
23.7 Feign继承支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Feign通过单继承接口支持样板API。这允许将常用操作分组为方便的基本接口。
UserService.java。
  1. public interface UserService {
  2.     @RequestMapping(method = RequestMethod.GET, value ="/users/{id}")
  3.     User getUser(@PathVariable("id") long id);
  4. }
复制代码
UserResource.java。
  1. @RestController
  2. public class UserResource implements UserService {
  3. }
复制代码
UserClient.java。
  1. package project.user;
  2. @FeignClient("users")
  3. public interface UserClient extends UserService {
  4. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-00TnRWCN-1591774641438)(https://www.springcloud.cc/images/note.png)]
通常不建议在服务器和客户端之间共享接口。它引入了紧密耦合,并且实际上也不能与当前形式的Spring MVC一起使用(方法参数映射不被继承)。
23.8 Feign请求/响应压缩

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您可以考虑为Feign请求启用请求或响应GZIP压缩。您可以通过启用以下属性之一来做到这一点:
  1. feign.compression.request.enabled=true
  2. feign.compression.response.enabled=true
复制代码
Feign请求压缩为您提供的设置类似于您为web服务器设置的设置:
  1. feign.compression.request.enabled=true
  2. feign.compression.request.mime-types=text/xml,application/xml,application/json
  3. feign.compression.request.min-request-size=2048
复制代码
这些属性使您可以选择压缩媒体类型和最小请求阈值长度。
23.9 Feign日志记录

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
为每个创建的Feign客户端创建一个记录器。默认情况下,记录器的名称是用于创建Feign客户端的接口的全类名称。Feign日志记录仅响应DEBUG级别。
application.yml。
  1. logging.level.project.user.UserClient: DEBUG
复制代码
您可以为每个客户端配置的Logger.Level对象告诉Feign要记录多少。选择是:
    NONE,无日志记录(DEFAULT)。BASIC,仅记录请求方法和URL以及响应状态代码和执行时间。HEADERS,记录基本信息以及请求和响应头。FULL,记录请求和响应的标题,正文和元数据。
例如,以下内容会将Logger.Level设置为FULL:
  1. @Configuration
  2. public class FooConfiguration {
  3.     @Bean
  4.     Logger.Level feignLoggerLevel() {
  5.         return Logger.Level.FULL;
  6.     }
  7. }
复制代码
23.10 Feign @QueryMap支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
OpenFeign @QueryMap批注支持将POJO用作GET参数映射。不幸的是,默认的OpenFeign QueryMap注释与Spring不兼容,因为它缺少value属性。
Spring Cloud OpenFeign提供等效的@SpringQueryMap批注,该批注用于将POJO或Map参数注释为查询参数映射。
例如,Params类定义参数param1和param2:
  1. // Params.java
  2. public class Params {
  3.     private String param1;
  4.     private String param2;
  5.     // [Getters and setters omitted for brevity]
  6. }
复制代码
以下伪装客户端通过使用@SpringQueryMap批注来使用Params类:
  1. @FeignClient("demo")
  2. public class DemoTemplate {
  3.     @GetMapping(path = "/demo")
  4.     String demoEndpoint(@SpringQueryMap Params params);
  5. }
复制代码
23.11故障排除

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
23.11.1早期初始化错误

根据您使用Feign客户端的方式,启动应用程序时可能会看到初始化错误。要变通解决此问题,您可以在自动接线客户端时使用ObjectProvider。
  1. @Autowired
  2. ObjectProvider<TestFeginClient> testFeginClient;
复制代码
第五部分。Spring Cloud Stream

24. Spring的数据集成之旅简史

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring的数据集成之旅始于Spring Integration。通过其编程模型,它为开发人员提供了一致的开发经验,以构建可以包含企业集成模式以与外部系统(例如数据库,消息代理等)连接的应用程序。
快进到云时代,微服务已在企业环境中变得突出。Spring Boot改变了开发人员构建应用程序的方式。借助Spring的编程模型和Spring Boot处理的运行时职责,无缝开发了基于生产,生产级Spring的独立微服务。
为了将其扩展到数据集成工作负载,Spring Integration和Spring Boot被放到一个新项目中。Spring Cloud Stream出生了。
使用Spring Cloud Stream,开发人员可以:*隔离地构建,测试,迭代和部署以数据为中心的应用程序。*应用现代微服务架构模式,包括通过消息传递进行组合。*以事件为中心的思维将应用程序职责分离。事件可以表示及时发生的事件,下游消费者应用程序可以在不知道事件起源或生产者身份的情况下做出反应。*将业务逻辑移植到消息代理(例如RabbitMQ,Apache Kafka,Amazon Kinesis)上。*通过使用项目Reactor的Flux和Kafka Streams API,可以在基于通道的应用程序和基于非通道的应用程序绑定方案之间进行互操作,以支持无状态和有状态的计算。*依靠框架对常见用例的自动内容类型支持。可以扩展到不同的数据转换类型。
25.快速入门

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您可以按照以下三步指南在不到5分钟的时间内尝试Spring Cloud Stream。
我们向您展示如何创建一个Spring Cloud Stream应用程序,该应用程序接收来自您选择的消息传递中间件的消息(稍后会详细介绍),并将接收到的消息记录到控制台。我们称之为LoggingConsumer。尽管不是很实用,但是它很好地介绍了一些主要概念和抽象,使您更容易理解本用户指南的其余部分。
三个步骤如下:
    第25.1节“使用Spring Initializr创建示例应用程序”第25.2节“将项目导入IDE”第25.3节“添加,处理和构建消息处理程序”
25.1使用Spring Initializr创建示例应用程序

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要开始使用,请访问Spring Initializr。从那里,您可以生成我们的LoggingConsumer应用程序。为此:
    在“ **依赖关系”**部分,开始输入stream。当“ 云流 ”选项出现时,选择它。
    开始输入“ kafka”或“兔子”。
    选择“ Kafka ”或“ RabbitMQ ”。
    基本上,您选择应用程序绑定到的消息传递中间件。我们建议您使用已经安装的那种,或者对安装和运行感到更自在。另外,从“启动程序”屏幕上可以看到,还有一些其他选项可以选择。例如,您可以选择Gradle作为构建工具,而不是Maven(默认设置)。
    工件字段中,输入“ logging-consumer”。
    Artifact字段的值成为应用程序名称。如果您选择RabbitMQ作为中间件,则Spring Initializr现在应该如下所示:
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-WA1urYEg-1591774641439)(https://www.springcloud.cc/images/stream-initializr.png)]
    单击生成项目按钮。
    这样做会将生成的项目的压缩版本下载到硬盘上。
    将文件解压缩到要用作项目目录的文件夹中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aqfL0UEG-1591774641440)(https://www.springcloud.cc/images/tip.png)]
我们鼓励您探索Spring Initializr中可用的许多可能性。它使您可以创建许多不同种类的Spring应用程序。
25.2将项目导入IDE

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
现在,您可以将项目导入到IDE中。请记住,取决于IDE,您可能需要遵循特定的导入过程。例如,根据项目的生成方式(Maven或Gradle),您可能需要遵循特定的导入过程(例如,在Eclipse或STS中,您需要使用File→Import→Maven→现有的Maven项目)。
导入后,该项目必须没有任何错误。另外,src/main/java应该包含com.example.loggingconsumer.LoggingConsumerApplication。
从技术上讲,此时,您可以运行应用程序的主类。它已经是有效的Spring Boot应用程序。但是,它没有任何作用,因此我们想添加一些代码。
25.3添加消息处理程序,构建并运行

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
修改com.example.loggingconsumer.LoggingConsumerApplication类,如下所示:
  1. @SpringBootApplication
  2. @EnableBinding(Sink.class)
  3. public class LoggingConsumerApplication {
  4.         public static void main(String[] args) {
  5.                 SpringApplication.run(LoggingConsumerApplication.class, args);
  6.         }
  7.         @StreamListener(Sink.INPUT)
  8.         public void handle(Person person) {
  9.                 System.out.println("Received: " + person);
  10.         }
  11.         public static class Person {
  12.                 private String name;
  13.                 public String getName() {
  14.                         return name;
  15.                 }
  16.                 public void setName(String name) {
  17.                         this.name = name;
  18.                 }
  19.                 public String toString() {
  20.                         return this.name;
  21.                 }
  22.         }
  23. }
复制代码
从前面的清单中可以看到:
    我们已经使用@EnableBinding(Sink.class)启用了Sink绑定(输入无输出)。这样做会向框架发出信号,以启动对消息传递中间件的绑定,在该消息传递中间件自动创建绑定到Sink.INPUT通道的目的地(即队列,主题和其他)。我们添加了一个handler方法来接收类型为Person的传入消息。这样做可以使您看到框架的核心功能之一:它尝试自动将传入的消息有效负载转换为类型Person。
您现在有了一个功能齐全的Spring Cloud Stream应用程序,该应用程序确实侦听消息。为了简单起见,我们从这里开始,假设您在第一步中选择了RabbitMQ 。假设已经安装并运行了RabbitMQ,则可以通过在IDE中运行其main方法来启动应用程序。
您应该看到以下输出:
  1.         --- [ main] c.s.b.r.p.RabbitExchangeQueueProvisioner : declaring queue for inbound: input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg, bound to: input
  2.         --- [ main] o.s.a.r.c.CachingConnectionFactory       : Attempting to connect to: [localhost:5672]
  3.         --- [ main] o.s.a.r.c.CachingConnectionFactory       : Created new connection: rabbitConnectionFactory#2a3a299:0/SimpleConnection@66c83fc8. . .
  4.         . . .
  5.         --- [ main] o.s.i.a.i.AmqpInboundChannelAdapter      : started inbound.input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg
  6.         . . .
  7.         --- [ main] c.e.l.LoggingConsumerApplication         : Started LoggingConsumerApplication in 2.531 seconds (JVM running for 2.897)
复制代码
转到RabbitMQ管理控制台或任何其他RabbitMQ客户端,然后向input.anonymous.CbMIwdkJSBO1ZoPDOtHtCg发送消息。anonymous.CbMIwdkJSBO1ZoPDOtHtCg部分代表组名并已生成,因此在您的环境中它一定是不同的。对于更可预测的内容,可以通过设置spring.cloud.stream.bindings.input.group=hello(或您喜欢的任何名称)来使用显式组名。
消息的内容应为Person类的JSON表示形式,如下所示:
  1. {"name":"Sam Spade"}
复制代码
然后,在控制台中,您应该看到:
  1. Received: Sam Spade
复制代码
您还可以将应用程序生成并打包到引导jar中(使用./mvnw clean install),并使用java -jar命令运行生成的JAR。
现在,您有了一个正在运行的(尽管非常基础的)Spring Cloud Stream应用程序。
26. 2.0中有什么新功能?

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream引入了许多新功能,增强功能和更改。以下各节概述了最值得注意的部分:
    第26.1节“新功能和组件”第26.2节“显着增强”
26.1新功能和组件

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
    轮询使用者:引入轮询使用者,使应用程序可以控制消息处理速率。请参见 “ 第29.3.5,‘使用轮询消费者 ’ ”的更多细节。您也可以阅读此博客文章以获取更多详细信息。千分尺支持:度量标准已切换为使用千分尺。MeterRegistry也以bean的形式提供,以便自定义应用程序可以将其自动连线以捕获自定义指标。有关更多详细信息,请参见 “ 第37章,度量标准发射器 ”。新的执行器绑定控件:新的执行器绑定控件使您可以可视化并控制绑定的生命周期。有关更多详细信息,请参见第30.6节“绑定可视化和控件”。可配置的RetryTemplate:除了提供配置RetryTemplate的属性外,我们现在还允许您提供自己的模板,有效地覆盖了框架提供的模板。要使用它,请在您的应用程序中将其配置为@Bean。
26.2显着增强

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
此版本包括以下显着增强:
    第26.2.1节“致动器和Web依赖关系现在都是可选的”第26.2.2节“内容类型协商的改进”第26.3节“显着的弃用”
26.2.1执行器和Web依赖项现在都是可选的

如果既不需要执行器也不需要web依赖项,那么此更改将减少已部署应用程序的占用空间。通过手动添加以下依赖项之一,它还使您可以在反应式和常规web范式之间切换。
以下清单显示了如何添加常规的web框架:
  1. <dependency>
  2.         <groupId>org.springframework.boot</groupId>
  3.         <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
复制代码
以下清单显示了如何添加反应式web框架:
  1. <dependency>
  2.         <groupId>org.springframework.boot</groupId>
  3.         <artifactId>spring-boot-starter-webflux</artifactId>
  4. </dependency>
复制代码
下表显示了如何添加执行器依赖性:
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-actuator</artifactId>
  4. </dependency>
复制代码
26.2.2内容类型协商的改进

Verion 2.0的核心主题之一是围绕内容类型协商和消息转换的改进(在一致性和性能方面)。以下摘要概述了该领域的显着变化和改进。有关更多详细信息,请参见“ 第32章,内容类型协商 ”部分。此外,此博客文章还包含更多详细信息。
    现在,所有消息转换由MessageConverter对象处理。我们引入了@StreamMessageConverter批注以提供自定义MessageConverter对象。我们引入了默认的Content Type作为application/json,在迁移1.3应用程序或以混合模式(即1.3生产者→2.0消费者)进行操作时,需要考虑该默认值。在无法确定所提供的MessageHandler的参数类型的情况下,带有文本有效载荷且contentType为text/…或…/json的消息不再转换为Message<String>。 public void handle(Message<?> message)或public void handle(Object payload))。此外,强参数类型可能不足以正确地转换消息,因此contentType标头可能被某些MessageConverters用作补充。
26.3显着弃用

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
从2.0版开始,不推荐使用以下项目:
    第26.3.1节“ Java序列化(Java Native和Kryo)”第26.3.2节“不推荐使用的类和方法”
26.3.1 Java序列化(Java本机和Kryo)

JavaSerializationMessageConverter和KryoMessageConverter暂时保留。但是,我们计划将来将它们移出核心软件包和支持。弃用此文件的主要原因是要标记基于类型,特定于语言的序列化可能在分布式环境中引起的问题,在该环境中,生产者和使用者可能依赖于不同的JVM版本或具有不同版本的支持库(即Kryo)。我们还想提请注意这样一个事实,即消费者和生产者甚至可能都不是基于Java的,因此多语言风格的序列化(即JSON)更适合。
26.3.2不推荐使用的类和方法

以下是显着弃用的快速摘要。有关更多详细信息,请参见相应的{spring-cloud-stream-javadoc-current} [javadoc]。
    SharedChannelRegistry.使用SharedBindingTargetRegistry。Bindings.符合条件的Beans已通过其类型唯一标识,例如,提供了Source,Processor或自定义绑定:
  1. public interface Sample {
  2.         String OUTPUT = "sampleOutput";
  3.         @Output(Sample.OUTPUT)
  4.         MessageChannel output();
  5. }
复制代码
    HeaderMode.raw.使用none,headers或embeddedHeadersProducerProperties.partitionKeyExtractorClass赞成partitionKeyExtractorName,而ProducerProperties.partitionSelectorClass赞成partitionSelectorName。此更改可确保Spring配置和管理两个组件,并以Spring友好的方式对其进行引用。BinderAwareRouterBeanPostProcessor.在保留该组件的同时,它不再是BeanPostProcessor,并且将来会重命名。BinderProperties.setEnvironment(Properties environment).使用BinderProperties.setEnvironment(Map<String, Object> environment)。
本节将详细介绍如何使用Spring Cloud Stream。它涵盖了诸如创建和运行流应用程序之类的主题。
27.简介Spring Cloud Stream

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream是用于构建消息驱动的微服务应用程序的框架。Spring Cloud Stream在Spring Boot的基础上创建了独立的生产级Spring应用程序,并使用Spring Integration提供了到消息代理的连接。它提供了来自多家供应商的中间件的合理配置,并介绍了持久性发布-订阅语义,使用者组和分区的概念。
您可以在应用程序中添加@EnableBinding批注,以立即连接到消息代理,还可以在方法中添加@StreamListener,以使其接收流处理的事件。以下示例显示了接收外部消息的接收器应用程序:
  1. @SpringBootApplication
  2. @EnableBinding(Sink.class)
  3. public class VoteRecordingSinkApplication {
  4.   public static void main(String[] args) {
  5.     SpringApplication.run(VoteRecordingSinkApplication.class, args);
  6.   }
  7.   @StreamListener(Sink.INPUT)
  8.   public void processVote(Vote vote) {
  9.       votingService.recordVote(vote);
  10.   }
  11. }
复制代码
@EnableBinding批注将一个或多个接口作为参数(在这种情况下,该参数是单个Sink接口)。接口声明输入和输出通道。Spring Cloud Stream提供了Source,Sink和Processor接口。您也可以定义自己的接口。
以下清单显示了Sink接口的定义:
  1. public interface Sink {
  2.   String INPUT = "input";
  3.   @Input(Sink.INPUT)
  4.   SubscribableChannel input();
  5. }
复制代码
@Input注释标识一个输入通道,接收到的消息通过该输入通道进入应用程序。@Output注释标识一个输出通道,已发布的消息通过该输出通道离开应用程序。@Input和@Output批注可以使用频道名称作为参数。如果未提供名称,则使用带注释的方法的名称。
Spring Cloud Stream为您创建接口的实现。您可以通过自动装配在应用程序中使用它,如以下示例所示(来自测试用例):
  1. @RunWith(SpringJUnit4ClassRunner.class)
  2. @SpringApplicationConfiguration(classes = VoteRecordingSinkApplication.class)
  3. @WebAppConfiguration
  4. @DirtiesContext
  5. public class StreamApplicationTests {
  6.   @Autowired
  7.   private Sink sink;
  8.   @Test
  9.   public void contextLoads() {
  10.     assertNotNull(this.sink.input());
  11.   }
  12. }
复制代码
28.主要概念

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream提供了许多抽象和原语,简化了消息驱动的微服务应用程序的编写。本节概述以下内容:
    Spring Cloud Stream的应用程序模型第28.2节“ Binder抽象”持久的发布-订阅支持消费者群体支持分区支持可插拔的Binder SPI
28.1应用模型

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream应用程序由与中间件无关的内核组成。该应用程序通过Spring Cloud Stream注入到其中的输入和输出通道与外界进行通信。通道通过特定于中间件的Binder实现与外部代理连接。
图28.1 Spring Cloud Stream申请
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-AVxaac72-1591774641440)(https://www.springcloud.cc/images/SCSt-with-binder.png)]
28.1.1 Fat JAR

可以从IDE以独立模式运行Spring Cloud Stream应用程序以进行测试。要在生产环境中运行Spring Cloud Stream应用程序,您可以使用为Maven或Gradle提供的标准Spring Boot工具来创建可执行(或“ fat ”)JAR。有关更多详细信息,请参见Spring Boot参考指南。
28.2 Binder抽象

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream为Kafka和Rabbit MQ提供了Binder实现。Spring Cloud Stream还包括一个TestSupportBinder,它使通道保持不变,因此测试可以与通道直接交互并可靠地断言所接收的内容。您也可以使用可扩展的API编写自己的Binder。
Spring Cloud Stream使用Spring Boot进行配置,而Binder抽象使Spring Cloud Stream应用程序可以灵活地连接中间件。例如,部署者可以在运行时动态选择通道连接到的目的地(例如Kafka主题或RabbitMQ交换)。可以通过外部配置属性以及Spring Boot支持的任何形式(包括应用程序参数,环境变量以及application.yml或application.properties文件)提供这种配置。在第27章“ 介绍Spring Cloud Stream”的接收器示例中,将spring.cloud.stream.bindings.input.destination应用程序属性设置为raw-sensor-data会使其从raw-sensor-data Kafka主题或绑定到该队列的队列中读取raw-sensor-data RabbitMQ交换。
Spring Cloud Stream自动检测并使用在类路径上找到的活页夹。您可以使用具有相同代码的不同类型的中间件。为此,在构建时包括一个不同的活页夹。对于更复杂的用例,您还可以在应用程序中打包多个活页夹,并在运行时选择活页夹(甚至为不同的通道使用不同的活页夹)。
28.3持久的发布-订阅支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
应用程序之间的通信遵循发布-订阅模型,其中数据通过共享主题进行广播。在下图中可以看到,该图显示了一组交互的Spring Cloud Stream应用程序的典型部署。
图28.2 Spring Cloud Stream发布-订阅
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-erdUoxzx-1591774641441)(https://www.springcloud.cc/images/SCSt-sensors.png)]
传感器报告给HTTP端点的数据将发送到名为raw-sensor-data的公共目标。从目的地开始,它由计算时间窗平均值的微服务应用程序和另一个将原始数据提取到HDFS(Hadoop分布式文件系统)的微服务应用程序独立处理。为了处理数据,两个应用程序都在运行时将主题声明为其输入。
发布-订阅通信模型降低了生产者和使用者的复杂性,并允许在不中断现有流程的情况下将新应用添加到拓扑中。例如,在平均计算应用程序的下游,您可以添加一个应用程序,该应用程序计算用于显示和监视的最高温度值。然后,您可以添加另一个解释相同平均值流以进行故障检测的应用程序。通过共享主题而不是点对点队列进行所有通信可以减少微服务之间的耦合。
尽管发布-订阅消息传递的概念并不是新概念,但是Spring Cloud Stream采取了额外的步骤,使其成为其应用程序模型的明智选择。通过使用本机中间件支持,Spring Cloud Stream还简化了跨不同平台的发布-订阅模型的使用。
28.4消费群体

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
尽管发布-订阅模型使通过共享主题轻松连接应用程序变得很重要,但是通过创建给定应用程序的多个实例进行扩展的能力同样重要。这样做时,会将应用程序的不同实例置于竞争的消费者关系中,在该消费者关系中,仅其中一个实例可以处理给定消息。
Spring Cloud Stream通过消费者群体的概念对这种行为进行建模。(Spring Cloud Stream消费者组类似于Kafka消费者组并受其启发。)每个消费者绑定都可以使用spring.cloud.stream.bindings.<channelName>.group属性来指定组名。对于下图所示的消费者,此属性将设置为spring.cloud.stream.bindings.<channelName>.group=hdfsWrite或spring.cloud.stream.bindings.<channelName>.group=average。
图28.3 Spring Cloud Stream消费群体
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6adfvdns-1591774641441)(https://www.springcloud.cc/images/SCSt-groups.png)]
订阅给定目标的所有组都将收到已发布数据的副本,但是每个组中只有一个成员从该目标接收给定消息。默认情况下,未指定组时,Spring Cloud Stream会将应用程序分配给与所有其他使用者组具有发布-订阅关系的匿名且独立的单成员使用者组。
28.5消费者类型

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
支持两种类型的使用者:
    消息驱动(有时称为异步)轮询(有时称为同步)
在2.0版之前,仅支持异步使用者。消息一旦可用,就会被传递,并且有线程可以处理它。
当您希望控制消息的处理速率时,可能需要使用同步使用者。
28.5.1耐久性

与公认的Spring Cloud Stream应用程序模型一致,消费者组订阅是持久的。也就是说,活页夹实现可确保组订阅是持久的,并且一旦创建了至少一个组订阅,该组将接收消息,即使在组中所有应用程序停止时发送消息也是如此。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FsFqPB5U-1591774641441)(https://www.springcloud.cc/images/note.png)]
匿名订阅本质上是非持久的。对于某些活页夹实现(例如RabbitMQ),可能具有非持久的组订阅。
通常,在将应用程序绑定到给定目标时,最好始终指定使用者组。扩展Spring Cloud Stream应用程序时,必须为其每个输入绑定指定使用者组。这样做可以防止应用程序的实例接收重复的消息(除非需要这种行为,这是不寻常的)。
28.6分区支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream支持在给定应用程序的多个实例之间分区数据。在分区方案中,物理通信介质(例如代理主题)被视为结构化为多个分区。一个或多个生产者应用程序实例将数据发送到多个消费者应用程序实例,并确保由共同特征标识的数据由同一消费者实例处理。
Spring Cloud Stream提供了用于以统一方式实现分区处理用例的通用抽象。因此,无论代理本身是否自然地被分区(例如,Kafka)(例如,RabbitMQ),都可以使用分区。
图28.4。Spring Cloud Stream分区
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-8IQFc3OA-1591774641442)(https://www.springcloud.cc/images/SCSt-partitioning.png)]
分区是有状态处理中的关键概念,对于确保所有相关数据都一起处理,分区是至关重要的(出于性能或一致性方面的考虑)。例如,在带时间窗的平均计算示例中,重要的是,来自任何给定传感器的所有测量都应由同一应用实例处理。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-APPSeXMK-1591774641442)(https://www.springcloud.cc/images/note.png)]
要设置分区处理方案,必须同时配置数据产生端和数据消耗端。
29.编程模型

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
要了解编程模型,您应该熟悉以下核心概念:
    **目标Binders:**负责与外部消息传递系统集成的组件。**目标绑定:**在外部消息传递系统和应用程序之间提供的消息的生产者使用者(由目标Binders创建)之间的桥梁。**消息:**生产者和消费者用来与目的地Binders(以及因此通过外部消息系统进行的其他应用程序)通信的规范数据结构。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xYuH9zeq-1591774641443)(https://www.springcloud.cc/images/SCSt-overview.png)]
29.1目的地Binders

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
目标Binders是Spring Cloud Stream的扩展组件,负责提供必要的配置和实现以促进与外部消息传递系统的集成。这种集成负责连接,委派和与生产者和消费者之间的消息路由,数据类型转换,用户代码调用等等。
Binders承担了许多样板工作,否则这些工作就落在了您的肩上。但是,要实现这一点,活页夹仍然需要用户提供的一些简单但需要的指令集形式的帮助,通常以某种类型的配置形式出现。
尽管讨论所有可用的绑定器和绑定配置选项(本手册的其余部分都涉及它们)不在本节的讨论范围之内,但 目标绑定确实需要特别注意。下一节将详细讨论。
29.2目标绑定

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如前所述,目标绑定提供了外部消息传递系统与应用程序提供的生产者消费者之间的桥梁。
将@EnableBinding批注应用于应用程序的配置类之一可定义目标绑定。@EnableBinding注释本身使用@Configuration进行元注释,并触发Spring Cloud Stream基础结构的配置。
下面的示例显示了一个功能完整且运行正常的Spring Cloud Stream应用程序,该应用程序从INPUT目标接收的消息净荷为String类型(请参见第32章,内容类型协商部分),并将其记录到控制台,并将其转换为大写字母后将其发送到OUTPUT目标。
  1. @SpringBootApplication
  2. @EnableBinding(Processor.class)
  3. public class MyApplication {
  4.         public static void main(String[] args) {
  5.                 SpringApplication.run(MyApplication.class, args);
  6.         }
  7.         @StreamListener(Processor.INPUT)
  8.         @SendTo(Processor.OUTPUT)
  9.         public String handle(String value) {
  10.                 System.out.println("Received: " + value);
  11.                 return value.toUpperCase();
  12.         }
  13. }
复制代码
如您所见,@EnableBinding批注可以将一个或多个接口类作为参数。这些参数称为绑定,它们包含表示可绑定组件的方法。这些组件通常是基于通道的活页夹(例如Rabbit,Kafka等)的消息通道(请参见Spring消息传递)。但是,其他类型的绑定可以为相应技术的本机功能提供支持。例如,Kafka Streams绑定器(以前称为KStream)允许直接绑定到Kafka Streams(有关更多详细信息,请参见Kafka Streams)。
Spring Cloud Stream已经为典型的消息交换合同提供了绑定接口,其中包括:
    **接收器:**通过提供消费消息的目的地来标识消息消费者的合同。**源:**通过提供将生成的消息发送到的目的地,来标识消息生产者的合同。**处理器:**通过公开两个允许使用和产生消息的目的地,封装了接收器和源协定。
  1. public interface Sink {
  2.   String INPUT = "input";
  3.   @Input(Sink.INPUT)
  4.   SubscribableChannel input();
  5. }
  6. public interface Source {
  7.   String OUTPUT = "output";
  8.   @Output(Source.OUTPUT)
  9.   MessageChannel output();
  10. }
  11. public interface Processor extends Source, Sink {}
复制代码
尽管前面的示例满足了大多数情况,但是您也可以通过定义自己的绑定接口并使用@Input和@Output批注来标识实际的可绑定组件,从而定义自己的合同。
例如:
  1. public interface Barista {
  2.     @Input
  3.     SubscribableChannel orders();
  4.     @Output
  5.     MessageChannel hotDrinks();
  6.     @Output
  7.     MessageChannel coldDrinks();
  8. }
复制代码
将上一个示例中显示的接口用作@EnableBinding的参数将分别触发三个绑定通道的创建,分别命名为orders,hotDrinks和coldDrinks。
您可以根据需要提供任意数量的绑定接口,作为@EnableBinding批注的参数,如以下示例所示:
  1. @EnableBinding(value = { Orders.class, Payment.class })
复制代码
在Spring Cloud Stream中,可绑定的MessageChannel组件是Spring消息传递MessageChannel(用于出站)及其扩展名SubscribableChannel(用于入站)。
可轮询的目标绑定
尽管前面描述的绑定支持基于事件的消息使用,但是有时您需要更多控制,例如使用率。
从2.0版开始,您现在可以绑定可轮询的使用者:
以下示例显示了如何绑定可轮询的使用者:
  1. public interface PolledBarista {
  2.     @Input
  3.     PollableMessageSource orders();
  4.         . . .
  5. }
复制代码
在这种情况下,PollableMessageSource的实现绑定到orders“通道”。有关更多详细信息,请参见第29.3.5节“使用轮询的使用者”。
自定义频道名称
通过使用@Input和@Output批注,可以为该通道指定自定义的通道名称,如以下示例所示:
  1. public interface Barista {
  2.     @Input("inboundOrders")
  3.     SubscribableChannel orders();
  4. }
复制代码
在前面的示例中,创建的绑定通道被命名为inboundOrders。
通常,您不需要直接访问各个通道或绑定(除非通过@EnableBinding注释对其进行配置)。但是,您有时可能会遇到诸如测试或其他极端情况的情况。
除了为每个绑定生成通道并将其注册为Spring beans外,对于每个绑定接口,Spring Cloud Stream还会生成一个实现该接口的bean。这意味着您可以通过在应用程序中自动接线来访问表示绑定或各个通道的接口,如以下两个示例所示:
自动接线绑定界面
  1. @Autowire
  2. private Source source
  3. public void sayHello(String name) {
  4.     source.output().send(MessageBuilder.withPayload(name).build());
  5. }
复制代码
自动连线个别频道
  1. @Autowire
  2. private MessageChannel output;
  3. public void sayHello(String name) {
  4.     output.send(MessageBuilder.withPayload(name).build());
  5. }
复制代码
对于自定义通道名称或在需要特别命名通道的多通道方案中,您也可以使用标准Spring的@Qualifier批注。
下面的示例演示如何以这种方式使用@Qualifier批注:
  1. @Autowire
  2. @Qualifier("myChannel")
  3. private MessageChannel output;
复制代码
29.3产生和消耗消息

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
您可以使用Spring Integration注释或Spring Cloud Stream本机注释编写Spring Cloud Stream应用程序。
29.3.1 Spring Integration支持

Spring Cloud Stream建立在Enterprise Integration Patterns定义的概念和模式的基础之上,并依靠其内部实现依赖于Spring项目组合Spring Integration框架中已经建立且流行的Enterprise Integration Patterns实现 。
因此,它支持Spring Integration已经建立的基础,语义和配置选项是很自然的。
例如,您可以将Source的输出通道附加到MessageSource并使用熟悉的@InboundChannelAdapter注释,如下所示:
  1. @EnableBinding(Source.class)
  2. public class TimerSource {
  3.   @Bean
  4.   @InboundChannelAdapter(value = Source.OUTPUT, poller = @Poller(fixedDelay = "10", maxMessagesPerPoll = "1"))
  5.   public MessageSource<String> timerMessageSource() {
  6.     return () -> new GenericMessage<>("Hello Spring Cloud Stream");
  7.   }
  8. }
复制代码
同样,可以在提供处理器绑定合同的消息处理程序方法的实现时使用@Transformer或@ServiceActivator ,如以下示例所示:
  1. @EnableBinding(Processor.class)
  2. public class TransformProcessor {
  3.   @Transformer(inputChannel = Processor.INPUT, outputChannel = Processor.OUTPUT)
  4.   public Object transform(String message) {
  5.     return message.toUpperCase();
  6.   }
  7. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-iRsjfkRN-1591774641443)(https://www.springcloud.cc/images/note.png)]
尽管这可能会略过一些,但重要的是要了解,当您使用@StreamListener注释从同一绑定进行消费时,将使用pub-sub模型。用@StreamListener注释的每种方法都收到其自己的消息副本,并且每种方法都有其自己的使用者组。但是,如果通过使用Spring Integration批注之一(例如@Aggregator,@Transformer或@ServiceActivator)从同一绑定中消费,则这些消费在竞争模型中。没有为每个订阅创建单独的消费者组。
29.3.2使用@StreamListener注释

作为对Spring Integration支持的补充,Spring Cloud Stream提供了自己的@StreamListener注释,其模仿其他Spring消息注释(@MessageMapping,@JmsListener,@RabbitListener等)并提供便利,例如基于内容的路由等。
  1. @EnableBinding(Sink.class)
  2. public class VoteHandler {
  3.   @Autowired
  4.   VotingService votingService;
  5.   @StreamListener(Sink.INPUT)
  6.   public void handle(Vote vote) {
  7.     votingService.record(vote);
  8.   }
  9. }
复制代码
与其他Spring消息传递方法一样,方法参数可以用@Payload,@Headers和@Header进行注释。
对于返回数据的方法,必须使用@SendTo批注为该方法返回的数据指定输出绑定目标,如以下示例所示:
  1. @EnableBinding(Processor.class)
  2. public class TransformProcessor {
  3.   @Autowired
  4.   VotingService votingService;
  5.   @StreamListener(Processor.INPUT)
  6.   @SendTo(Processor.OUTPUT)
  7.   public VoteResult handle(Vote vote) {
  8.     return votingService.record(vote);
  9.   }
  10. }
复制代码
29.3.3使用@StreamListener进行基于内容的路由

Spring Cloud Stream支持根据条件将消息调度到用@StreamListener注释的多个处理程序方法。
为了有资格支持条件分派,一种方法必须满足以下条件:
    它不能返回值。它必须是单独的消息处理方法(不支持反应性API方法)。
该条件由注释的condition参数中的SpEL表达式指定,并针对每条消息进行评估。所有与条件匹配的处理程序都在同一线程中调用,并且不必假设调用的顺序。
在具有分配条件的@StreamListener的以下示例中,所有带有标头type且具有值bogey的消息都被分配到receiveBogey方法,所有带有标头{11的消息值bacall的/}发送到receiveBacall方法。
  1. @EnableBinding(Sink.class)
  2. @EnableAutoConfiguration
  3. public static class TestPojoWithAnnotatedArguments {
  4.     @StreamListener(target = Sink.INPUT, condition = "headers['type']=='bogey'")
  5.     public void receiveBogey(@Payload BogeyPojo bogeyPojo) {
  6.        // handle the message
  7.     }
  8.     @StreamListener(target = Sink.INPUT, condition = "headers['type']=='bacall'")
  9.     public void receiveBacall(@Payload BacallPojo bacallPojo) {
  10.        // handle the message
  11.     }
  12. }
复制代码
condition上下文中的内容类型协商
了解使用@StreamListener参数condition的基于内容的路由背后的一些机制很重要,尤其是在整个消息类型的上下文中。如果在继续之前熟悉第32章,内容类型协商,这也可能会有所帮助。
请考虑以下情形:
  1. @EnableBinding(Sink.class)
  2. @EnableAutoConfiguration
  3. public static class CatsAndDogs {
  4.     @StreamListener(target = Sink.INPUT, condition = "payload.class.simpleName=='Dog'")
  5.     public void bark(Dog dog) {
  6.        // handle the message
  7.     }
  8.     @StreamListener(target = Sink.INPUT, condition = "payload.class.simpleName=='Cat'")
  9.     public void purr(Cat cat) {
  10.        // handle the message
  11.     }
  12. }
复制代码
前面的代码是完全有效的。它可以毫无问题地进行编译和部署,但是永远不会产生您期望的结果。
这是因为您正在测试的东西在您期望的状态下尚不存在。这是因为消息的有效负载尚未从有线格式(byte[])转换为所需的类型。换句话说,它尚未经过第32章,内容类型协商中描述的类型转换过程。
因此,除非使用SPeL表达式评估原始数据(例如,字节数组中第一个字节的值),否则请使用基于消息标头的表达式(例如condition = "headers['type']=='dog'")。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-f0stq0yx-1591774641444)(https://www.springcloud.cc/images/note.png)]
目前,仅基于通道的绑定程序(不支持响应编程)支持通过@StreamListener条件进行分派。
29.3.4 Spring Cloud功能支持

从Spring Cloud Stream v2.1开始,定义流处理程序源的另一种方法是使用对Spring Cloud函数的内置支持,其中可以将它们表示为java.util.function.[Supplier/Function/Consumer]类型的beans。
若要指定要绑定到绑定公开的外部目标的功能bean,必须提供spring.cloud.stream.function.definition属性。
这是Processor应用程序将消息处理程序公开为java.util.function.Function的示例
  1. @SpringBootApplication
  2. @EnableBinding(Processor.class)
  3. public class MyFunctionBootApp {
  4.         public static void main(String[] args) {
  5.                 SpringApplication.run(MyFunctionBootApp.class, "--spring.cloud.stream.function.definition=toUpperCase");
  6.         }
  7.         @Bean
  8.         public Function<String, String> toUpperCase() {
  9.                 return s -> s.toUpperCase();
  10.         }
  11. }
复制代码
在上面的代码中,我们仅定义了类型为java.util.function.Function的bean(称为*toUpperCase)*并将其标识为bean,用作消息处理程序,其“输入”和“输出”必须绑定到外部目标由处理器绑定公开。
以下是支持源,处理器和接收器的简单功能应用程序的示例。
这是定义为java.util.function.Supplier的Source应用程序的示例
  1. @SpringBootApplication
  2. @EnableBinding(Source.class)
  3. public static class SourceFromSupplier {
  4.         public static void main(String[] args) {
  5.                 SpringApplication.run(SourceFromSupplier.class, "--spring.cloud.stream.function.definition=date");
  6.         }
  7.         @Bean
  8.         public Supplier<Date> date() {
  9.                 return () -> new Date(12345L);
  10.         }
  11. }
复制代码
这是定义为java.util.function.Function的Processor应用程序的示例
  1. @SpringBootApplication
  2. @EnableBinding(Processor.class)
  3. public static class ProcessorFromFunction {
  4.         public static void main(String[] args) {
  5.                 SpringApplication.run(ProcessorFromFunction.class, "--spring.cloud.stream.function.definition=toUpperCase");
  6.         }
  7.         @Bean
  8.         public Function<String, String> toUpperCase() {
  9.                 return s -> s.toUpperCase();
  10.         }
  11. }
复制代码
这是一个定义为java.util.function.Consumer的接收器应用程序的示例
  1. @EnableAutoConfiguration
  2. @EnableBinding(Sink.class)
  3. public static class SinkFromConsumer {
  4.         public static void main(String[] args) {
  5.                 SpringApplication.run(SinkFromConsumer.class, "--spring.cloud.stream.function.definition=sink");
  6.         }
  7.         @Bean
  8.         public Consumer<String> sink() {
  9.                 return System.out::println;
  10.         }
  11. }
复制代码
功能组成

使用此编程模型,您还可以从功能组合中受益,在该功能组合中,您可以从一组简单的函数中动态组成复杂的处理程序。作为示例,我们将以下函数bean添加到上面定义的应用程序中
  1. @Bean
  2. public Function<String, String> wrapInQuotes() {
  3.         return s -> """ + s + """;
  4. }
复制代码
并修改spring.cloud.stream.function.definition属性以反映您打算从’toUpperCase’和’wrapInQuotes’编写新函数的意图。为此,可以使用Spring Cloud函数使用|(管道)符号。因此,完成我们的示例,我们的属性现在将如下所示:
  1. —spring.cloud.stream.function.definition=toUpperCase|wrapInQuotes
复制代码
29.3.5使用轮询的使用者

总览

使用轮询的使用者时,您可以按需轮询PollableMessageSource。考虑以下受调查消费者的示例:
  1. public interface PolledConsumer {
  2.     @Input
  3.     PollableMessageSource destIn();
  4.     @Output
  5.     MessageChannel destOut();
  6. }
复制代码
给定上一个示例中的受调查消费者,您可以按以下方式使用它:
  1. @Bean
  2. public ApplicationRunner poller(PollableMessageSource destIn, MessageChannel destOut) {
  3.     return args -> {
  4.         while (someCondition()) {
  5.             try {
  6.                 if (!destIn.poll(m -> {
  7.                     String newPayload = ((String) m.getPayload()).toUpperCase();
  8.                     destOut.send(new GenericMessage<>(newPayload));
  9.                 })) {
  10.                     Thread.sleep(1000);
  11.                 }
  12.             }
  13.             catch (Exception e) {
  14.                 // handle failure
  15.             }
  16.         }
  17.     };
  18. }
复制代码
PollableMessageSource.poll()方法采用一个MessageHandler参数(通常为lambda表达式,如此处所示)。如果收到并成功处理了消息,它将返回true。
与消息驱动的使用者一样,如果MessageHandler引发异常,消息将发布到错误通道,如“ ???”中所述。”。
通常,poll()方法会在MessageHandler退出时确认该消息。如果该方法异常退出,则该消息将被拒绝(不重新排队),但请参阅“处理错误”一节。您可以通过对确认负责来覆盖该行为,如以下示例所示:
  1. @Bean
  2. public ApplicationRunner poller(PollableMessageSource dest1In, MessageChannel dest2Out) {
  3.     return args -> {
  4.         while (someCondition()) {
  5.             if (!dest1In.poll(m -> {
  6.                 StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).noAutoAck();
  7.                 // e.g. hand off to another thread which can perform the ack
  8.                 // or acknowledge(Status.REQUEUE)
  9.             })) {
  10.                 Thread.sleep(1000);
  11.             }
  12.         }
  13.     };
  14. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-LVsJOP4l-1591774641444)(https://www.springcloud.cc/images/important.png)]重要
您必须在某一时刻ack(或nack)消息,以避免资源泄漏。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-c1HyNV6u-1591774641445)(https://www.springcloud.cc/images/important.png)]重要
某些消息传递系统(例如Apache Kafka)在日志中维护简单的偏移量。如果传递失败,并用StaticMessageHeaderAccessor.getAcknowledgmentCallback(m).acknowledge(Status.REQUEUE);重新排队,则重新传递任何以后成功确认的消息。
还有一个重载的poll方法,其定义如下:
  1. poll(MessageHandler handler, ParameterizedTypeReference<?> type)
复制代码
type是一个转换提示,它允许转换传入的消息有效负载,如以下示例所示:
  1. boolean result = pollableSource.poll(received -> {
  2.                         Map<String, Foo> payload = (Map<String, Foo>) received.getPayload();
  3.             ...
  4.                 }, new ParameterizedTypeReference<Map<String, Foo>>() {});
复制代码
处理错误

默认情况下,为可轮询源配置了一个错误通道。如果回调引发异常,则将ErrorMessage发送到错误通道(<destination>.<group>.errors);此错误通道也桥接到全局Spring Integration errorChannel。
您可以使用@ServiceActivator订阅任何一个错误通道来处理错误。如果没有订阅,则将仅记录错误并确认消息成功。如果错误通道服务激活器引发异常,则该消息将被拒绝(默认情况下),并且不会重新发送。如果服务激活器抛出RequeueCurrentMessageException,则该消息将在代理处重新排队,并在随后的轮询中再次检索。
如果侦听器直接抛出RequeueCurrentMessageException,则如上所述,该消息将重新排队,并且不会发送到错误通道。
29.4错误处理

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
错误会发生,Spring Cloud Stream提供了几种灵活的机制来处理它们。错误处理有两种形式:
    **应用程序:**错误处理是在应用程序(自定义错误处理程序)中完成的。**系统:**将错误处理委托给绑定程序(重新排队,DL和其他)。注意,这些技术取决于绑定程序的实现和底层消息传递中间件的功能。
Spring Cloud Stream使用Spring重试库来促进成功的消息处理。有关更多详细信息,请参见第29.4.3节“重试模板”。但是,当所有方法均失败时,消息处理程序引发的异常将传播回绑定程序。那时,活页夹调用自定义错误处理程序或将错误传达回消息传递系统(重新排队,DLQ等)。
29.4.1应用程序错误处理

有两种类型的应用程序级错误处理。可以在每个绑定订阅中处理错误,或者全局处理程序可以处理所有绑定订阅错误。让我们查看详细信息。
图29.1 具有自定义和全局错误处理程序的Spring Cloud Stream接收器应用程序
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Zjd5kGRV-1591774641446)(https://www.springcloud.cc/images/custom_vs_global_error_channels.png)]
对于每个输入绑定,Spring Cloud Stream创建具有以下语义<destinationName>.errors的专用错误通道。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zeEzbeiU-1591774641446)(https://www.springcloud.cc/images/note.png)]
<destinationName>由绑定的名称(例如input)和组的名称(例如myGroup)组成。
考虑以下:
  1. spring.cloud.stream.bindings.input.group=myGroup
  2. @StreamListener(Sink.INPUT) // destination name 'input.myGroup'
  3. public void handle(Person value) {
  4.         throw new RuntimeException("BOOM!");
  5. }
  6. @ServiceActivator(inputChannel = Processor.INPUT + ".myGroup.errors") //channel name 'input.myGroup.errors'
  7. public void error(Message<?> message) {
  8.         System.out.println("Handling ERROR: " + message);
  9. }
复制代码
在前面的示例中,目标名称为input.myGroup,专用错误通道名称为input.myGroup.errors。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-EYUwI9Xq-1591774641447)(https://www.springcloud.cc/images/note.png)]
@StreamListener批注的使用专门用于定义桥接内部通道和外部目标的绑定。假设目标特定错误通道没有关联的外部目标,则该通道是Spring Integration(SI)的特权。这意味着必须使用SI处理程序注释之一(即@ ServiceActivator,@ Transformer等)定义用于此类目标的处理程序。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-n6FzkvXb-1591774641447)(https://www.springcloud.cc/images/note.png)]
如果未指定group,则使用匿名组(类似于input.anonymous.2K37rb06Q6m2r51-SPIDDQ),这不适用于错误处理方案,因为在创建目标之前,您不知道它将是什么。
另外,如果您绑定到现有目的地,例如:
  1. spring.cloud.stream.bindings.input.destination=myFooDestination
  2. spring.cloud.stream.bindings.input.group=myGroup
复制代码
完整的目标名称为myFooDestination.myGroup,然后专用错误通道名称为myFooDestination.myGroup.errors。
回到例子…
预订名为input的通道的handle(..)方法会引发异常。给定错误通道input.myGroup.errors的订阅者,所有错误消息均由该订阅者处理。
如果您有多个绑定,则可能需要一个错误处理程序。Spring Cloud Stream 通过将每个单独的错误通道桥接到名为errorChannel的通道来自动提供对全局错误通道的支持,从而允许单个订阅者处理所有错误,如以下示例所示:
  1. @StreamListener("errorChannel")
  2. public void error(Message<?> message) {
  3.         System.out.println("Handling ERROR: " + message);
  4. }
复制代码
如果错误处理逻辑相同,则与哪个处理程序产生错误无关,这可能是一个方便的选择。
29.4.2系统错误处理

系统级错误处理意味着将错误传递回消息传递系统,并且鉴于并非每个消息传递系统都相同,因此各个粘合剂的功能可能有所不同。
也就是说,在本节中,我们解释了系统级错误处理背后的一般思想,并以Rabbit活页夹为例。注意:Kafka活页夹提供了类似的支持,尽管某些配置属性确实有所不同。另外,有关更多详细信息和配置选项,请参见各个活页夹的文档。
如果未配置内部错误处理程序,则错误将传播到绑定程序,而绑定程序随后会将这些错误传播回消息传递系统。根据消息传递系统的功能,此类系统可能会丢弃该消息,重新排队该消息以进行重新处理或将失败的消息发送给DLQ。Rabbit和Kafka都支持这些概念。但是,其他联编程序可能没有,因此请参阅您单独的联编程序的文档,以获取有关受支持的系统级错误处理选项的详细信息。
删除失败的消息

默认情况下,如果未提供其他系统级配置,则消息传递系统将丢弃失败的消息。尽管在某些情况下可以接受,但在大多数情况下是不可接受的,我们需要一些恢复机制来避免消息丢失。
DLQ-死信队列

DLQ允许将失败的消息发送到特殊目标:-Dead Letter Queue
配置后,失败的消息将发送到此目标,以进行后续的重新处理或审核与对帐。
例如,继续前面的示例,并使用Rabbit活页夹设置DLQ,您需要设置以下属性:
  1. spring.cloud.stream.rabbit.bindings.input.consumer.auto-bind-dlq=true
复制代码
请记住,在以上属性中,input对应于输入目标绑定的名称。consumer指示它是消费者属性,auto-bind-dlq指示绑定程序为input目标配置DLQ,这将导致名为input.myGroup.dlq的附加Rabbit队列。
配置完成后,所有失败的消息都会通过错误消息路由到此队列,类似于以下内容:
  1. delivery_mode:        1
  2. headers:
  3. x-death:
  4. count:        1
  5. reason:        rejected
  6. queue:        input.hello
  7. time:        1522328151
  8. exchange:
  9. routing-keys:        input.myGroup
  10. Payload {"name”:"Bob"}
复制代码
从上面可以看到,原始消息会保留下来以供进一步操作。
但是,您可能已经注意到的一件事是,有关消息处理的原始问题的信息有限。例如,您看不到与原始错误相对应的堆栈跟踪。要获取有关原始错误的更多相关信息,您必须设置一个附加属性:
  1. spring.cloud.stream.rabbit.bindings.input.consumer.republish-to-dlq=true
复制代码
这样做会强制内部错误处理程序在将错误消息发布到DLQ之前拦截该错误消息并向其添加其他信息。配置完成后,您会看到错误消息包含与原始错误有关的更多信息,如下所示:
  1. delivery_mode:        2
  2. headers:
  3. x-original-exchange:
  4. x-exception-message:        has an error
  5. x-original-routingKey:        input.myGroup
  6. x-exception-stacktrace:        org.springframework.messaging.MessageHandlingException: nested exception is
  7.       org.springframework.messaging.MessagingException: has an error, failedMessage=GenericMessage [payload=byte[15],
  8.       headers={amqp_receivedDeliveryMode=NON_PERSISTENT, amqp_receivedRoutingKey=input.hello, amqp_deliveryTag=1,
  9.       deliveryAttempt=3, amqp_consumerQueue=input.hello, amqp_redelivered=false, id=a15231e6-3f80-677b-5ad7-d4b1e61e486e,
  10.       amqp_consumerTag=amq.ctag-skBFapilvtZhDsn0k3ZmQg, contentType=application/json, timestamp=1522327846136}]
  11.       at org.spring...integ...han...MethodInvokingMessageProcessor.processMessage(MethodInvokingMessageProcessor.java:107)
  12.       at. . . . .
  13. Payload {"name”:"Bob"}
复制代码
这有效地结合了应用程序级和系统级的错误处理,以进一步协助下游故障排除机制。
重新排队失败的消息

如前所述,当前支持的活页夹(Rabbit和Kafka)依靠RetryTemplate来促进成功的消息处理。有关详细信息,请参见第29.4.3节“重试模板”。但是,对于max-attempts属性设置为1的情况,将禁用消息的内部重新处理。此时,您可以通过指示消息传递系统重新排队失败的消息来促进消息的重新处理(重试)。重新排队后,失败的消息将被发送回原始处理程序,从而创建一个重试循环。
如果错误的性质与某些资源的偶发性但短期不可用有关,则此选项可能是可行的。
为此,必须设置以下属性:
  1. spring.cloud.stream.bindings.input.consumer.max-attempts=1
  2. spring.cloud.stream.rabbit.bindings.input.consumer.requeue-rejected=true
复制代码
在前面的示例中,max-attempts设置为1,实际上禁用了内部重试,而requeue-rejected(重新排队拒绝消息的缩写)被设置为true。设置后,失败的消息将重新提交给同一处理程序并连续循环,直到处理程序抛出AmqpRejectAndDontRequeueException为止,从本质上讲,您可以在处理程序本身内构建自己的重试逻辑。
29.4.3重试模板

RetryTemplate是Spring重试库的一部分。尽管涵盖RetryTemplate的所有功能超出了本文档的范围,但我们将提及以下与RetryTemplate特别相关的使用者属性:
    maxAttempts
    处理消息的尝试次数。默认值:3。
    backOffInitialInterval
    重试时的退避初始间隔。默认值1000毫秒。
    backOffMaxInterval
    最大退避间隔。默认值10000毫秒。
    backOffMultiplier
    退避乘数。默认为2.0。
    defaultRetryable
    retryableExceptions中未列出的由侦听器引发的异常是否可以重试。默认值:true。
    retryableExceptions
    键中Throwable类名称的映射,值中布尔值的映射。指定将要重试的那些异常(和子类)。另请参见defaultRetriable。示例:spring.cloud.stream.bindings.input.consumer.retryable-exceptions.java.lang.IllegalStateException=false。默认值:空。
尽管上述设置足以满足大多数自定义要求,但它们可能无法满足某些复杂的要求,此时,您可能希望提供自己的RetryTemplate实例。为此,在应用程序配置中将其配置为bean。应用程序提供的实例将覆盖框架提供的实例。另外,为避免冲突,必须将绑定程序要使用的RetryTemplate实例限定为@StreamRetryTemplate。例如,
  1. @StreamRetryTemplate
  2. public RetryTemplate myRetryTemplate() {
  3.     return new RetryTemplate();
  4. }
复制代码
从上面的示例中可以看到,由于@StreamRetryTemplate是合格的@Bean,因此无需使用@Bean对其进行注释。
29.5反应式编程支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream还支持使用反应式API,将传入和传出的数据作为连续的数据流进行处理。可通过spring-cloud-stream-reactive获得对反应式API的支持,需要将其显式添加到您的项目中。
具有响应式API的编程模型是声明性的。您可以使用描述从入站数据流到出站数据流的功能转换的运算符,而不是指定每个消息的处理方式。
目前Spring Cloud Stream仅支持Reactor API。将来,我们打算支持基于反应式流的更通用的模型。
反应式编程模型还使用@StreamListener注释来设置反应式处理程序。区别在于:
    @StreamListener批注不能指定输入或输出,因为它们作为参数提供并从方法返回值。该方法的参数必须用@Input和@Output注释,分别指示传入和传出数据流连接到哪个输入或输出。该方法的返回值(如果有)用@Output注释,指示应该将数据发送到的输入。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-CpyHPDSi-1591774641448)(https://www.springcloud.cc/images/note.png)]
响应式编程支持需要Java 1.8。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-jO6hGtcm-1591774641448)(https://www.springcloud.cc/images/note.png)]
从Spring Cloud Stream 1.1.1起(从发行版Brooklyn.SR2开始),反应式编程支持要求使用Reactor 3.0.4.RELEASE及更高版本。不支持更早的Reactor版本(包括3.0.1.RELEASE,3.0.2.RELEASE和3.0.3.RELEASE)。spring-cloud-stream-reactive可传递地检索正确的版本,但是项目结构可以将io.projectreactor:reactor-core的版本管理为早期版本,尤其是在使用Maven时。使用Spring Initializr和Spring Boot 1.x生成的项目就是这种情况,该项目将Reactor版本覆盖为2.0.8.RELEASE。在这种情况下,必须确保发布了正确版本的工件。您可以通过在项目中添加对版本为3.0.4.RELEASE或更高版本的io.projectreactor:reactor-core的直接依赖来实现。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kQ8oshJI-1591774641448)(https://www.springcloud.cc/images/note.png)]
当前,术语“ 反应式 ”的使用是指正在使用的反应式API,而不是指执行模型是反应式的(也就是说,绑定的端点仍然使用“推”式而非“拉式”模型)。尽管通过使用Reactor提供了一些反压支持,但在将来的发行版中,我们确实打算通过对连接的中间件使用本机反应性客户端来完全支持反应性管道。
29.5.1基于Reactor的处理程序

基于Reactor的处理程序可以具有以下参数类型:
    对于带有@Input注释的参数,它支持Reactor Flux类型。入站Flux的参数化遵循与处理单个消息时相同的规则:可以是整个Message,可以是Message有效负载的POJO或由于以下原因而产生的POJO:基于Message内容类型标头的转换。提供了多个输入。对于带有Output注释的参数,它支持FluxSender类型,该类型将方法生成的Flux与输出连接起来。一般而言,仅在该方法可以具有多个输出时才建议将输出指定为参数。
基于Reactor的处理程序支持Flux的返回类型。在这种情况下,必须用@Output进行注释。当单个输出Flux可用时,建议使用该方法的返回值。
以下示例显示了基于Reactor的Processor:
  1. @EnableBinding(Processor.class)
  2. @EnableAutoConfiguration
  3. public static class UppercaseTransformer {
  4.   @StreamListener
  5.   @Output(Processor.OUTPUT)
  6.   public Flux<String> receive(@Input(Processor.INPUT) Flux<String> input) {
  7.     return input.map(s -> s.toUpperCase());
  8.   }
  9. }
复制代码
使用输出参数的同一处理器看起来像以下示例:
  1. @EnableBinding(Processor.class)
  2. @EnableAutoConfiguration
  3. public static class UppercaseTransformer {
  4.   @StreamListener
  5.   public void receive(@Input(Processor.INPUT) Flux<String> input,
  6.      @Output(Processor.OUTPUT) FluxSender output) {
  7.      output.send(input.map(s -> s.toUpperCase()));
  8.   }
  9. }
复制代码
29.5.2反应源

Spring Cloud Stream反应性支持还提供了通过@StreamEmitter注释创建反应性源的功能。通过使用@StreamEmitter批注,可以将常规源转换为被动源。@StreamEmitter是方法级别的注释,用于将方法标记为用@EnableBinding声明的输出的发射器。您不能将@Input批注与@StreamEmitter一起使用,因为标有该批注的方法不会监听任何输入。而是用标记为@StreamEmitter的方法生成输出。遵循@StreamListener中使用的相同编程模型,@StreamEmitter还允许灵活地使用@Output批注,具体取决于方法是否具有任何参数,返回类型和其他考虑因素。
本节的其余部分包含使用各种样式的@StreamEmitter批注的示例。
以下示例每毫秒发出一次Hello, World消息,并发布到Reactor Flux中:
  1. @EnableBinding(Source.class)
  2. @EnableAutoConfiguration
  3. public static class HelloWorldEmitter {
  4.   @StreamEmitter
  5.   @Output(Source.OUTPUT)
  6.   public Flux<String> emit() {
  7.     return Flux.intervalMillis(1)
  8.             .map(l -> "Hello World");
  9.   }
  10. }
复制代码
在前面的示例中,Flux中的结果消息被发送到Source的输出通道。
下一个示例是@StreamEmmitter的另一种形式,它发送Reactor Flux。以下方法代替返回Flux,而是使用FluxSender从源代码中以编程方式发送Flux:
  1. @EnableBinding(Source.class)
  2. @EnableAutoConfiguration
  3. public static class HelloWorldEmitter {
  4.   @StreamEmitter
  5.   @Output(Source.OUTPUT)
  6.   public void emit(FluxSender output) {
  7.     output.send(Flux.intervalMillis(1)
  8.             .map(l -> "Hello World"));
  9.   }
  10. }
复制代码
下一个示例在功能和样式上与上述代码段完全相同。但是,它没有在方法上使用显式的@Output注释,而是在方法参数上使用了注释。
  1. @EnableBinding(Source.class)
  2. @EnableAutoConfiguration
  3. public static class HelloWorldEmitter {
  4.   @StreamEmitter
  5.   public void emit(@Output(Source.OUTPUT) FluxSender output) {
  6.     output.send(Flux.intervalMillis(1)
  7.             .map(l -> "Hello World"));
  8.   }
  9. }
复制代码
本节的最后一个示例是使用Reactive Streams Publisher API并利用Spring Integration Java DSL中对它的支持来编写反应源的另一种方式。以下示例中的Publisher仍在幕后使用Reactor Flux,但是,从应用程序角度看,这对用户是透明的,并且对于Spring Integration仅需要响应流和Java DSL:
  1. @EnableBinding(Source.class)
  2. @EnableAutoConfiguration
  3. public static class HelloWorldEmitter {
  4.   @StreamEmitter
  5.   @Output(Source.OUTPUT)
  6.   @Bean
  7.   public Publisher<Message<String>> emit() {
  8.     return IntegrationFlows.from(() ->
  9.                 new GenericMessage<>("Hello World"),
  10.         e -> e.poller(p -> p.fixedDelay(1)))
  11.         .toReactivePublisher();
  12.   }
  13. }
复制代码
30. Binders

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream提供了Binder抽象,用于连接到外部中间件上的物理目标。本节提供有关Binder SPI背后的主要概念,其主要组件以及特定于实现的详细信息。
30.1生产者和消费者

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
下图显示了生产者和消费者的一般关系:
图30.1。生产者和消费者
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ULSMalHj-1591774641449)(https://www.springcloud.cc/images/producers-consumers.png)]
生产者是将消息发送到通道的任何组件。可以将该通道绑定到具有该代理的Binder实现的外部消息代理。调用bindProducer()方法时,第一个参数是代理内目标的名称,第二个参数是生产者向其发送消息的本地通道实例,第三个参数包含属性(例如分区键表达式) ),以在为该通道创建的适配器中使用。
使用者是从通道接收消息的任何组件。与生产者一样,消费者的渠道可以绑定到外部消息代理。调用bindConsumer()方法时,第一个参数是目标名称,第二个参数提供逻辑消费者组的名称。由给定目标的使用者绑定表示的每个组都接收生产者发送到该目标的每个消息的副本(也就是说,它遵循常规的发布-订阅语义)。如果有多个使用相同组名绑定的使用者实例,那么消息将在这些使用者实例之间进行负载平衡,以便由生产者发送的每条消息仅在每个组内的单个使用者实例中被使用(也就是说,它遵循常规排队语义)。
30.2 Binder SPI

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Binder SPI由许多接口,现成的实用程序类和发现策略组成,这些策略提供了用于连接到外部中间件的可插拔机制。
SPI的关键是Binder接口,这是将输入和输出连接到外部中间件的策略。以下清单显示了Binder接口的定义:
  1. public interface Binder<T, C extends ConsumerProperties, P extends ProducerProperties> {
  2.     Binding<T> bindConsumer(String name, String group, T inboundBindTarget, C consumerProperties);
  3.     Binding<T> bindProducer(String name, T outboundBindTarget, P producerProperties);
  4. }
复制代码
该接口已参数化,提供了许多扩展点:
    输入和输出绑定目标。从1.0版开始,仅支持MessageChannel,但将来打算将其用作扩展点。扩展的使用者和生产者属性,允许特定的Binder实现添加可以以类型安全的方式支持的补充属性。
典型的活页夹实现包括以下内容:
    实现Binder接口的类;
    一个Spring @Configuration类,它创建类型为Binder的bean和中间件连接基础结构。
  • 在类路径上找到一个META-INF/spring.binders文件,其中包含一个或多个绑定程序定义,如以下示例所示:
    1. kafka:\
    2. org.springframework.cloud.stream.binder.kafka.config.KafkaBinderConfiguration
    复制代码
30.3 Binder检测

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream依赖于Binder SPI的实现来执行将通道连接到消息代理的任务。每个Binder实现通常都连接到一种消息传递系统。
30.3.1类路径检测

默认情况下,Spring Cloud Stream依靠Spring Boot的自动配置来配置绑定过程。如果在类路径上找到单个Binder实现,则Spring Cloud Stream将自动使用它。例如,旨在仅绑定到RabbitMQ的Spring Cloud Stream项目可以添加以下依赖项:
  1. <dependency>
  2.   <groupId>org.springframework.cloud</groupId>
  3.   <artifactId>spring-cloud-stream-binder-rabbit</artifactId>
  4. </dependency>
复制代码
有关其他绑定程序依赖项的特定Maven坐标,请参阅该绑定程序实现的文档。
类路径上的30.4倍数Binders

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
当类路径上存在多个绑定程序时,应用程序必须指示将哪个绑定程序用于每个通道绑定。每个活页夹配置都包含一个META-INF/spring.binders文件,它是一个简单的属性文件,如以下示例所示:
  1. rabbit:\
  2. org.springframework.cloud.stream.binder.rabbit.config.RabbitServiceAutoConfiguration
复制代码
其他提供的活页夹实现(例如Kafka)也存在类似的文件,并且期望自定义活页夹实现也将提供它们。关键字表示绑定程序实现的标识名,而该值是逗号分隔的配置类列表,每个配置类都包含一个且仅一个bean类型为org.springframework.cloud.stream.binder.Binder的定义。
可以使用spring.cloud.stream.defaultBinder属性(例如,spring.cloud.stream.defaultBinder=rabbit)在全局上执行Binder选择,也可以通过在每个通道绑定上配置活页夹来分别进行Binder选择。例如,从Kafka读取并写入RabbitMQ的处理器应用程序(具有分别名为input和output的通道用于读取和写入)可以指定以下配置:
  1. spring.cloud.stream.bindings.input.binder=kafka
  2. spring.cloud.stream.bindings.output.binder=rabbit
复制代码
30.5连接到多个系统

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
默认情况下,活页夹共享应用程序的Spring Boot自动配置,以便创建在类路径上找到的每个活页夹的一个实例。如果您的应用程序应连接到多个相同类型的代理,则可以指定多个绑定程序配置,每个配置具有不同的环境设置。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-omBahkzK-1591774641450)(https://www.springcloud.cc/images/note.png)]
启用显式绑定程序配置将完全禁用默认的绑定程序配置过程。如果这样做,则配置中必须包括所有正在使用的活页夹。打算透明使用Spring Cloud Stream的框架可以创建可以按名称引用的活页夹配置,但它们不会影响默认的活页夹配置。为此,活页夹配置可以将其defaultCandidate标志设置为false(例如spring.cloud.stream.binders.<configurationName>.defaultCandidate=false)。这表示独立于默认绑定程序配置过程而存在的配置。
以下示例显示了连接到两个RabbitMQ代理实例的处理器应用程序的典型配置:
  1. spring:
  2.   cloud:
  3.     stream:
  4.       bindings:
  5.         input:
  6.           destination: thing1
  7.           binder: rabbit1
  8.         output:
  9.           destination: thing2
  10.           binder: rabbit2
  11.       binders:
  12.         rabbit1:
  13.           type: rabbit
  14.           environment:
  15.             spring:
  16.               rabbitmq:
  17.                 host: <host1>
  18.         rabbit2:
  19.           type: rabbit
  20.           environment:
  21.             spring:
  22.               rabbitmq:
  23.                 host: <host2>
复制代码
30.6绑定可视化和控制

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
从2.0版开始,Spring Cloud Stream支持通过Actuator端点进行绑定的可视化和控制。
从2.0版执行器开始,并且web是可选的,您必须首先添加web依赖项之一,然后手动添加执行器依赖项。以下示例说明如何为Web框架添加依赖项:
  1. <dependency>
  2.      <groupId>org.springframework.boot</groupId>
  3.      <artifactId>spring-boot-starter-web</artifactId>
  4. </dependency>
复制代码
以下示例显示如何为WebFlux框架添加依赖项:
  1. <dependency>
  2.        <groupId>org.springframework.boot</groupId>
  3.        <artifactId>spring-boot-starter-webflux</artifactId>
  4. </dependency>
复制代码
您可以添加执行器依赖项,如下所示:
  1. <dependency>
  2.     <groupId>org.springframework.boot</groupId>
  3.     <artifactId>spring-boot-starter-actuator</artifactId>
  4. </dependency>
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-L7THiVMu-1591774641450)(https://www.springcloud.cc/images/note.png)]
要在Cloud Foundry中运行Spring Cloud Stream 2.0应用程序,必须将spring-boot-starter-web和spring-boot-starter-actuator添加到类路径中。否则,由于运行状况检查失败,该应用程序将无法启动。
您还必须通过设置以下属性来启用bindings执行器端点:--management.endpoints.web.exposure.include=bindings。
一旦满足这些先决条件。应用程序启动时,您应该在日志中看到以下内容:
  1. : Mapped "{[/actuator/bindings/{name}],methods=[POST]. . .
  2. : Mapped "{[/actuator/bindings],methods=[GET]. . .
  3. : Mapped "{[/actuator/bindings/{name}],methods=[GET]. . .
复制代码
要显示当前绑定,请访问以下URL:http://<host>:<port>/actuator/bindings
或者,要查看单个绑定,请访问类似于以下内容的URL之一:http://<host>:<port>/actuator/bindings/myBindingName
您还可以通过发布到相同的URL来停止,开始,暂停和恢复单个绑定,同时提供一个state作为JSON的参数,如以下示例所示:
curl -d’{“ state”:“ STOPPED”}’-H“内容类型:应用程序/ json” -X POST http:// <主机>: / actuator / bindings / myBindingName curl -d’{ “ state”:“ STARTED”}’-H“内容类型:application / json” -X POST http:// : / actuator / bindings / myBindingName curl -d’{“ state”:“ PAUSED“}’-H”内容类型:application / json“ -X POST http:// : / actuator / bindings / myBindingName curl -d’{” state“:” RESUMED“}’- H“内容类型:应用程序/ json” -X POST http:// <主机>:<端口> / actuator / bindings / myBindingName
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bRM9D0wu-1591774641451)(https://www.springcloud.cc/images/note.png)]
PAUSED和RESUMED仅在相应的活页夹及其基础技术支持时起作用。否则,您会在日志中看到警告消息。当前,只有Kafka活页夹支持PAUSED和RESUMED状态。
30.7 Binder配置Properties

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
定制活页夹配置时,以下属性可用。这些属性通过org.springframework.cloud.stream.config.BinderProperties公开
它们必须以spring.cloud.stream.binders.<configurationName>为前缀。
    类型
    资料夹类型。它通常引用在类路径上找到的绑定器之一-特别是META-INF/spring.binders文件中的键。默认情况下,它具有与配置名称相同的值。
    继承环境
    配置是否继承应用程序本身的环境。默认值:true。
    环境
    根可用于定制活页夹环境的一组属性。设置此属性后,在其中创建活页夹的上下文不是应用程序上下文的子级。该设置允许在粘合剂组分和应用组分之间完全分离。默认值:empty。
    defaultCandidate
    活页夹配置是被视为默认活页夹的候选者还是仅在明确引用时才可以使用。此设置允许添加活页夹配置,而不会干扰默认处理。默认值:true。
31.配置选项

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream支持常规配置选项以及绑定和活页夹的配置。一些活页夹使附加的绑定属性支持特定于中间件的功能。
可以通过Spring Boot支持的任何机制向Spring Cloud Stream应用程序提供配置选项。这包括应用程序参数,环境变量以及YAML或.properties文件。
31.1绑定服务Properties

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
这些属性通过org.springframework.cloud.stream.config.BindingServiceProperties公开
    spring.cloud.stream.instanceCount
    应用程序已部署实例的数量。必须在生产者端进行分区设置。使用RabbitMQ时必须在用户端设置,如果使用autoRebalanceEnabled=false,则必须在Kafka时设置。默认值:1。
    spring.cloud.stream.instanceIndex
    应用程序的实例索引:从0到instanceCount - 1的数字。用于通过RabbitMQ和Kafka(如果是autoRebalanceEnabled=false)进行分区。在Cloud Foundry中自动设置以匹配应用程序的实例索引。
    spring.cloud.stream.dynamic目的地
    可以动态绑定的目的地列表(例如,在动态路由方案中)。如果设置,则只能绑定列出的目的地。默认值:空(将任何目的地绑定)。
    spring.cloud.stream.defaultBinder
    如果配置了多个联编程序,则使用的默认联编程序。请参见Classpath上的多个Binders。默认值:空。
    spring.cloud.stream.overrideCloudConnectors
    仅当cloud配置文件处于活动状态并且应用程序提供了Spring Cloud Connectors时,此属性才适用。如果属性为false(默认值),则绑定器检测到合适的绑定服务(例如,RabbitMQ绑定器在Cloud Foundry中绑定的RabbitMQ服务)并将其用于创建连接(通常通过Spring Cloud Connectors)。设置为true时,此属性指示绑定程序完全忽略绑定的服务,并依赖Spring Boot属性(例如,依赖于环境中为RabbitMQ绑定程序提供的spring.rabbitmq.*属性) 。连接到多个系统时,此属性的典型用法是嵌套在自定义环境中。默认值:false。
    spring.cloud.stream.bindingRetryInterval
    例如,活页夹不支持后期绑定和代理(例如Apache Kafka)关闭时,重试绑定创建之间的间隔(以秒为单位)。将该值设置为零可将此类情况视为致命情况,从而阻止应用程序启动。默认值:30
31.2绑定Properties

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
绑定属性是使用spring.cloud.stream.bindings.<channelName>.<property>=<value>格式提供的。<channelName>代表正在配置的通道的名称(例如,对于Source,为output)。
为避免重复,Spring Cloud Stream支持所有通道的设置值,格式为spring.cloud.stream.default.<property>=<value>。
在避免重复使用扩展绑定属性时,应使用此格式-spring.cloud.stream.<binder-type>.default.<producer|consumer>.<property>=<value>。
在下面的内容中,我们指出了省略了spring.cloud.stream.bindings.<channelName>.前缀的位置,仅着眼于属性名称,但要了解前缀是在运行时包含的。
31.2.1通用绑定Properties

这些属性通过org.springframework.cloud.stream.config.BindingProperties公开
以下绑定属性可用于输入和输出绑定,并且必须以spring.cloud.stream.bindings.<channelName>.为前缀(例如,spring.cloud.stream.bindings.input.destination=ticktock)。
可以使用前缀spring.cloud.stream.default设置默认值(例如“ spring.cloud.stream.default.contentType = application / json”)。
    目的地
    绑定的中间件上的通道的目标目的地(例如,RabbitMQ交换或Kafka主题)。如果将通道绑定为使用者,则可以将其绑定到多个目标,并且目标名称可以指定为逗号分隔的String值。如果未设置,则使用通道名称。此属性的默认值不能被覆盖。

    渠道的消费群体。仅适用于入站绑定。请参阅消费者组。默认值:null(指示匿名使用者)。
    内容类型
    频道的内容类型。请参见“ 第32章,内容类型协商 ”。默认值:application/json。
    黏合剂
    此绑定使用的粘合剂。有关详细信息,请参见“ 第30.4节“类路径上的多个Binders ”)。默认值:null(如果存在默认活页夹,则使用默认活页夹)。
31.2.2消费者Properties

这些属性通过org.springframework.cloud.stream.binder.ConsumerProperties公开
以下绑定属性仅可用于输入绑定,并且必须以spring.cloud.stream.bindings.<channelName>.consumer.为前缀(例如,spring.cloud.stream.bindings.input.consumer.concurrency=3)。
可以使用spring.cloud.stream.default.consumer前缀(例如,spring.cloud.stream.default.consumer.headerMode=none)设置默认值。
    并发
    入站使用者的并发。默认值:1。
    分区的
    消费者是否从分区生产者那里接收数据。默认值:false。
    headerMode
    设置为none时,禁用输入的标头解析。仅对本身不支持消息头并且需要消息头嵌入的消息中间件有效。当不支持本机头时,使用来自非Spring Cloud Stream应用程序的数据时,此选项很有用。设置为headers时,它使用中间件的本机头机制。设置为embeddedHeaders时,它将标头嵌入到消息有效负载中。默认值:取决于活页夹的实现。
    maxAttempts
    如果处理失败,则尝试处理消息的次数(包括第一次)。设置为1以禁用重试。默认值:3。
    backOffInitialInterval
    重试时的退避初始间隔。默认值:1000。
    backOffMaxInterval
    最大退避间隔。默认值:10000。
    backOffMultiplier
    退避乘数。默认值:2.0。
    defaultRetryable
    retryableExceptions中未列出的由侦听器引发的异常是否可以重试。默认值:true。
    instanceIndex
    设置为大于零的值时,它允许自定义此使用者的实例索引(如果与spring.cloud.stream.instanceIndex不同)。设置为负值时,默认为spring.cloud.stream.instanceIndex。有关更多信息,请参见“ 第34.2节“实例索引和实例计数” ”。默认值:-1。
    instanceCount
    设置为大于零的值时,它允许自定义此使用者的实例计数(如果与spring.cloud.stream.instanceCount不同)。设置为负值时,默认为spring.cloud.stream.instanceCount。有关更多信息,请参见“ 第34.2节“实例索引和实例计数” ”。默认值:-1。
    retryableExceptions
    键中Throwable类名称的映射,值中布尔值的映射。指定将要重试的那些异常(和子类)。另请参见defaultRetriable。示例:spring.cloud.stream.bindings.input.consumer.retryable-exceptions.java.lang.IllegalStateException=false。默认值:空。
    useNativeDecoding
    设置为true时,入站消息将直接由客户端库反序列化,该库必须进行相应配置(例如,设置适当的Kafka生产者值反序列化器)。使用此配置时,入站消息解组不是基于绑定的contentType。使用本机解码时,生产者负责使用适当的编码器(例如,Kafka生产者值序列化程序)对出站消息进行序列化。同样,当使用本机编码和解码时,headerMode=embeddedHeaders属性将被忽略,并且标头不会嵌入消息中。请参见生产者属性useNativeEncoding。默认值:false。
31.2.3生产者Properties

这些属性通过org.springframework.cloud.stream.binder.ProducerProperties公开
以下绑定属性仅可用于输出绑定,并且必须以spring.cloud.stream.bindings.<channelName>.producer.为前缀(例如,spring.cloud.stream.bindings.input.producer.partitionKeyExpression=payload.id)。
可以使用前缀spring.cloud.stream.default.producer(例如,spring.cloud.stream.default.producer.partitionKeyExpression=payload.id)设置默认值。
    partitionKeyExpression
    一个SpEL表达式,用于确定如何对出站数据进行分区。如果已设置或设置了partitionKeyExtractorClass,则会对该通道上的出站数据进行分区。partitionCount必须设置为大于1的值才能生效。与partitionKeyExtractorClass互斥。请参见“ 第28.6节“分区支持” ”。默认值:null。
    partitionKeyExtractorClass
    PartitionKeyExtractorStrategy实现。如果已设置,或者已设置partitionKeyExpression,则会对该通道上的出站数据进行分区。partitionCount必须设置为大于1的值才能生效。与partitionKeyExpression互斥。请参见“ 第28.6节“分区支持” ”。默认值:null。
    partitionSelectorClass
    PartitionSelectorStrategy实现。与partitionSelectorExpression互斥。如果两者均未设置,则将该分区选择为hashCode(key) % partitionCount,其中key通过partitionKeyExpression或partitionKeyExtractorClass计算。默认值:null。
    partitionSelectorExpression
    用于自定义分区选择的SpEL表达式。与partitionSelectorClass互斥。如果两者均未设置,则将分区选择为hashCode(key) % partitionCount,其中key通过partitionKeyExpression或partitionKeyExtractorClass计算。默认值:null。
    partitionCount
    数据的目标分区数(如果启用了分区)。如果生产者已分区,则必须将其设置为大于1的值。在Kafka上,它被解释为提示。取其较大者,并使用目标主题的分区数。默认值:1。
    requiredGroups
    生产者必须确保将消息传递到的组的逗号分隔列表,即使它们是在创建消息之后开始的(例如,通过在RabbitMQ中预先创建持久队列)。
    headerMode
    设置为none时,它将禁用在输出中嵌入标头。它仅对本身不支持消息头并且需要消息头嵌入的消息中间件有效。当不支持本机头时,为非Spring Cloud Stream应用程序生成数据时,此选项很有用。设置为headers时,它使用中间件的本机头机制。设置为embeddedHeaders时,它将标头嵌入到消息有效负载中。默认值:取决于活页夹的实现。
    useNativeEncoding
    设置为true时,出站消息将直接由客户端库进行序列化,该库必须进行相应配置(例如,设置适当的Kafka生产者值序列化程序)。使用此配置时,出站消息编组不是基于绑定的contentType。使用本机编码时,使用方负责使用适当的解码器(例如,Kafka使用方值反序列化器)对入站消息进行反序列化。此外,当使用本机编码和解码时,headerMode=embeddedHeaders属性将被忽略,并且标头不会嵌入消息中。请参阅消费者属性useNativeDecoding。默认值:false。
    errorChannelEnabled
    设置为true时,如果活页夹支持异步发送结果,则发送失败将发送到目标的错误通道。参见“ ??? ”以获取更多信息。默认值:false。
31.3使用动态绑定的目的地

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
除了使用@EnableBinding定义的通道外,Spring Cloud Stream还允许应用程序将消息发送到动态绑定的目的地。例如,当需要在运行时确定目标目的地时,这很有用。应用程序可以通过使用@EnableBinding注释自动注册的BinderAwareChannelResolver bean来实现。
“ spring.cloud.stream.dynamicDestinations”属性可用于将动态目标名称限制为已知集合(白名单)。如果未设置此属性,则可以动态绑定任何目标。
BinderAwareChannelResolver可以直接使用,如以下使用路径变量来确定目标通道的REST控制器示例所示:
  1. @EnableBinding
  2. @Controller
  3. public class SourceWithDynamicDestination {
  4.     @Autowired
  5.     private BinderAwareChannelResolver resolver;
  6.     @RequestMapping(path = "/{target}", method = POST, consumes = "*/*")
  7.     @ResponseStatus(HttpStatus.ACCEPTED)
  8.     public void handleRequest(@RequestBody String body, @PathVariable("target") target,
  9.            @RequestHeader(HttpHeaders.CONTENT_TYPE) Object contentType) {
  10.         sendMessage(body, target, contentType);
  11.     }
  12.     private void sendMessage(String body, String target, Object contentType) {
  13.         resolver.resolveDestination(target).send(MessageBuilder.createMessage(body,
  14.                 new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
  15.     }
  16. }
复制代码
现在考虑当我们在默认端口(8080)上启动应用程序并使用CURL发出以下请求时会发生什么:
  1. curl -H "Content-Type: application/json" -X POST -d "customer-1" http://localhost:8080/customers
  2. curl -H "Content-Type: application/json" -X POST -d "order-1" http://localhost:8080/orders
复制代码
在经纪人中创建目的地“客户”和“订单”(交换为Rabbit或在主题为“ Kafka”中),名称为“客户”和“订单”,数据为发布到适当的目的地。
BinderAwareChannelResolver是通用的Spring Integration DestinationResolver,并且可以注入到其他组件中,例如,在路由器中使用基于传入的target字段的SpEL表达式的路由器JSON消息。以下示例包含一个读取SpEL表达式的路由器:
  1. @EnableBinding
  2. @Controller
  3. public class SourceWithDynamicDestination {
  4.     @Autowired
  5.     private BinderAwareChannelResolver resolver;
  6.     @RequestMapping(path = "/", method = POST, consumes = "application/json")
  7.     @ResponseStatus(HttpStatus.ACCEPTED)
  8.     public void handleRequest(@RequestBody String body, @RequestHeader(HttpHeaders.CONTENT_TYPE) Object contentType) {
  9.         sendMessage(body, contentType);
  10.     }
  11.     private void sendMessage(Object body, Object contentType) {
  12.         routerChannel().send(MessageBuilder.createMessage(body,
  13.                 new MessageHeaders(Collections.singletonMap(MessageHeaders.CONTENT_TYPE, contentType))));
  14.     }
  15.     @Bean(name = "routerChannel")
  16.     public MessageChannel routerChannel() {
  17.         return new DirectChannel();
  18.     }
  19.     @Bean
  20.     @ServiceActivator(inputChannel = "routerChannel")
  21.     public ExpressionEvaluatingRouter router() {
  22.         ExpressionEvaluatingRouter router =
  23.             new ExpressionEvaluatingRouter(new SpelExpressionParser().parseExpression("payload.target"));
  24.         router.setDefaultOutputChannelName("default-output");
  25.         router.setChannelResolver(resolver);
  26.         return router;
  27.     }
  28. }
复制代码
该路由器接收器应用程序使用此技术的按需创建的目的地。
如果预先知道通道名称,则可以像其他任何目的地一样配置生产者属性。或者,如果您注册NewBindingCallback<> bean,则会在创建绑定之前调用它。回调采用绑定程序使用的扩展生产者属性的通用类型。它有一种方法:
  1. void configure(String channelName, MessageChannel channel, ProducerProperties producerProperties,
  2.         T extendedProducerProperties);
复制代码
下面的示例显示如何使用RabbitMQ活页夹:
  1. @Bean
  2. public NewBindingCallback<RabbitProducerProperties> dynamicConfigurer() {
  3.     return (name, channel, props, extended) -> {
  4.         props.setRequiredGroups("bindThisQueue");
  5.         extended.setQueueNameGroupOnly(true);
  6.         extended.setAutoBindDlq(true);
  7.         extended.setDeadLetterQueueName("myDLQ");
  8.     };
  9. }
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FMOjk5oC-1591774641452)(https://www.springcloud.cc/images/note.png)]
如果需要支持具有多个活页夹类型的动态目标,请对通用类型使用Object,并根据需要强制转换extended参数。
32.内容类型协商

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
数据转换是任何消息驱动的微服务体系结构的核心功能之一。假设在Spring Cloud Stream中,此类数据表示为Spring Message,则在到达消息之前,可能必须将消息转换为所需的形状或大小。这是必需的,原因有两个:
    转换传入消息的内容以匹配应用程序提供的处理程序的签名。将外发邮件的内容转换为有线格式。
有线格式通常为byte[](对于Kafka和Rabbit活页夹而言是正确的),但是它由活页夹实现方式控制。
在Spring Cloud Stream中,消息转换是通过org.springframework.messaging.converter.MessageConverter完成的。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P4ivFNip-1591774641452)(https://www.springcloud.cc/images/note.png)]
作为后续细节的补充,您可能还需要阅读以下博客文章。
32.1力学

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
为了更好地理解内容类型协商的机制和必要性,我们以下面的消息处理程序为例,看一个非常简单的用例:
  1. @StreamListener(Processor.INPUT)
  2. @SendTo(Processor.OUTPUT)
  3. public String handle(Person person) {..}
复制代码
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-2cHD6AuK-1591774641453)(https://www.springcloud.cc/images/note.png)]
为简单起见,我们假设这是应用程序中唯一的处理程序(我们假设没有内部管道)。
前面示例中所示的处理程序期望将Person对象作为参数,并产生String类型作为输出。为了使框架成功将传入的Message作为参数传递给此处理程序,它必须以某种方式将Message类型的有效负载从有线格式转换为Person类型。换句话说,框架必须找到并应用适当的MessageConverter。为此,该框架需要用户提供一些指导。处理程序方法本身的签名(Person类型)已经提供了这些指令之一。因此,从理论上讲,这应该是(并且在某些情况下是足够的)。但是,对于大多数用例而言,为了选择适当的MessageConverter,框架需要额外的信息。缺少的部分是contentType。
Spring Cloud Stream提供了三种机制来定义contentType(按优先顺序):
    标题:contentType可以通过消息本身进行通信。通过提供contentType标头,您可以声明用于查找和应用适当的MessageConverter的内容类型。
    绑定:可以通过设置spring.cloud.stream.bindings.input.content-type属性为每个目标绑定设置contentType。
    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-leIOemLe-1591774641453)(https://www.springcloud.cc/images/note.png)]
    属性名称中的input段与目的地的实际名称相对应(在本例中为“输入”)。通过这种方法,您可以在每个绑定的基础上声明用于查找和应用适当的MessageConverter的内容类型。
    默认值:如果Message标头或绑定中不存在contentType,则使用默认的application/json内容类型来查找和应用适当的MessageConverter。
如前所述,前面的列表还演示了平局时的优先顺序。例如,标头提供的内容类型优先于任何其他内容类型。对于按绑定设置的内容类型也是如此,这实际上使您可以覆盖默认内容类型。但是,它也提供了明智的默认值(由社区反馈确定)。
将application/json设置为默认值的另一个原因是由分布式微服务体系结构驱动的互操作性要求,在该体系结构中,生产者和使用者不仅可以在不同的JVM中运行,而且还可以在不同的非JVM平台上运行。
当非无效处理程序方法返回时,如果返回值已经是Message,则该Message成为有效负载。但是,当返回值不是Message时,将使用返回值作为有效负载构造新的Message,同时从输入Message继承标头减去由SpringIntegrationProperties.messageHandlerNotPropagatedHeaders定义或过滤的标头。默认情况下,仅设置一个标头:contentType。这意味着新的Message没有设置contentType头,从而确保contentType可以演进。您始终可以选择不从处理程序方法返回Message的位置,在该方法中可以注入所需的任何标头。
如果存在内部管道,则通过相同的转换过程将Message发送到下一个处理程序。但是,如果没有内部管道或您已经到达内部管道的末尾,则Message将发送回输出目的地。
32.1.1内容类型与参数类型

如前所述,为了使框架选择适当的MessageConverter,它需要参数类型以及(可选)内容类型信息。选择适当的MessageConverter的逻辑驻留在参数解析器(HandlerMethodArgumentResolvers)中,该解析器在调用用户定义的处理程序方法之前(即当框架知道实际的参数类型时)触发。如果参数类型与当前有效负载的类型不匹配,则框架将委派给预先配置的MessageConverters的堆栈,以查看其中是否有一个可以转换有效负载。如您所见,MessageConverter的Object fromMessage(Message<?> message, Class<?> targetClass);操作将targetClass作为其参数之一。该框架还确保提供的Message始终包含一个contentType头。当没有contentType标头时,它会插入按绑定的contentType标头或默认的contentType标头。contentType参数类型的组合是框架确定消息是否可以转换为目标类型的机制。如果找不到合适的MessageConverter,则会引发异常,您可以通过添加自定义MessageConverter来处理该异常(请参见“ 第32.3节,“用户定义的消息转换器” ”)。
但是,如果有效载荷类型与处理程序方法声明的目标类型匹配,该怎么办?在这种情况下,没有任何要转换的内容,并且有效载荷未经修改地传递。尽管这听起来很简单且合乎逻辑,但请记住以Message<?>或Object作为参数的处理程序方法。通过将目标类型声明为Object(在Java中为instanceof,是所有内容),实际上就放弃了转换过程。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IAqmFbX9-1591774641454)(https://www.springcloud.cc/images/note.png)]
不要期望仅根据contentType将Message转换为其他类型。请记住,contentType是目标类型的补充。如果需要,您可以提供一个提示,MessageConverter可能会也可能不会考虑。
32.1.2消息转换器

MessageConverters定义两种方法:
  1. Object fromMessage(Message<?> message, Class<?> targetClass);
  2. Message<?> toMessage(Object payload, @Nullable MessageHeaders headers);
复制代码
重要的是要了解这些方法的约定及其用法,尤其是在Spring Cloud Stream的上下文中。
fromMessage方法将传入的Message转换为参数类型。Message的有效载荷可以是任何类型,而MessageConverter的实际实现要支持多种类型。例如,某些JSON转换器可能支持有效负载类型为byte[],String等。当应用程序包含内部管道(即输入→handler1→handler2→…→输出)并且上游处理程序的输出结果为Message时,这可能不是初始连接格式,这一点很重要。
但是,toMessage方法的合同更为严格,必须始终将Message转换为有线格式:byte[]。
因此,出于所有意图和目的(尤其是在实现自己的转换器时),您将这两种方法视为具有以下签名:
  1. Object fromMessage(Message<?> message, Class<?> targetClass);
  2. Message<byte[]> toMessage(Object payload, @Nullable MessageHeaders headers);
复制代码
32.2提供的MessageConverters

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
如前所述,该框架已经提供了MessageConverters堆栈来处理最常见的用例。以下列表按优先级描述了提供的MessageConverters(使用了第一个有效的MessageConverter):
    ApplicationJsonMessageMarshallingConverter:org.springframework.messaging.converter.MappingJackson2MessageConverter的变体。对于contentType为application/json(默认)的情况,支持将Message的有效负载转换为POJO或从POJO转换为PO195。TupleJsonMessageConverter:已弃用支持将Message的有效负载转换为org.springframework.tuple.Tuple或从org.springframework.tuple.Tuple转换。ByteArrayMessageConverter:在contentType为application/octet-stream的情况下,支持将Message的有效载荷从byte[]转换为byte[]。它本质上是一个传递,主要是为了向后兼容而存在。ObjectStringMessageConverter:当contentType为text/plain时,支持将任何类型转换为String。它调用Object的toString()方法,或者,如果有效载荷为byte[],则调用新的String(byte[])。JavaSerializationMessageConverter:已弃用当contentType为application/x-java-serialized-object时,支持基于Java序列化的转换。KryoMessageConverter:已弃用当contentType为application/x-java-object时,支持基于Kryo序列化的转换。JsonUnmarshallingConverter:类似于ApplicationJsonMessageMarshallingConverter。当contentType为application/x-java-object时,它支持任何类型的转换。它期望将实际类型信息作为属性嵌入在contentType中(例如,application/x-java-object;type=foo.bar.Cat)。
当找不到合适的转换器时,框架将引发异常。发生这种情况时,应检查代码和配置,并确保您没有错过任何内容(即,确保使用绑定或标头提供了contentType)。但是,很可能您发现了一些不常见的情况(例如自定义contentType),并且提供的MessageConverters的当前堆栈不知道如何进行转换。在这种情况下,您可以添加自定义MessageConverter。请参见第32.3节“用户定义的消息转换器”。
32.3用户定义的消息转换器

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream公开了定义和注册其他MessageConverters的机制。要使用它,请实现org.springframework.messaging.converter.MessageConverter,将其配置为@Bean,并用@StreamMessageConverter进行注释。然后将其附加到MessageConverter的现有堆栈中。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-nc6sqsTu-1591774641454)(https://www.springcloud.cc/images/note.png)]
了解自定义MessageConverter实现已添加到现有堆栈的开头很重要。因此,自定义MessageConverter实现优先于现有实现,您可以覆盖它们并添加到现有转换器中。
下面的示例说明如何创建消息转换器bean以支持称为application/bar的新内容类型:
  1. @EnableBinding(Sink.class)
  2. @SpringBootApplication
  3. public static class SinkApplication {
  4.     ...
  5.     @Bean
  6.     @StreamMessageConverter
  7.     public MessageConverter customMessageConverter() {
  8.         return new MyCustomMessageConverter();
  9.     }
  10. }
  11. public class MyCustomMessageConverter extends AbstractMessageConverter {
  12.     public MyCustomMessageConverter() {
  13.         super(new MimeType("application", "bar"));
  14.     }
  15.     @Override
  16.     protected boolean supports(Class<?> clazz) {
  17.         return (Bar.class.equals(clazz));
  18.     }
  19.     @Override
  20.     protected Object convertFromInternal(Message<?> message, Class<?> targetClass, Object conversionHint) {
  21.         Object payload = message.getPayload();
  22.         return (payload instanceof Bar ? payload : new Bar((byte[]) payload));
  23.     }
  24. }
复制代码
Spring Cloud Stream还为基于Avro的转换器和模式演变提供支持。有关详细信息,请参见“ 第33章,Schema Evolution支持 ”。
33. Schema进化支持

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
Spring Cloud Stream为模式演化提供了支持,因此数据可以随着时间的推移而演化,并且仍然可以与较新的生产者和消费者以及反之亦然。大多数序列化模型,尤其是旨在跨不同平台和语言进行移植的模型,都依赖于一种描述如何在二进制有效负载中序列化数据的模式。为了序列化数据然后解释它,发送方和接收方都必须有权访问描述二进制格式的模式。在某些情况下,可以从序列化时的有效负载类型或反序列化时的目标类型推断模式。但是,许多应用程序可以从访问描述二进制数据格式的显式架构中受益。通过模式注册表,您可以以文本格式(通常为JSON)存储模式信息,并使该信息可用于需要它以二进制格式接收和发送数据的各种应用程序。模式可引用为一个元组,该元组包括:
    主题,是架构的逻辑名称模式版本模式格式,描述数据的二进制格式
以下各节详细介绍了架构演变过程中涉及的各种组件。
33.1 Schema注册客户端

90%高可用的千亿级微服务架构之道深入学习一线大厂必备微服务架构技术。VIP 教程限时免费领取。 ? 立即查看
与模式注册表服务器进行交互的客户端抽象是SchemaRegistryClient接口,该接口具有以下结构:
  1. public interface SchemaRegistryClient {
  2.     SchemaRegistrationResponse register(String subject, St
复制代码

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

×
懒得打字嘛,点击右侧快捷回复 【右侧内容,后台自定义】
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|手机版|Unity开发者联盟 ( 粤ICP备20003399号 )

GMT+8, 2024-6-27 15:17 , Processed in 0.256050 second(s), 26 queries .

Powered by Discuz! X3.5 Licensed

© 2001-2024 Discuz! Team.

快速回复 返回顶部 返回列表