天天看點

Spring CloudSpring Cloud ContextSpring Cloud Commons

目錄

Spring Cloud Context

Bootstrap Context

Application Context 層級

改變本地Bootstrap屬性

覆寫遠端屬性值

自定義boostrap配置

自定義boostrap PropertySource

 Environment 改變

Refresh Scope

Endpoints

Spring Cloud Commons

@EnableDiscoveryClient

健康訓示器

服務發現執行個體排序

服務注冊

自動注冊

Actuator Endpoint

負載均衡

失敗請求重試

Multiple RestTemplate objects

spring cloud 中的許多特性都已經被spring boot實作,其他一些特性主要是通過spring cloud context 和 spring cloud commons來實作的。spring cloud context提供了一些常用的公共類以及spring cloud 特有的服務(bootstrap context, encryption, refresh scope, and environment endpoints);spring cloud commons提供了一系列的抽象類和公共類,供spring cloud不同的實作元件去使用(such as Spring Cloud Netflix and Spring Cloud Consul).。

Spring Cloud Context

通過spring boot 建構 spring application已經很友善了,她還提供了一些監控、管理相關的endpoint; 由于 spring cloud 是建立在spring boot基礎之上的,是以spring boot 的功能spring cloud都滿足,此外還提供了一些特有的特性,這些特性可能在所有的spring cloud元件中都會(或者偶爾)使用到。

Bootstrap Context

spring cloud 應用中建立一個 bootstrap context,她是作為了主應用的 parent context ;主要負責從外部資源(主要指配置中心)加載配置以及再解碼本地的配置檔案。appliction context 和 bootstrap context 共享了相同Environment. 預設情況下,bootstrap properties(從配置中心加載的屬性) 有更高的優先級,是以它們不能被本地的配置所覆寫。

bootstrap context使用了和主應用的 context 不同的配置方式,使用的是 bootstrap.yml 而不是 application.yml,這主要是為了保證兩個context的配置可以很好的分離。如果需要禁用 bootstrap context ,我們可以設定環境變量spring.cloud.bootstrap.enabled=false

Application Context 層級

如果你通過SpringApplication 或者 SpringApplicationBuilder來建構你的應用,那麼Bootstrap context 将會作為application context 的 parent context。子容器會繼承從父容器中的屬性,新增的屬性有:

  • bootstrap

如果在Bootstrap context有任何非空的屬性,那麼這些屬性的優先級都更高,比如說從 spring cloud config 加載的配置,預設優先級就比本地配置高。

  • applicationConfig

如果你配置bootstrap.yml, 那麼bootstrap.yml中的屬性會用來配置 Bootstrap context;當Bootstrap context配置完成後就會把這些屬性添加到application context 中,這些屬性的優先級會比配置在application.yml 中的低

由于bootstrap.yml 的優先級比較低,是以可以用來設定一些屬性的預設值。

spring cloud 允許你擴充ApplicationContext ,例如可以使用 ApplicationContext已有的接口或者使用SpringApplicationBuilder提供的方法(parent(), child() and sibling());這個bootstrap context是所有容器的父容器。

改變本地Bootstrap屬性

能夠通過設定環境變量spring.cloud.bootstrap.name(預設是bootstrap)配置引導檔案(bootstrap.yml)的名字,通過 spring.cloud.bootstrap.location(預設是空)配置路徑。

例如在環境變量中配置:

spring.cloud.bootstrap.name=start

spring.cloud.bootstrap.location=classpath:/test/

覆寫遠端屬性值

通過bootstrap context從遠端添加的屬性預設情況下是不能被本地配置所覆寫的。如果你想要使用系統變量或者配置檔案來覆寫遠端的屬性,在遠端配置中設定屬性spring.cloud.config.allowOverride=true(注意:在本地檔案中設定是無效的)來授權,同時需要配置哪些可以覆寫:

spring.cloud.config.overrideNone=true                            #  本地任何屬性都可以覆寫

spring.cloud.config.overrideSystemProperties=false       #環境變量,指令行參數可以覆寫,本地配置檔案除外

自定義boostrap配置

bootstrap context通過/META-INF/spring.factories配置的類初始化的所有的Bean都會在SpingApplicatin啟動前加入到它的上下文裡去。spring.factories中key 使用 org.springframework.cloud.bootstrap.BootstrapConfiguration, 所有需要用來配置bootstrap context的配置類用逗号分隔;可以通過@Order來更改初始化序列,預設是”last”

首先使用spring.factories中配置的類來建立bootstrap context,然後所有使用@Bean注解的ApplicationContextInitializer将會被添加 main application context。

自定義boostrap PropertySource

預設情況下外部化配置都是從配置中心擷取屬性,但是也可以通過實作PropertySourceLocator來實作添加屬性到bootstrap context(需要把實作類添加到spring.factories);比如可以從不同的資料庫或者伺服器擷取配置。

 Environment 改變

應用可以監聽EnvironmentChangeEvent來響應環境變量被改變的事件。一旦EnvironmentChangeEvent事件被觸發,應用程式将會做這些事情:

  • 重新綁定使用了@ConfigurationProperties的類
  • 根據logging.level.*來設定應用的日志級别

預設情況下,Config Client不輪詢Environment的改變。一般情況,不建議使用這種方式來監測變化(雖然你可以通過@Scheduled注解來設定)。對于可擴充的應用程式,使用廣播EnvironmentChangeEvent到所有執行個體的方式,好過輪詢的方式。(比如使用Spring Cloud Bus項目)。

Refresh Scope

當有配置發生變化的時候,使用了@RefreshScope的類将會被處理;這個特性解決了有狀态bean隻能在初始化注入配置的問題。例如:比如,在使用DataSource獲得一個資料庫連接配接的是時候,當通過Environment修改資料庫連接配接字元串的時候,我們可以通過執行@RefreshScope來根據修改的配置擷取一個新的URL的連接配接。

Refresh scope beans是延遲初始化的,在第一次使用的時候才初始化,這個scope充當了初始值的緩存;為了在下一次調用時強制初始化,必須使緩存無效。

RefreshScope在容器中是一個bean, 提供了一個public方法refreshAll(),通過清理目前的緩存來重新整理,可以通過通路/refresh來觸發重新整理;也可以使用bean的名字來重新整理refresh(String),要使用還需要暴露出這個endpoint

Endpoints

相對于Spring Boot Actuator的應用,添加了一些管理端點:

  • POST /actuator/env :更新Environment重新加載@ConfigurationProperties和日志級别
  • /actuator/refresh: 重新初始化添加了@RefreshScope 的bean
  • /actuator/restart: 重新開機初始化ApplicationContext,重新開機 (預設是禁用的)
  • /actuator/pause: 調用ApplicationContext生命周期的方法stop()和start()
如果禁用了/actuator/restart,那麼/actuator/pause和/actuator/resume都會被禁用

Spring Cloud Commons

服務發現、負載平衡和斷路器等通用的模型,本身是一個抽象層,可以被所有Spring Cloud元件獨立的實作,例如服務發現有具體的實作Eureka、Consul。

@EnableDiscoveryClient

Spring Cloud Commons 提供了@EnableDiscoveryClient這個注解,主要的作用是在META-INF/spring.factories尋找DiscoveryClient的實作類;實作了DiscoveryClient的類都會配置在spring.factories中,添加key為 org.springframework.cloud.client.discovery.EnableDiscoveryClient

@EnableDiscoveryClient現在已經不是必須的了,隻要DiscoveryClient的實作類在classpath就OK

健康訓示器

springboot 中提供了一個檢查檢查的接口HealthIndicator, DiscoveryClient能夠通過實作DiscoveryHealthIndicator來做健康檢查。設定spring.cloud.discovery.client.composite-indicator.enabled=false來禁用這種混和的健康檢查;DiscoveryClientHealthIndicator通常是自動配置的,設定spring.cloud.discovery.client.health-indicator.enabled=false來禁用;設定spring.cloud.discovery.client.health-indicator.include-description=false來禁用description字段,如果沒有禁用,就會一直向上層傳遞。

服務發現執行個體排序

DiscoveryClient繼承了Ordered; 當你使用多個服務發現的時候這個會很有用,可以定義通過這種方式來按照指定的順序來從注冊中心加載bean。DiscoveryClient預設的order設定的是 0 ;如果想要為你自己實作的DiscoveryClient設定不同的order,僅僅需要覆寫getOrder()。除此之外Spring Cloud提供了配置spring.cloud.{clientIdentifier}.discovery.order來設定order,這其中主要是ConsulDiscoveryClient, EurekaDiscoveryClient, ZookeeperDiscoveryClient;

服務注冊

ServiceRegistry接口提供了注冊服務register(Registration) 和 取消注冊deregister(Registration)的方法。

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

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

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

每個

ServiceRegistry

實作類都會提供一個對應

Registry

  • ZookeeperRegistration 使用的 ZookeeperServiceRegistry
  • EurekaRegistration 使用的 EurekaServiceRegistry
  • ConsulRegistration 使用的 ConsulServiceRegistry

自動注冊

預設情況下ServiceRegistry的實作類在運作的時候會自動注冊服務,兩種方式來禁用自動注冊服務

@EnableDiscoveryClient(autoRegister=false)

spring.cloud.service-registry.auto-registration.enabled=false

當一個服務自動注冊的時會觸發兩個事件,第一個是InstancePreRegisteredEvent,在注冊之前觸發;第二個是InstanceRegisteredEvent 在注冊完成之後觸發;可以使用ApplicationListener來監聽這兩個事件。

spring.cloud.service-registry.auto-registration.enabled=false

的時候就不會觸發這兩個事件

Actuator Endpoint

Spring Cloud Commons 提供了一個/service-registry 端點,這個endpoint依賴于容器中的Registration。GET請求這個位址将會傳回Registration的狀态;POST請求這個位址可以修改Registration,這個json格式的body中必須要包含一個status;查詢ServiceRegistry的實作類文檔來确定status的值;比如Eureka的狀态值:UP, DOWN, OUT_OF_SERVICE, UNKNOWN.

負載均衡

建立一個支援負載均衡的

RestTemplate

,使用

@LoadBalanced

@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;
    }
}
           

失敗請求重試

RestTemplate可以配置請求失敗後的重試政策;預設這個邏輯是禁止的,如果需要可以開啟,隻需要添加 Spring Retry到classpath; 如果spring retry已經在classpath,你想要禁用這個retry的功能,那麼可以配置spring.cloud.loadbalancer.retry.enabled=false

如果想要自定義一個BackOffPolicy,需要建立一個LoadBalancedRetryFactory并覆寫方法createBackOffPolicy ; eg:

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

Multiple RestTemplate objects

如何建立一個支援負載均衡的

RestTemplate

和不支援負載均衡的

RestTemplate

以及注入的方式?看下面的列子:

@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);
    }
}
           
@Primary的作用是在使用@Autowired注入時,如果發現了多個類型的bean, 就選擇使用了@Primary 的bean

繼續閱讀