目錄
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