1 什么是災難性雪崩效應?
如下圖的過程所示,災難性雪崩形成原因就大致如此:
造成災難性雪崩效應的原因,可以簡單歸結為下述三種:
- 服務提供者不可用。如:硬件故障、程序BUG、緩存擊穿、並發請求量過大等。
- 重試加大流量。如:用戶重試、代碼重試邏輯等。
- 服務調用者不可用。如:同步請求阻塞造成的資源耗盡等。
雪崩效應最終的結果就是:服務鏈條中的某一個服務不可用,導致一系列的服務不可用,最終造成服務邏輯崩潰。這種問題造成的后果,往往是無法預料的。
2 如何解決災難性雪崩效應?
解決災難性雪崩效應的方式通常有:降級、隔離、熔斷、請求緩存、請求合並。
在Spring cloud中處理服務雪崩效應,都需要依賴hystrix組件。在pom文件中都需要引入下述依賴:
<dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
通常來說,開發的時候,使用ribbon處理服務災難雪崩效應(因此章節2示例均采用Ribbon,章節3是Feign實現方式詳解),開發的成本低。維護成本高。使用feign技術處理服務災難雪崩效應,開發的成本較高,維護成本低。
2.1 降級
降級是指,當請求超時、資源不足等情況發生時進行服務降級處理,不調用真實服務邏輯,而是使用快速失敗(fallback)方式直接返回一個托底數據,保證服務鏈條的完整,避免服務雪崩。
解決服務雪崩效應,都是避免application client請求application service時,出現服務調用錯誤或網絡問題。處理手法都是在application client中實現。我們需要在application client相關工程中導入hystrix依賴信息。並在對應的啟動類上增加新的注解@EnableCircuitBreaker,這個注解是用於開啟hystrix熔斷器的,簡言之,就是讓代碼中的hystrix相關注解生效。
引入依賴:
<!-- hystrix依賴, 處理服務災難雪崩效應的。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
啟動器代碼:
/** * @EnableCircuitBreaker - 開啟斷路器。就是開啟hystrix服務容錯能力。 * 當應用啟用Hystrix服務容錯的時候,必須增加的一個注解。 */ @EnableCircuitBreaker @EnableEurekaClient @SpringBootApplication public class HystrixApplicationClientApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplicationClientApplication.class, args); } }
在調用application service相關代碼中,增加新的方法注解@HystrixCommand,代表當前方法啟用Hystrix處理服務雪崩效應。
@HystrixCommand注解中的屬性:fallbackMethod - 代表當調用的application service出現問題時,調用哪個fallback快速失敗處理方法返回托底數據。
實現類:
@Service public class HystrixService { @Autowired private LoadBalancerClient loadBalancerClient; /** * 服務降級處理。 * 當前方法遠程調用application service服務的時候,如果service服務出現了任何錯誤(超時,異常等) * 不會將異常拋到客戶端,而是使用本地的一個fallback(錯誤返回)方法來返回一個托底數據。 * 避免客戶端看到錯誤頁面。 * 使用注解來描述當前方法的服務降級邏輯。 * @HystrixCommand - 開啟Hystrix命令的注解。代表當前方法如果出現服務調用問題,使用Hystrix邏輯來處理。 * 重要屬性 - fallbackMethod * 錯誤返回方法名。如果當前方法調用服務,遠程服務出現問題的時候,調用本地的哪個方法得到托底數據。 * Hystrix會調用fallbackMethod指定的方法,獲取結果,並返回給客戶端。 * @return */ @HystrixCommand(fallbackMethod="downgradeFallback") public List<Map<String, Object>> testDowngrade() { System.out.println("testDowngrade method : " + Thread.currentThread().getName()); ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); StringBuilder sb = new StringBuilder(); sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/test"); System.out.println("request application service URL : " + sb.toString()); RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Map<String, Object>> result = response.getBody(); return result; } /** * fallback方法。本地定義的。用來處理遠程服務調用錯誤時,返回的基礎數據。 */ private List<Map<String, Object>> downgradeFallback(){ List<Map<String, Object>> result = new ArrayList<>(); Map<String, Object> data = new HashMap<>(); data.put("id", -1); data.put("name", "downgrade fallback datas"); data.put("age", 0); result.add(data); return result; } }
2.2 緩存
緩存是指請求緩存。通常意義上說,就是將同樣的GET請求結果緩存起來,使用緩存機制(如redis、mongodb)提升請求響應效率。
使用請求緩存時,需要注意非冪等性操作對緩存數據的影響。
請求緩存是依托某一緩存服務來實現的。在案例中使用redis作為緩存服務器,那么可以使用spring-data-redis來實現redis的訪問操作。需要在application client相關工程中導入下述依賴:
<!-- hystrix依賴, 處理服務災難雪崩效應的。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency> <!-- spring-data-redis spring cloud中集成的spring-data相關啟動器。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
在Spring Cloud應用中,啟用spring對cache的支持,需要在啟動類中增加注解@EnableCaching,此注解代表當前應用開啟spring對cache的支持。簡言之就是使spring-data-redis相關的注解生效,如:@CacheConfig、@Cacheable、@CacheEvict等。
啟動器:
/** * @EnableCircuitBreaker - 開啟斷路器。就是開啟hystrix服務容錯能力。 * 當應用啟用Hystrix服務容錯的時候,必須增加的一個注解。 */ @EnableCircuitBreaker /** * @EnableCaching - 開啟spring cloud對cache的支持。 * 可以自動的使用請求緩存,訪問redis等cache服務。 */ @EnableCaching @EnableEurekaClient @SpringBootApplication public class HystrixApplicationClientApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplicationClientApplication.class, args); } }
spring cloud會檢查每個冪等性請求,如果請求完全相同(路徑、參數等完全一致),則首先訪問緩存redis,查看緩存數據,如果緩存中有數據,則不調用遠程服務application service。如果緩存中沒有數據,則調用遠程服務,並將結果緩存到redis中,供后續請求使用。
如果請求是一個非冪等性操作,則會根據方法的注解來動態管理redis中的緩存數據,避免數據不一致。
注意:使用請求緩存會導致很多的隱患,如:緩存管理不當導致的數據不同步、問題排查困難等。在商業項目中,解決服務雪崩效應不推薦使用請求緩存。
實現類:
/** * 在類上,增加@CacheConfig注解,用來描述當前類型可能使用cache緩存。 * 如果使用緩存,則緩存數據的key的前綴是cacheNames。 * cacheNames是用來定義一個緩存集的前綴命名的,相當於分組。 */ @CacheConfig(cacheNames={"test.hystrix.cache"}) @Service public class HystrixService { @Autowired private LoadBalancerClient loadBalancerClient; /** * 請求緩存處理方法。 * 使用注解@Cacheable描述方法。配合啟動器中的相關注解,實現一個請求緩存邏輯。 * 將當期方法的返回值緩存到cache中。 * 屬性 value | cacheNames - 代表緩存到cache的數據的key的一部分。 * 可以使用springEL來獲取方法參數數據,定制特性化的緩存key。 * 只要方法增加了@Cacheable注解,每次調用當前方法的時候,spring cloud都會先訪問cache獲取數據, * 如果cache中沒有數據,則訪問遠程服務獲取數據。遠程服務返回數據,先保存在cache中,再返回給客戶端。 * 如果cache中有數據,則直接返回cache中的數據,不會訪問遠程服務。 * * 請求緩存會有緩存數據不一致的可能。 * 緩存數據過期、失效、臟數據等情況。 * 一旦使用了請求緩存來處理冪等性請求操作。則在非冪等性請求操作中必須管理緩存。避免緩存數據的錯誤。 * @return */ @Cacheable("testCache4Get") public List<Map<String, Object>> testCache4Get() { System.out.println("testCache4Get method thread name : " + Thread.currentThread().getName()); ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); StringBuilder sb = new StringBuilder(); sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/test"); System.out.println("request application service URL : " + sb.toString()); RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Map<String, Object>> result = response.getBody(); return result; } /** * 非冪等性操作。用於模擬刪除邏輯。 * 一旦非冪等性操作執行,則必須管理緩存。就是釋放緩存中的數據。刪除緩存數據。 * 使用注解@CacheEvict管理緩存。 * 通過數據cacheNames | value來刪除對應key的緩存。 * 刪除緩存的邏輯,是在當前方法執行結束后。 * @return */ @CacheEvict("testCache4Get") public List<Map<String, Object>> testCache4Del() { ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); StringBuilder sb = new StringBuilder(); sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/test"); System.out.println("request application service URL : " + sb.toString()); RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Map<String, Object>> result = response.getBody(); return result; } }
2.3 請求合並
請求合並是指,在一定時間內,收集一定量的同類型請求,合並請求需求后,一次性訪問服務提供者,得到批量結果。這種方式可以減少服務消費者和服務提供者之間的通訊次數,提升應用執行效率。
未使用請求合並:
使用請求合並:
什么情況下使用請求合並:
在微服務架構中,我們將一個項目拆分成很多個獨立的模塊,這些獨立的模塊通過遠程調用來互相配合工作,但是,在高並發情況下,通信次數的增加會導致總的通信時間增加,同時,線程池的資源也是有限的,高並發環境會導致有大量的線程處於等待狀態,進而導致響應延遲,為了解決這些問題,我們需要來了解Hystrix的請求合並。
通常來說,服務鏈條超出4個,不推薦使用請求合並。因為請求合並有等待時間。
請求合並的缺點:
設置請求合並之后,本來一個請求可能5ms就搞定了,但是現在必須再等10ms看看還有沒有其他的請求一起的,這樣一個請求的耗時就從5ms增加到15ms了,不過,如果我們要發起的命令本身就是一個高延遲的命令,那么這個時候就可以使用請求合並了,因為這個時候時間窗的時間消耗就顯得微不足道了,另外高並發也是請求合並的一個非常重要的場景。
引入依賴:
<!-- hystrix依賴, 處理服務災難雪崩效應的。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
啟動器:
/** * @EnableCircuitBreaker - 開啟斷路器。就是開啟hystrix服務容錯能力。 * 當應用啟用Hystrix服務容錯的時候,必須增加的一個注解。 */ @EnableCircuitBreaker @EnableEurekaClient @SpringBootApplication public class HystrixApplicationClientApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplicationClientApplication.class, args); } }
使用注解@HystrixCollapser來描述需要合並請求的方法,並提供合並方法使用注解@HystrixCommand來描述。當合並條件(@HystrixCollapser)滿足時,會觸發合並方法(@HystrixCommand)來調用遠程服務並得到結果。
@HystrixCollapser注解介紹:此注解描述的方法,返回值類型必須是java.util.concurrent.Future類型的。代表方法為異步方法。
@HystrixCollapser注解的屬性:
batchMethod - 請求合並方法名。
scope - 請求合並方式。可選值有REQUEST和GLOBAL。REQUEST代表在一個request請求生命周期內的多次遠程服務調用請求需要合並處理,此為默認值。GLOBAL代表所有request線程內的多次遠程服務調用請求需要合並處理。
timerDelayInMilliseconds - 多少時間間隔內的請求進行合並處理,默認值為10ms。建議設置時間間隔短一些,如果單位時間並發量不大,並沒有請求合並的必要。
maxRequestsInBatch - 設置合並請求的最大極值,也就是timerDelayInMilliseconds時間內,最多合並多少個請求。默認值是Integer.MAX_VALUE。
實現類:
@Service public class HystrixService { @Autowired private LoadBalancerClient loadBalancerClient; /** * 需要合並請求的方法。 * 這種方法的返回結果一定是Future類型的。 * 這種方法的處理邏輯都是異步的。 * 是application client在一定時間內收集客戶端請求,或收集一定量的客戶端請求,一次性發給application service。 * application service返回的結果,application client會進行二次處理,封裝為future對象並返回 * future對象需要通過get方法獲取最終的結果。 get方法是由控制器調用的。所以控制器調用service的過程是一個異步處理的過程。 * 合並請求的方法需要使用@HystrixCollapser注解描述。 * batchMethod - 合並請求后,使用的方法是什么。如果當前方法有參數,合並請求后的方法參數是當前方法參數的集合,如 int id >> int[] ids。 * scope - 合並請求的請求作用域。可選值有global和request。 * global代表所有的請求線程都可以等待可合並。 常用,所有瀏覽器或者請求源(Postman、curl等)調用的請求 * request代表一個請求線程中的多次遠程服務調用可合並 * collapserProperties - 細致配置。就是配置合並請求的特性。如等待多久,如可合並請求的數量。 * 屬性的類型是@HystrixProperty類型數組,可配置的屬性值可以直接通過字符串或常量類定義。 * timerDelayInMilliseconds - 等待時長 * maxRequestsInBatch - 可合並的請求最大數量。 * * 方法處理邏輯不需要實現,直接返回null即可。 * 合並請求一定是可合並的。也就是同類型請求。同URL的請求。 * @param id * @return */ @HystrixCollapser(batchMethod = "mergeRequest", scope = com.netflix.hystrix.HystrixCollapser.Scope.GLOBAL, collapserProperties = { // 請求時間間隔在20ms之內的請求會被合並為一個請求,默認為10ms @HystrixProperty(name = "timerDelayInMilliseconds", value = "20"), // 設置觸發批處理執行之前,在批處理中允許的最大請求數。 @HystrixProperty(name = "maxRequestsInBatch", value = "200"), }) public Future<Map<String, Object>> testMergeRequest(Long id){ return null; } /** * 批量處理方法。就是合並請求后真實調用遠程服務的方法。 * 必須使用@HystrixCommand注解描述,代表當前方法是一個Hystrix管理的服務容錯方法。 * 是用於處理請求合並的方法。 * @param ids * @return */ @HystrixCommand public List<Map<String, Object>> mergeRequest(List<Long> ids){ ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); StringBuilder sb = new StringBuilder(); sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/testMerge?"); for(int i = 0; i < ids.size(); i++){ Long id = ids.get(i); if(i != 0){ sb.append("&"); } sb.append("ids=").append(id); } System.out.println("request application service URL : " + sb.toString()); RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Map<String, Object>> result = response.getBody(); return result; } }
2.4 熔斷
當一定時間內,異常請求比例(請求超時、網絡故障、服務異常等)達到閥值時,啟動熔斷器,熔斷器一旦啟動,則會停止調用具體服務邏輯,通過fallback快速返回托底數據,保證服務鏈的完整。
熔斷有自動恢復機制,如:當熔斷器啟動后,每隔5秒,嘗試將新的請求發送給服務提供者,如果服務可正常執行並返回結果,則關閉熔斷器,服務恢復。如果仍舊調用失敗,則繼續返回托底數據,熔斷器持續開啟狀態。
引入依賴:
<!-- hystrix依賴, 處理服務災難雪崩效應的。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
啟動器:
/** * @EnableCircuitBreaker - 開啟斷路器。就是開啟hystrix服務容錯能力。 * 當應用啟用Hystrix服務容錯的時候,必須增加的一個注解。 */ @EnableCircuitBreaker @EnableEurekaClient @SpringBootApplication public class HystrixApplicationClientApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplicationClientApplication.class, args); } }
熔斷的實現是在調用遠程服務的方法上增加@HystrixCommand注解。當注解配置滿足則開啟或關閉熔斷器。
@Service public class HystrixService { @Autowired private LoadBalancerClient loadBalancerClient; /** * 熔斷機制 * 相當於一個強化的服務降級。 服務降級是只要遠程服務出錯,立刻返回fallback結果。 * 熔斷是收集一定時間內的錯誤比例,如果達到一定的錯誤率。則啟動熔斷,返回fallback結果。 * 間隔一定時間會將請求再次發送給application service進行重試。如果重試成功,熔斷關閉。 * 如果重試失敗,熔斷持續開啟,並返回fallback數據。 * @HystrixCommand 描述方法。 * fallbackMethod - fallback方法名 * commandProperties - 具體的熔斷標准。類型是HystrixProperty數組。 * 可以通過字符串或常亮類配置。 * CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD - 錯誤數量。在10毫秒內,出現多少次遠程服務調用錯誤,則開啟熔斷。 * 默認20個。 10毫秒內有20個錯誤請求則開啟熔斷。 * CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE - 錯誤比例。在10毫秒內,遠程服務調用錯誤比例達標則開啟熔斷。 * CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS - 熔斷開啟后,間隔多少毫秒重試遠程服務調用。默認5000毫秒。 * @return */ @HystrixCommand(fallbackMethod = "breakerFallback", commandProperties = { // 默認20個;10ms內請求數大於20個時就啟動熔斷器,當請求符合熔斷條件時將觸發getFallback()。 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD, value="10"), // 請求錯誤率大於50%時就熔斷,然后for循環發起請求,當請求符合熔斷條件時將觸發getFallback()。 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE, value="50"), // 默認5秒;熔斷多少秒后去嘗試請求 @HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS, value="5000")} ) public List<Map<String, Object>> testBreaker() { System.out.println("testBreaker method thread name : " + Thread.currentThread().getName()); ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); StringBuilder sb = new StringBuilder(); sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/test"); System.out.println("request application service URL : " + sb.toString()); RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Map<String, Object>> result = response.getBody(); return result; } private List<Map<String, Object>> breakerFallback(){ System.out.println("breakerFallback method thread name : " + Thread.currentThread().getName()); List<Map<String, Object>> result = new ArrayList<>(); Map<String, Object> data = new HashMap<>(); data.put("id", -1); data.put("name", "breaker fallback datas"); data.put("age", 0); result.add(data); return result; } }
注解屬性描述:
CIRCUIT_BREAKER_ENABLED
"circuitBreaker.enabled";
# 是否開啟熔斷策略。默認值為true。
CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD
"circuitBreaker.requestVolumeThreshold";
# 10ms內,請求並發數超出則觸發熔斷策略。默認值為20。
CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS
"circuitBreaker.sleepWindowInMilliseconds";
# 當熔斷策略開啟后,延遲多久嘗試再次請求遠程服務。默認為5秒。
CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE
"circuitBreaker.errorThresholdPercentage";
# 10ms內,出現錯誤的請求百分比達到限制,則觸發熔斷策略。默認為50%。
CIRCUIT_BREAKER_FORCE_OPEN
"circuitBreaker.forceOpen";
# 是否強制開啟熔斷策略。即所有請求都返回fallback托底數據。默認為false。
CIRCUIT_BREAKER_FORCE_CLOSED
"circuitBreaker.forceClosed";
# 是否強制關閉熔斷策略。即所有請求一定調用遠程服務。默認為false。
2.5 隔離
所謂隔離,就是當服務發生問題時,使用技術手段隔離請求,保證服務調用鏈的完整。隔離分為線程池隔離和信號量隔離兩種實現方式。
2.5.1 線程池隔離
所謂線程池隔離,就是將並發請求量大的部分服務使用獨立的線程池處理,避免因個別服務並發過高導致整體應用宕機。
線程池隔離優點:
- 使用線程池隔離可以完全隔離依賴的服務,請求線程可以快速放回。
- 當線程池出現問題時,線程池是完全隔離狀態的,是獨立的,不會影響到其他服務的正常執行。
- 當崩潰的服務恢復時,線程池可以快速清理並恢復,不需要相對漫長的恢復等待。
- 獨立的線程池也提供了並發處理能力。
線程池隔離缺點:
線程池隔離機制,會導致服務硬件計算開銷加大(CPU計算、調度等),每個命令的執行都涉及到排隊、調度、上下文切換等,這些命令都是在一個單獨的線程上運行的。
線程池隔離的實現方式同樣是使用@HystrixCommand注解。相關注解配置屬性如下:
- groupKey - 分組命名,在application client中會為每個application service服務設置一個分組,同一個分組下的服務調用使用同一個線程池。默認值為this.getClass().getSimpleName();
- commandKey - Hystrix中的命令命名,默認為當前方法的方法名。可省略。用於標記當前要觸發的遠程服務是什么。
- threadPoolKey - 線程池命名。要求一個應用中全局唯一。多個方法使用同一個線程池命名,代表使用同一個線程池。默認值是groupKey數據。
- threadPoolProperties - 用於為線程池設置的參數。其類型為HystrixProperty數組。常用線程池設置參數有:
- coreSize - 線程池最大並發數,建議設置標准為:requests per second at peak when healthy * 99th percentile latency in second + some breathing room。 即每秒最大支持請求數*(99%平均響應時間 + 一定量的緩沖時間(99%平均響應時間的10%-20%))。如:每秒可以處理請求數為1000,99%的響應時間為60ms,自定義提供緩沖時間為60*0.2=12ms,那么結果是 1000*(0.060+0.012) = 72。
- maxQueueSize - BlockingQueue的最大長度,默認值為-1,即不限制。如果設置為正數,等待隊列將從同步隊列SynchronousQueue轉換為阻塞隊列LinkedBlockingQueue。
- queueSizeRejectionThreshold - 設置拒絕請求的臨界值。默認值為5。此屬性是配合阻塞隊列使用的,也就是不適用maxQueueSize=-1(為-1的時候此值無效)的情況。是用於設置阻塞隊列限制的,如果超出限制,則拒絕請求。此參數的意義就是在服務啟動后,可以通過Hystrix的API調用config API動態修改,而不用用重啟服務,不常用。
- keepAliveTimeMinutes - 線程存活時間,單位是分鍾。默認值為1。
- execution.isolation.thread.timeoutInMilliseconds - 超時時間,默認為1000ms。當請求超時自動中斷,返回fallback,避免服務長期阻塞。
- execution.isolation.thread.interruptOnTimeout - 是否開啟超時中斷。默認為TRUE。和上一個屬性配合使用。
引入依賴:
<!-- hystrix依賴, 處理服務災難雪崩效應的。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
啟動器:
/** * @EnableCircuitBreaker - 開啟斷路器。就是開啟hystrix服務容錯能力。 * 當應用啟用Hystrix服務容錯的時候,必須增加的一個注解。 */ @EnableCircuitBreaker @EnableEurekaClient @SpringBootApplication public class HystrixApplicationClientApplication { public static void main(String[] args) { SpringApplication.run(HystrixApplicationClientApplication.class, args); } }
實現類:
@Service public class HystrixService { @Autowired private LoadBalancerClient loadBalancerClient; /** * 如果使用了@HystrixCommand注解,則Hystrix自動創建獨立的線程池。 * groupKey和threadPoolKey默認值是當前服務方法所在類型的simpleName * * 所有的fallback方法,都執行在一個HystrixTimer線程池上。 * 這個線程池是Hystrix提供的一個,專門處理fallback邏輯的線程池。 * * 線程池隔離實現 * 線程池隔離,就是為某一些服務,獨立划分線程池。讓這些服務邏輯在獨立的線程池中運行。 * 不使用tomcat提供的默認線程池。 * 線程池隔離也有熔斷能力。如果線程池不能處理更多的請求的時候,會觸發熔斷,返回fallback數據。 * groupKey - 分組名稱,就是為服務划分分組。如果不配置,默認使用threadPoolKey作為組名。 * commandKey - 命令名稱,默認值就是當前業務方法的方法名。 * threadPoolKey - 線程池命名,真實線程池命名的一部分。Hystrix在創建線程池並命名的時候,會提供完整命名。默認使用gourpKey命名 * 如果多個方法使用的threadPoolKey是同名的,則使用同一個線程池。 * threadPoolProperties - 為Hystrix創建的線程池做配置。可以使用字符串或HystrixPropertiesManager中的常量指定。 * 常用線程池配置: * coreSize - 核心線程數。最大並發數。1000*(99%平均響應時間 + 適當的延遲時間) * maxQueueSize - 阻塞隊列長度。如果是-1這是同步隊列。如果是正數這是LinkedBlockingQueue。如果線程池最大並發數不足, * 提供多少的阻塞等待。 * keepAliveTimeMinutes - 心跳時間,超時時長。單位是分鍾。 * queueSizeRejectionThreshold - 拒絕臨界值,當最大並發不足的時候,超過多少個阻塞請求,后續請求拒絕。 */ @HystrixCommand(groupKey="test-thread-quarantine", commandKey = "testThreadQuarantine", threadPoolKey="test-thread-quarantine", threadPoolProperties = { @HystrixProperty(name="coreSize", value="30"), @HystrixProperty(name="maxQueueSize", value="100"), @HystrixProperty(name="keepAliveTimeMinutes", value="2"), @HystrixProperty(name="queueSizeRejectionThreshold", value="15") }, fallbackMethod = "threadQuarantineFallback") public List<Map<String, Object>> testThreadQuarantine() { System.out.println("testQuarantine method thread name : " + Thread.currentThread().getName()); ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); StringBuilder sb = new StringBuilder(); sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/test"); System.out.println("request application service URL : " + sb.toString()); RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Map<String, Object>> result = response.getBody(); return result; } private List<Map<String, Object>> threadQuarantineFallback(){ System.out.println("threadQuarantineFallback method thread name : " + Thread.currentThread().getName()); List<Map<String, Object>> result = new ArrayList<>(); Map<String, Object> data = new HashMap<>(); data.put("id", -1); data.put("name", "thread quarantine fallback datas"); data.put("age", 0); result.add(data); return result; } }
關於線程池:
- 對於所有請求,都交由tomcat容器的線程池處理,是一個以http-nio開頭的的線程池;
- 開啟了線程池隔離后,tomcat容器默認的線程池會將請求轉交給threadPoolKey定義名稱的線程池,處理結束后,由定義的線程池進行返回,無需還回tomcat容器默認的線程池。線程池默認為當前方法名;
- 所有的fallback都單獨由Hystrix創建的一個線程池處理。
2.5.2 信號量隔離
所謂信號量隔離,就是設置一個並發處理的最大極值。當並發請求數超過極值時,通過fallback返回托底數據,保證服務完整性。
信號量隔離同樣通過@HystrixCommand注解配置,常用注解屬性有:
- commandProperty - 配置信號量隔離具體數據。屬性類型為HystrixProperty數組,常用配置內容如下:
- execution.isolation.strategy - 設置隔離方式,默認為線程池隔離。可選值只有THREAD和SEMAPHORE。
- execution.isolation.semaphore.maxConcurrentRequests - 最大信號量並發數,默認為10。
依賴注入和啟動器同線程池隔離,實現類如下:
@Service public class HystrixService { @Autowired private LoadBalancerClient loadBalancerClient; /** * 信號量隔離實現 * 不會使用Hystrix管理的線程池處理請求。使用容器(Tomcat)的線程處理請求邏輯。 * 不涉及線程切換,資源調度,上下文的轉換等,相對效率高。 * 信號量隔離也會啟動熔斷機制。如果請求並發數超標,則觸發熔斷,返回fallback數據。 * commandProperties - 命令配置,HystrixPropertiesManager中的常量或字符串來配置。 * execution.isolation.strategy - 隔離的種類,可選值只有THREAD(線程池隔離)和SEMAPHORE(信號量隔離)。 * 默認是THREAD線程池隔離。 * 設置信號量隔離后,線程池相關配置失效。 * execution.isolation.semaphore.maxConcurrentRequests - 信號量最大並發數。默認值是10。常見配置500~1000。 * 如果並發請求超過配置,其他請求進入fallback邏輯。 */ @HystrixCommand(fallbackMethod="semaphoreQuarantineFallback", commandProperties={ @HystrixProperty( name=HystrixPropertiesManager.EXECUTION_ISOLATION_STRATEGY, value="SEMAPHORE"), // 信號量隔離 @HystrixProperty( name=HystrixPropertiesManager.EXECUTION_ISOLATION_SEMAPHORE_MAX_CONCURRENT_REQUESTS, value="100") // 信號量最大並發數 }) public List<Map<String, Object>> testSemaphoreQuarantine() { System.out.println("testSemaphoreQuarantine method thread name : " + Thread.currentThread().getName()); ServiceInstance si = this.loadBalancerClient.choose("eureka-application-service"); StringBuilder sb = new StringBuilder(); sb.append("http://").append(si.getHost()) .append(":").append(si.getPort()).append("/test"); System.out.println("request application service URL : " + sb.toString()); RestTemplate rt = new RestTemplate(); ParameterizedTypeReference<List<Map<String, Object>>> type = new ParameterizedTypeReference<List<Map<String, Object>>>() { }; ResponseEntity<List<Map<String, Object>>> response = rt.exchange(sb.toString(), HttpMethod.GET, null, type); List<Map<String, Object>> result = response.getBody(); return result; } private List<Map<String, Object>> semaphoreQuarantineFallback(){ System.out.println("threadQuarantineFallback method thread name : " + Thread.currentThread().getName()); List<Map<String, Object>> result = new ArrayList<>(); Map<String, Object> data = new HashMap<>(); data.put("id", -1); data.put("name", "thread quarantine fallback datas"); data.put("age", 0); result.add(data); return result; } }
2.5.3線程池隔離和信號量隔離的對比
2.5.4線程池隔離和信號量隔離的選擇
- 線程池隔離:請求並發大,耗時較長(一般都是計算大,服務鏈長或訪問數據庫)時使用線程池隔離。可以盡可能保證外部容器(如Tomcat)線程池可用,不會因為服務調用的原因導致請求阻塞等待。
- 信號量隔離:請求並發大,耗時短(計算小,服務鏈段或訪問緩存)時使用信號量隔離。因為這類服務的響應快,不會占用外部容器(如Tomcat)線程池太長時間,減少線程的切換,可以避免不必要的開銷,提高服務處理效率。
3 Feign的雪崩處理
在聲明式遠程服務調用Feign中,實現服務災難性雪崩效應處理也是通過Hystrix實現的。而feign啟動器spring-cloud-starter-feign中是包含Hystrix相關依賴的。如果只使用服務降級、熔斷功能不需要做獨立依賴。如果需要使用Hystrix其他服務容錯能力,需要依賴spring-cloud-starter-hystrix資源。
<!-- hystrix依賴。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix</artifactId> </dependency>
從Dalston版本后,feign默認關閉Hystrix支持。所以必須在全局配置文件中開啟feign技術中的Hystrix支持。配置如下:
feign.hystrix.enabled=true
如果不使用Hystrix服務容錯功能,在application client端,服務接口只需要繼承服務標准api接口即可實現遠程服務調用。如果使用了Hystrix,則有不同的編寫方式。具體如下。
3.1 代碼實現 - 接口實現類方式
定義和服務標准api相同的application client服務接口。並通過@FeignClient注解來描述fallback方法所在類是什么。這個fallback方法所在類就是接口的實現類,實現的方法就是接口中定義方法的fallback方法。
/** * 如果在Feign中使用Hystrix,則不能直接繼承服務標准接口。 * 因為繼承接口,一般都不會給予實現。會缺少fallback方法。熔斷機制鏈條不完整。 * 在當前接口中,重復定義服務標准接口中定義的方法。 * 遠程服務調用的時候,是通過@FeignClient實現的。 * 如果遠程服務調用失敗,則觸發fallback注解屬性定義的接口實現類中的對應方法,作為fallback方法。 * * 在默認的Hystrix配置環境中,使用的是服務降級保護機制。 * * 服務降級,默認的情況下,包含了請求超時。 * feign聲明式遠程服務調用,在啟動的時候,初始化過程比較慢(通過注釋@FeignClient描述接口,接口生成動態代理對象,實現服務調用)。比ribbon要慢很多。 * 很容易在第一次訪問的時候,產生超時。導致返回fallback數據。 */ @FeignClient(name="test-feign-application-service", fallback=FirstClientFeignServiceImpl.class ) public interface FirstClientFeignService{ @RequestMapping(value="/testFeign", method=RequestMethod.GET) public List<String> testFeign(); @RequestMapping(value="/get", method=RequestMethod.GET) public FeignTestPOJO getById(@RequestParam(value="id") Long id); @RequestMapping(value="/get", method=RequestMethod.POST) public FeignTestPOJO getByIdWithPOST(@RequestBody Long id); @RequestMapping(value="/add", method=RequestMethod.GET) public FeignTestPOJO add(@RequestParam("id") Long id, @RequestParam("name") String name); @RequestMapping(value="/addWithGET", method=RequestMethod.GET) public FeignTestPOJO add(@RequestBody FeignTestPOJO pojo); @RequestMapping(value="/addWithPOST", method=RequestMethod.POST) public FeignTestPOJO addWithPOST(@RequestBody FeignTestPOJO pojo); }
為接口提供實現類,類中的方法實現就是fallback邏輯。實現類需要spring容器管理,使用@Component注解來描述類型。
/** * 實現類中的每個方法,都是對應的接口方法的fallback。 * 一定要提供spring相關注解(@Component/@Service/@Repository等)。 * 注解是為了讓當前類型的對象被spring容器管理。 * fallback是本地方法。 * 是接口的實現方法。 */ @Component public class FirstClientFeignServiceImpl implements FirstClientFeignService { @Override public List<String> testFeign() { List<String> result = new ArrayList<>(); result.add("this is testFeign method fallback datas"); return result; } @Override public FeignTestPOJO getById(Long id) { return new FeignTestPOJO(-1L, "this is getById method fallback datas"); } @Override public FeignTestPOJO getByIdWithPOST(Long id) { return new FeignTestPOJO(-1L, "this is getByIdWithPOST method fallback datas"); } @Override public FeignTestPOJO add(Long id, String name) { return new FeignTestPOJO(-1L, "this is add(id, name) method fallback datas"); } @Override public FeignTestPOJO add(FeignTestPOJO pojo) { return new FeignTestPOJO(-1L, "this is add(pojo) method fallback datas"); } @Override public FeignTestPOJO addWithPOST(FeignTestPOJO pojo) { return new FeignTestPOJO(-1L, "this is addWithPOST method fallback datas"); } }
3.2 相關配置
在Feign技術中,一般不使用請求合並,請求緩存等容錯機制。常用的機制是隔離,降級和熔斷。
3.2.1 Properties全局配置
# hystrix.command.default和hystrix.threadpool.default中的default為默認CommandKey,CommandKey默認值為服務方法名。
# 在properties配置中配置格式混亂,如果需要為每個方法設置不同的容錯規則,建議使用yml文件配置。 # Command Properties
# Execution相關的屬性的配置: # 隔離策略,默認是Thread, 可選Thread|Semaphore
hystrix.command.default.execution.isolation.strategy=THREAD
#命令執行超時時間,默認1000ms,只在線程池隔離中有效。
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=1000
# 執行是否啟用超時,默認啟用true,只在線程池隔離中有效。
hystrix.command.default.execution.timeout.enabled=true
# 發生超時是是否中斷,默認true,只在線程池隔離中有效。
hystrix.command.default.execution.isolation.thread.interruptOnTimeout=true
# 最大並發請求數,默認10,該參數當使用ExecutionIsolationStrategy.SEMAPHORE策略時才有效。如果達到最大並發請求數,請求會被拒絕。
# 理論上選擇semaphore的原則和選擇thread一致,但選用semaphore時每次執行的單元要比較小且執行速度快(ms級別),否則的話應該用thread。
# semaphore應該占整個容器(tomcat)的線程池的一小部分。
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests=10
# 如果並發數達到該設置值,請求會被拒絕和拋出異常並且fallback不會被調用。默認10。
# 只在信號量隔離策略中有效,建議設置大一些,這樣並發數達到execution最大請求數時,會直接調用fallback,而並發數達到fallback最大請求數時會被拒絕和拋出異常。
hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests=10
# ThreadPool 相關參數
# 並發執行的最大線程數,默認10
hystrix.threadpool.default.coreSize=10
# BlockingQueue的最大隊列數,當設為-1,會使用SynchronousQueue,值為正時使用LinkedBlcokingQueue。
# 該設置只會在初始化時有效,之后不能修改threadpool的queue size,除非reinitialising thread executor。默認-1。
hystrix.threadpool.default.maxQueueSize=-1
# 即使maxQueueSize沒有達到,達到queueSizeRejectionThreshold該值后,請求也會被拒絕。
hystrix.threadpool.default.queueSizeRejectionThreshold=20
# 線程存活時間,單位是分鍾。默認值為1。
hystrix.threadpool.default.keepAliveTimeMinutes=1
# Fallback相關的屬性
# 當執行失敗或者請求被拒絕,是否會嘗試調用fallback方法 。默認true
hystrix.command.default.fallback.enabled=true
# Circuit Breaker相關的屬性
# 是否開啟熔斷器。默認true
hystrix.command.default.circuitBreaker.enabled=true
# 一個rolling window內最小的請求數。如果設為20,那么當一個rolling window的時間內(比如說1個rolling window是10毫秒)收到19個請求
# 即使19個請求都失敗,也不會觸發circuit break。默認20
hystrix.command.default.circuitBreaker.requestVolumeThreshold=20
# 觸發短路的時間值,當該值設為5000時,則當觸發circuit break后的5000毫秒內都會拒絕遠程服務調用,也就是5000毫秒后才會重試遠程服務調用。默認5000
hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds=5000
# 錯誤比率閥值,如果錯誤率>=該值,circuit會被打開,並短路所有請求觸發fallback。默認50
hystrix.command.default.circuitBreaker.errorThresholdPercentage=50
# 強制打開熔斷器
hystrix.command.default.circuitBreaker.forceOpen=false
# 強制關閉熔斷器
hystrix.command.default.circuitBreaker.forceClosed=false
3.2.2 YML全局配置
YML配置文件,對SpringEL的支持更加優秀。可以通過SpringEL定制化的為每個服務調用配置Hystrix的容錯處理方案。對Hystrix的配置粒度相比較Properties的配置方案更加細致。
在YML中可配置的Hystrix信息,和Properties中配置的內容是一致。
如果需要對每個服務做定制化配置,建議使用yml配置文件。在語法和格式上更容易管理和維護。
spring: application: name: test-feign-application-client server: port: 9008 feign: hystrix: enabled: true hystrix: command: # default代表全部服務配置,如果為某個具體服務定制配置,使用:'服務接口名#方法名(參數類型列表)'的方式來定義。 # 如:'FirstClientFeignService#test(int)'。如果接口名稱在應用中唯一,可以只寫simpleName。 # 如果接口名稱在應用中不唯一,需要寫fullName(包名.類名) "FirstClientFeignService#testFeign()": fallback: enabled: true
3.3 代碼實現 - Factory實現方式
在服務接口的@FeignClient注解中,不再使用fallback屬性,而是定義fallbackFactory屬性。這個屬性的類型是Class類型的,用於配置fallback代碼所處的Factory。
再定義一個Java類,實現接口FallbackFactory,實現其中的create方法。使用匿名內部類的方式,為服務接口定義一個實現類,定義fallback方法實現。
本地接口定義:
@FeignClient(name="test-feign-application-service", fallbackFactory=FirstClientFeignServiceFallbackFactory.class ) public interface FirstClientFeignService{ @RequestMapping(value="/testFeign", method=RequestMethod.GET) public List<String> testFeign(); @RequestMapping(value="/get", method=RequestMethod.GET) public FeignTestPOJO getById(@RequestParam(value="id") Long id); @RequestMapping(value="/get", method=RequestMethod.POST) public FeignTestPOJO getByIdWithPOST(@RequestBody Long id); @RequestMapping(value="/add", method=RequestMethod.GET) public FeignTestPOJO add(@RequestParam("id") Long id, @RequestParam("name") String name); @RequestMapping(value="/addWithGET", method=RequestMethod.GET) public FeignTestPOJO add(@RequestBody FeignTestPOJO pojo); @RequestMapping(value="/addWithPOST", method=RequestMethod.POST) public FeignTestPOJO addWithPOST(@RequestBody FeignTestPOJO pojo); }
FallbackFactory實現類:
/** * 使用Factory方式實現Feign的Hystrix容錯處理。 * 編寫的自定義Factory必須實現接口FallbackFactory。 * FallbackFactory中的方法是 * 服務接口的類型 create(Throwable 遠程服務調用的錯誤) * * 工廠實現方案和服務接口實現類實現方案的區別: * 工廠可以提供自定義的異常信息處理邏輯。因為create方法負責傳遞遠程服務調用的異常對象。 * 實現類可以快速的開發,但是會丟失遠程服務調用的異常信息。 */ @Component public class FirstClientFeignServiceFallbackFactory implements FallbackFactory<FirstClientFeignService> { Logger logger = LoggerFactory.getLogger(FirstClientFeignServiceFallbackFactory.class); /** * create方法 - 就是工廠的生產產品的方法。 * 當前工廠生產的產品就是服務接口的Fallback處理對象。 就是服務接口的實現類的對象。 */ @Override public FirstClientFeignService create(final Throwable cause) { return new FirstClientFeignService() { @Override public List<String> testFeign() { logger.warn("testFeign() - ", cause); List<String> result = new ArrayList<>(); result.add("this is testFeign method fallback datas"); return result; } @Override public FeignTestPOJO getById(Long id) { return new FeignTestPOJO(-1L, "this is getById method fallback datas"); } @Override public FeignTestPOJO getByIdWithPOST(Long id) { return new FeignTestPOJO(-1L, "this is getByIdWithPOST method fallback datas"); } @Override public FeignTestPOJO add(Long id, String name) { return new FeignTestPOJO(-1L, "this is add(id, name) method fallback datas"); } @Override public FeignTestPOJO add(FeignTestPOJO pojo) { return new FeignTestPOJO(-1L, "this is add(pojo) method fallback datas"); } @Override public FeignTestPOJO addWithPOST(FeignTestPOJO pojo) { return new FeignTestPOJO(-1L, "this is addWithPOST method fallback datas"); } }; } }
這種實現邏輯的優勢是,可以獲取遠程調用服務的異常信息。為后期異常處理提供參考。
工廠實現方案和實現類的實現方案,沒有效率和邏輯上的優缺點對比。只是在遠程服務調用異常的處理上有區別。
4 Hystrix Dashboard - 數據監控
Hystrix dashboard是一款針對Hystrix進行實時監控的工具,通過Hystrix Dashboard我們可以在直觀地看到各Hystrix Command的請求響應時間, 請求成功率等數據。
4.1 實現單服務單節點數據監控
在使用了Hystrix技術的application client工程中增加下述依賴:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency>
在啟動器上增加注解@EnableHystrixDashboard、@EnableHystrix。
@EnableCircuitBreaker @EnableCaching @EnableEurekaClient @SpringBootApplication @EnableHystrixDashboard @EnableHystrix public class HystrixDashboardApplication { public static void main(String[] args) { SpringApplication.run(HystrixDashboardApplication.class, args); } }
啟動工程后,如果觸發了Hystrix,則可以通過http://ip:port/hystrix.stream得到監控數據。這種監控數據的獲取都是JSON數據。且數據量級較大。不易於查看。可以使用Hystrix Dashboard提供的視圖界面來觀察監控結果。視圖界面訪問路徑為http://ip:port/hystrix。視圖界面中各數據的含義如下:
建議:監控中心建議使用獨立工程來實現。這樣更便於維護。
4.2 使用Turbine實現多服務或集群的數據監控
Turbine是聚合服務器發送事件流數據的一個工具,hystrix的監控中,只能監控單個服務或單個節點,實際生產中都為多服務集群,因此可以通過turbine來監控多集群服務。
Turbine在Hystrix Dashboard中的作用如下:
4.2.1多服務監控
當使用Turbine來監控多服務狀態時,需提供一個獨立工程來搭建Turbine服務邏輯。並在工程中增加下述依賴:
<!-- Dashboard需要的依賴信息。 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-hystrix-dashboard</artifactId> </dependency> <!-- Turbine需要的依賴信息。 --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-turbine</artifactId> </dependency> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-netflix-turbine</artifactId> </dependency>
並在全局配置文件中增加下述配置:
#配置Eureka中的serviceId列表,標記監控哪些服務,多個服務名用逗號分隔,可以配置監控的服務,必須開啟了Hystrix Dashboard。
turbine.appConfig=hystrix-application-client,test-feign-application-client
#指定聚合哪些集群,多個使用","分割,default代表默認集群。集群就是服務名稱。需要配置clusterNameExpression使用。
turbine.aggregator.clusterConfig=default
# 1. clusterNameExpression指定集群名稱,默認表達式appName;此時:turbine.aggregator.clusterConfig需要配置想要監控的應用名稱; # 2. 當clusterNameExpression: default時,turbine.aggregator.clusterConfig可以不寫,因為默認就是default;代表所有集群都需要監控
turbine.clusterNameExpression="default"
在應用啟動類中,增加注解@EnableTurbine,代表開啟Turbine服務,提供多服務集群監控數據收集。
/** * @EnableTurbine - 開啟Turbine功能。 * 可以實現收集多個App client的Dashboard監控數據。 */ @SpringBootApplication @EnableTurbine public class HystrixTurbineApplication { public static void main(String[] args) { SpringApplication.run(HystrixTurbineApplication.class, args); } }
最后再Hystrix Dashboard視圖監控服務中,使用http://ip:port/turbine.stream作為監控數據來源,提供可視化監控界面。
注意:使用Turbine做多服務監控的時候,要求全局配置文件中配置的服務列表命名在Eureka注冊中心中可見。就是先啟動Application client再啟動Turbine。
4.2.2服務集群監控
在spring cloud中,服務名相同的多服務結點會自動形成集群,並提供服務。在Turbine中,監控服務集群不需要提供任何的特殊配置,因為turbine.appConfig已經配置了要監控的服務名稱。集群監控數據會自動收集。
在Hystrix Dashboard的可視化監控界面中,hosts信息會顯示出服務集群中的節點數量。如圖所示:
注意:使用Turbine做服務集群監控的時候,必須先啟動application client集群,再啟動Turbine。保證Turbine啟動的時候,可以在eureka注冊中心中發現要監控的服務集群。