spring cloud系列一:Cloud Native Applications

前言:

Cloud Native是一种应用程序开发风格,鼓励在持续交付和价值驱动开发领域轻松采用最佳实践。一个相关的学科是构建12-factor Apps 应用程序,其中开发实践与交付和运行目标相一致,例如通过使用声明式编程、管理、监控。 Spring Cloud以许多特定方式促进了这些开发风格,并且出发点是一组功能,即分布式系统中的所有组件都需要或需要时可轻松访问的功能。

Spring Boot涵盖了许多这些功能,我们在Spring Cloud基于spring boot构建这些功能。 Spring Cloud还提供了两个库:Spring Cloud Context和Spring Cloud Commons。 Spring Cloud Context为Spring云应用程序的ApplicationContext(bootstrap context, encryption, refresh scope and environment endpoints)提供实用程序和特殊服务。 Spring Cloud Commons是一组用于不同Spring云实现(例如Spring Cloud Netflix vs. Spring Cloud Consul)的抽象类和公共类。

如果由于“Illegal key size”而导致异常,并且您正在使用Sun的JDK,则需要安装Java Cryptography Extension (JCE) Unlimited Strength Jurisdiction Policy Files。有关更多信息,请参阅以下链接:

将文件解压缩到JDK / jre / lib / security文件夹中(无论您使用哪种版本的JRE / JDK x64 / x86)。

2. Spring Cloud Context: Application Context Services

Spring Boot对于如何使用Spring构建应用程序有着自己的观点:例如,它具有常规配置文件的常规位置,以及常见管理和监视任务的endpoints 。 Spring cloud建立在此基础之上,并添加了一些可能系统中所有组件都会使用或偶尔需要的功能。

2.1 The Bootstrap Application Context

Spring Cloud应用程序通过创建“bootstrap”context来运行,该bootstrap上下文是main application 的parent context。开箱即用,它负责从外部源加载配置属性,并且还解密本地外部配置文件中的属性。这两个上下文共享一个EnvironmentEnvironment是任何Spring应用程序的外部属性的来源。Bootstrap properties 具有较高优先级,因此默认情况下它们不能被本地配置覆盖。

和main bootstrap context相比,bootstrap context使用不同的约定来定位外部配置,所以不是使用application.yml(或.properties),而是使用bootstrap.yml,将bootstrap和main上下文的外部配置保持良好分离。例:

bootstrap.yml. 

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

如果您的应用程序需要来自服务器的任何特定于应用程序的配置,那么设置spring.application.name(在bootstrap.yml或application.yml中)是个不错的主意。

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

2.2 Application Context Hierarchies

如果您从SpringApplication或SpringApplicationBuilder构建application上下文,那么Bootstrap上下文将作为该上下文的父项添加。 Spring的一个特性是子级上下文从父级继承属性来源和配置文件,因此与不使用Spring Cloud Config构建应用程序上下文相比,“main”应用程序上下文将包含其他属性源。额外的property sources是:

  • “bootstrap”:如果在Bootstrap上下文中找到任何PropertySourceLocators,则可选CompositePropertySource将以高优先级出现,并且它们具有非空属性。一个例子是Spring Cloud Config Server的属性。请参阅 below的说明,了解如何自定义此属性来源的内容。
  • “applicationConfig”:classpath:bootstrap.yml(如果Spring  profile文件处于active状态,则bootstrap-***.yml也会被使用)。如果您有一个bootstrap.yml(或properties),则使用这些属性来配置Bootstrap上下文,然后当子context设置了此父context的时候就会将这些属性添加到子上下文中。这些父context中的属性的优先级低于application.yml(或properties)和任何其他作为创建Spring Boot应用程序过程的正常部分添加到子 context的属性源。请参阅 below的说明,了解如何自定义这些属性来源的内容。

由于properties sourcer的排序规则,“bootstrapentries 优先,但请注意,这些条目不包含bootstrap.yml中的任何数据,它们的优先级非常低,但可用于设置默认值。

您可以通过为您创建的任何ApplicationContext设置父上下文来扩展上下文层次结构,可以使用ApplicationContext自己的接口,或使用SpringApplicationBuilder的方法(parent(),child()和sibling())。引导程序上下文将是您自己创建的最高级祖先的父级。层次结构中的每个context都有自己的“bootstrap”properties source(可能为空),以避免无意中将属性从父 context继承到其后代。层次结构中的每个context也可以(原则上)具有不同的spring.application.name,因此如果有Config Server,则具有不同的远程属性源。普通的Spring应用程序上下文行为规则适用于属性解析:来自子上下文的属性覆盖父上下文中的属性,按名称和属性源名称区分(如果子context具有与父context相同名称的属性源,则父context中的这个属性源不包括在子上下文中)。

请注意,SpringApplicationBuilder允许您在整个层次结构中共享一个Environment,但这不是默认设置。因此,兄弟上下文尤其不需要具有相同的profile或properties source,即使他们与他们的父上下文共享相同的东西。

2.3 Changing the Location of Bootstrap Properties

可以使用spring.cloud.bootstrap.name(默认“bootstrap”)或spring.cloud.bootstrap.location(默认为空)来指定bootstrap.yml(或.properties)位置,例如,在System Properties中。这些属性的行为与具有相同名称的spring.config.*变体相同,实际上它们通过在bootstrap ApplicationContext 的Environment中设置这些属性来设置bootstrap ApplicationContext。如果存在active profile(来自spring.profiles.active或通过您正在构建的context中的Environment API配置),那么该配置文件中的属性也将被加载,就像在常规的Spring Boot应用程序中一样,例如。“development” profile的bootstrap-development.properties。

2.4 Overriding the Values of Remote Properties

通过booststrap 上下文添加到应用程序的属性源通常是“remote”(例如来自Config Server),默认情况下,它们不能被本地的属性覆盖,除了命令行。如果您希望允许应用程序使用自己的系统属性或配置文件覆盖远程属性,那么远程属性源必须通过设置spring.cloud.config.allowOverride = true来授予其权限(在本地设置这个属性不生效)。一旦设置了该标志,就会有一些更细粒度的设置来控制与System properties and the application’s local configuration有关的远程属性的location:spring.cloud.config.overrideNone = true允许任何本地属性源覆盖远程配置。spring.cloud.config.overrideSystemProperties = false,如果只有系统属性和env vars(环境变量)可以覆盖远程配置,本地配置文件不能覆盖远程配置。

2.5 Customizing the Bootstrap Configuration

通过向/META-INF/spring.factories中添加以org.springframework.cloud.bootstrap.BootstrapConfiguration 为key的条目可以训练bootstrap context执行任何您想要的操作。这个key对应的值是逗号分隔的@Configuration类列表,这些类用于创建bootstrap context。您想要在 "main" application context中可以自动装配的任何bean都可以在此处创建,并且该上下文中还有一个ApplicationContextInitializer类型的@Beans特殊规范。如果要控制启动顺序(默认顺序为“last”),可以使用@Order标记类。

添加自定义BootstrapConfiguration时要小心,您添加的这些类不能错误地@ComponentScanned到您的 "main" application context中,因为 "main" application context中可能不需要它们。对于尚未由@ComponentScan或@SpringBootApplication注释的配置类所包含的boot 配置类,使用单独的package name。

bootstrap process结束于将initializers注入主SpringApplication实例(即普通的Spring Boot启动sequence,无论它是作为独立应用程序运行还是部署在 application server中)。首先,通过spring.factories中的类创建bootstrap context,然后在启动之前,将所有类型为ApplicationContextInitializer的@Beans添加到主SpringApplication中。

2.6 Customizing the Bootstrap Property Sources

bootstrap process添加的外部配置的默认属性源是Config Server,但可以通过将类型PropertySourceLocator的Bean添加到bootstrap context(通过spring.factories)来添加其他属性源。例如,您可以使用指定的数据源从不同的server或数据库插入其他属性。

作为例子,请考虑以下简单的 custom locator

@Configuration
public class CustomPropertySourceLocator implements PropertySourceLocator {

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

}

传入的Environment是要创建的ApplicationContext的Environment,这个Environment也就是我们为其提供附加属性源的Environment。这个Environment将拥有普通的Spring Boot-provided property sources,因此您可以使用这些property source来定位特定于此Environmentproperty source(例如,通过在spring.application.name上键入它,如在缺省Config Server property source locator)。

如果你创建了一个jar包含这个类,然后添加一个META-INF / spring.factories包含如下内容:

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

那么任何包含该jar的application中,“customProperty” PropertySource都可用。

2.7 Environment Changes

应用程序将监听EnvironmentChangeEvent并以一些标准方式对更改做出反应(额外的ApplicationListener可以通过正常方式添加为@Beans)。当观察到EnvironmentChangeEvent时,这个event将有一个已更改的key-value列表,并且应用程序将使用这些key-value来:

  • Re-bind any @ConfigurationProperties beans in the context
  • Set the logger levels for any properties in logging.level.*

请注意,Config Client不会默认轮询 Environment中的更改,通常我们不会建议用于检测更改的方法(尽管您可以使用@Scheduled注释进行设置)。如果您有多个客户端应用程序,则最好将EnvironmentChangeEvent广播到所有实例,而不是让他们轮询更改(例如,使用Spring Cloud Bus)。

EnvironmentChangeEvent涵盖了一大类刷新使用场景,只要您可以对Environment进行实际更改并发布该事件(这些API是公开的并且是Spring核心的一部分)。您可以通过访问/ configprops端点(普通的Spring Boot Actuator功能)来验证更改是否绑定到@ConfigurationProperties bean。例如,DataSource可以在运行时更改其maxPoolSize(由Spring Boot创建的默认DataSource是一个@ConfigurationProperties bean)并动态增加容量。重新绑定@ConfigurationProperties不包含另一大类场景,这些场景中需要您需要更多地控制刷新,并且这里需要对整个ApplicationContext进行原子级更改。为了解决这些问题,我们使用了@RefreshScope。

2.8 Refresh Scope

标记为@RefreshScope的Spring @Bean将在配置更改时得到特殊处理。这解决了有状态bean被初始化时仅注入它们自己的配置的问题。例如,如果通过Environment更改数据库URL时,如果DataSource已打开了连接,我们可能希望这些连接的持有者能够完成他们正在执行的操作。然后当有人从连接池借用一个连接时,他会得到一个新的URL。

Refresh scope beans是在它们被使用时(即,当调用方法时)进行初始化的惰性代理,并且scop充当初始化值的缓存。要强制一个bean在下一次方法调用时重新初始化,只需要使其缓存项无效。

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

@RefreshScope在技术上适用于@Configuration类,但它可能会导致令人惊讶的行为:例如这并不意味着该类中定义的所有@Beans本身都是@RefreshScope。具体来说,anything依赖这些RefreshScope beans不能相信这些RefreshScope beans会在refresh初始化的时候更新,除非它本身也位于@RefreshScope中(上面所说的anything将在刷新时重建并重新注入它的依赖项,这些依赖项会从刷新的@Configuration重新初始化)

2.9 Encryption and Decryption

Spring Cloud有一个Environment pre-processor用于本地解密属性值。它遵循与配置服务器相同的规则,并且使用encrypt.* 作为key。因此,您可以使用{cipher}*格式的加密值,只要有一个有效的密钥,那么它们将在主应用程序上下文获取Environment之前被解密。要在应用程序中使用加密功能,您需要在您的类路径中包含Spring Security RSA(Maven协调“org.springframework.security:spring-security-rsa”),并且您还需要JVM中的全功能JCE扩展。

如果由于“Illegal key size”而导致异常,并且您正在使用Sun的JDK,则需要安装Java加密扩展(JCE)Unlimited Strength Jurisdiction Policy Files。有关更多信息,请参阅以下链接:

将文件解压缩到JDK / jre / lib / security文件夹中(无论您使用哪种版本的JRE / JDK x64 / x86)。


2.10 Endpoints

对于Spring Boot Actuator应用程序,还有一些额外的管理端点:

  • POST to /env to update the Environment and rebind @ConfigurationProperties and log levels
  • /refresh for re-loading the boot strap context and refreshing the @RefreshScope beans
  • /restart for closing the ApplicationContext and restarting it (disabled by default)
  • /pause and /resume for calling the Lifecycle methods (stop() and start() on the ApplicationContext)

3. Spring Cloud Commons: Common Abstractions

诸如服务发现,负载平衡和断路器等模式被抽象到了common abstraction layer(通用抽象层),这个抽象层适合于所有Spring Cloud客户端,而与实现无关(例如,通过Eureka或Consul发现)。

3.1 @EnableDiscoveryClient

Commons提供@EnableDiscoveryClient注释。这通过META-INF/spring.factories查找DiscoveryClient接口的实现。Discovery Client的实现类将在spring.factories中添加一个配置类,这个配置类的key是org.springframework.cloud.client.discovery.EnableDiscoveryClient。DiscoveryClient实现的例子有:

Spring Cloud Netflix EurekaSpring Cloud Consul Discovery and Spring Cloud Zookeeper Discovery.

默认情况下,DiscoveryClient的实现将自动注册本地Spring Boot服务器为remote discovery server。这可以通过在@EnableDiscoveryClient中设置autoRegister = false来禁用。

不再需要使用@EnableDiscoveryClient。只需在类路径上有一个DiscoveryClient实现就足以让Spring Boot应用程序注册service discovery server

3.1.1 Health Indicator

DiscoveryClient实现可以通过实现DiscoveryHealthIndicator接口加入Commons创建的Spring Boot HealthIndicator。要禁用composite 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,otherwise it can bubble up as the description of the rolled up HealthIndicator.

3.2 ServiceRegistry

Commons现在提供了一个ServiceRegistry接口,该接口提供了诸如register(Registration) and deregister(Registration)等方法,允许您提供自定义注册服务。Registration是一个marker interface

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

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

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

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

3.2.1 ServiceRegistry Auto-Registration

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

3.2.2 Service Registry Actuator Endpoint

一个 /service-registry监控端点由Commons提供。该端点依赖于Spring应用程序上下文中的Registration Bean。通过GET调用/ service-registry / instance-status将返回Registration的状态。使用String body向同一端点发布POST请求会将当前Registration的状态更改为新值。请参阅您正在使用的ServiceRegistry实现的文档,以获取更新状态的允许值以及状态的返回值。

3.3 Spring RestTemplate as a Load Balancer Client

RestTemplate可以自动配置为使用ribbon。要创建一个负载均衡的RestTemplate,创建一个RestTemplate @Bean并使用@LoadBalanced限定符。

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

@Configuration
public class MyConfiguration {

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

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

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

该URI需要使用virtual host name (即service name, not a host name)。Ribbon Client用于创建完整的物理地址。有关如何设置RestTemplate的详细信息,请参阅 RibbonAutoConfiguration

3.3.1 Retrying Failed Requests

负载均衡的RestTemplate可以配置为重试失败的request。默认情况下,此逻辑被禁用,您可以通过将 Spring Retry添加到应用程序的类路径来启用它。负载均衡的RestTemplate将遵循某些与重试失败请求相关的Ribbon configuration values。如果你想在类路径中使用Spring Retry但是又想禁用重试逻辑,你可以设置spring.cloud.loadbalancer.retry.enabled = false。您可以使用的属性是client.ribbon.MaxAutoRetries,client.ribbon.MaxAutoRetriesNextServer和client.ribbon.OkToRetryOnAllOperations。请参阅 Ribbon documentation 以获取有关属性的说明。

如果你想在你的重试中实现一个BackOffPolicy,你将需要创建一个LoadBalancedBackOffPolicyFactory类型的bean,并返回你想用于给定服务的BackOffPolicy。

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

上述示例中的client应替换为您的Ribbon客户端的名称

如果要将一个或多个RetryListener添加到retry中,您需要创建一个类型为LoadBalancedRetryListenerFactory的bean,并返回您希望用于给定服务的RetryListener数组。

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

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

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

3.4 Multiple RestTemplate objects

如果你想要一个没有负载均衡的RestTemplate,创建一个RestTemplate bean并且照常注入它。要访问负载平衡的RestTemplate,请在创建@Bean时使用@LoadBalanced限定符。

在下面的例子中,注意在简单的RestTemplate声明中的@Primary注解,以消除不合格的@Autowired注入

@Configuration
public class MyConfiguration {

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

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

public class MyClass {
    @Autowired
    private RestTemplate restTemplate;

    @Autowired
    @LoadBalanced
    private RestTemplate loadBalanced;

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

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

如果您看到如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.5 Ignore Network Interfaces

有时忽略某些具有明确名称的网络接口是有用的,因此它们可以从服务发现注册中被排除(例如,在Docker容器中运行的服务)。可以设置正则表达式列表,这可以使期望的网络接口被忽略。以下配置将忽略“docker0”接口以及以“veth”开头的所有接口。

application.yml. 

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

您还可以强制使用正则表达式列表仅使用指定的网络地址:

application.yml. 

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

您也可以强制只使用链路本地地址。有关更多详细信息,请参阅Inet4Address.html.isSiteLocalAddress()什么是链路本地地址

application.yml. 

spring:
  cloud:
    inetutils:
      useOnlySiteLocalInterfaces: true

3.6 HTTP Client Factories

Spring Cloud Commons提供了用于创建Apache HTTP客户端(ApacheHttpClientFactory)以及OK HTTP客户端(OkHttpClientFactory)的bean。OkHttpClientFactory bean只有在OK HTTP jar位于类路径中时才会被创建。另外,Spring Cloud Commons提供了用于创建连接管理器的Beans,两个客户端都需要使用这些beans。Apache HTTP客户端使用ApacheHttpClientConnectionManagerFactory,OK HTTP客户端使用OkHttpClientConnectionPoolFactory。如果您想定制在下游项目中创建HTTP客户端的方式,您可以提供您自己的这些bean的实现。您还可以通过将spring.cloud.httpclientfactories.apache.enabled或spring.cloud.httpclientfactories.ok.enabled设置为false来禁用这些bean的创建。



猜你喜欢

转载自blog.csdn.net/qq_34680763/article/details/80297783