javascript
SpringCloud Netflix Hystrix
文章目錄
- 一、 Hystrix簡介
- 1 什么是災難性雪崩效應
- 2 什么是Hystrix
- 二、 服務降級(Ribbon中)
- 三、 服務熔斷(Ribbon中)(服務降級的強化版)
- 四、 請求緩存(Ribbon中)(不推薦)(查詢頻率高,修改頻率低時謹慎使用)
- 五、 Openfeign的雪崩處理
- 1 服務降級
- 2 服務熔斷
- 六、 可視化的數據監控Hystrix-dashboard(近實時監控)
- 1 Ribbon中數據監控
- 2 openfeign 中數據監控
- 3 可視化監控
- 4 訪問Hystrix監控數據結果
一、 Hystrix簡介
1 什么是災難性雪崩效應
造成災難性雪崩效應的原因,可以簡單歸結為下述三種:
1.服務提供者不可用。如:硬件故障、程序BUG、緩存擊穿、并發請求量過大等。
2.重試加大流量。如:用戶重試、代碼重試邏輯等。
3.服務調用者不可用。如:同步請求阻塞造成的資源耗盡等。
雪崩效應最終的結果就是:
服務鏈條中的某一個服務不可用,導致一系列的服務不可用,最終造成服務邏輯崩潰。這種問題造成的后果,往往是無法預料的。
解決災難性雪崩效應的方式通常有:降級、熔斷和請求緩存。
2 什么是Hystrix
Hystrix是Netflix開源的一款容錯框架,具有自我保護能力。為了實現容錯和自我保護。
Hystrix設計目標:
1、 對來自依賴的延遲和故障進行防護和控制——這些依賴通常都是通過網絡訪問的
2、 阻止故障的連鎖反應
3、 快速失敗并迅速恢復
4、 回退并優雅降級
5、 提供近實時的監控與告警
Hystrix遵循的設計原則:
1、 防止任何單獨的依賴耗盡資源(線程)
2、 過載立即切斷并快速失敗,防止排隊
3、 盡可能提供回退以保護用戶免受故障
4、 使用隔離技術(例如隔板,泳道和斷路器模式)來限制任何一個依賴的影響
5、 通過近實時的指標,監控和告警,確保故障被及時發現
6、 通過動態修改配置屬性,確保故障及時恢復
7、 防止整個依賴客戶端執行失敗,而不僅僅是網絡通信
Hystrix如何實現這些設計目標?
1、 使用命令模式將所有對外部服務(或依賴關系)的調用包裝在HystrixCommand或HystrixObservableCommand對象中,并將該對象放在單獨的線程中執行;
2、 每個依賴都維護著一個線程池(或信號量),線程池被耗盡則拒絕請求(而不是讓請求排隊)。
3、 記錄請求成功,失敗,超時和線程拒絕。
4、 服務錯誤百分比超過了閾值,熔斷器開關自動打開,一段時間內停止對該服務的所有請求。
5、 請求失敗,被拒絕,超時或熔斷時執行降級邏輯。
6、 近實時地監控指標和配置的修改。
二、 服務降級(Ribbon中)
降級是指,當(請求超時、資源不足等情況)發生時進行服務降級處理,不調用真實服務邏輯,而是使用快速失敗(fallback)方式直接返回一個托底數據,保證服務鏈條的完整,避免服務雪崩。
解決服務雪崩效應,都是避免application client請求application service時,出現服務調用錯誤或網絡問題。處理手法都是在application client中實現。我們需要在application client相關工程中導入hystrix依賴信息。并在對應的啟動類上增加新的注解@EnableCircuitBreaker,這個注解是用于開啟hystrix熔斷器的,簡言之,就是讓代碼中的hystrix相關注解生效。
具體實現過程如下:
1 修改application service 中的service代碼
修改application service工程中的代碼,模擬超時錯誤。此模擬中,讓服務端代碼返回之前休眠2000毫秒,替代具體的復雜服務邏輯。
@RequestMapping("/user/save")@ResponseBodypublic Map<String, Object> save(User user){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("新增用戶數據: " + user);Map<String, Object> result = new HashMap<>();result.put("code", "200"); // 返回的狀態碼result.put("message", "新增用戶成功"); // 返回的處理結果消息。return result;} }2 application client POM依賴添加
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId></dependency>3 application client容錯處理代碼
/*** 遠程方法調用。訪問application service,訪問的地址是:http://localhost:8080/user/save* HystrixCommand注解 - Hystrix容災處理注解。當前方法做容災處理。* 屬性fallbackMethod - 如果遠程調用發生問題,調用本地的什么方法獲取降級結果(托底)。*/@Override@HystrixCommand(fallbackMethod = "saveFallback")public Map<String, Object> save(User user) {// 根據服務的名稱,獲取服務實例。服務名稱就是配置文件yml中的spring.application.name// 服務實例包括,這個名稱的所有服務地址和端口。ServiceInstance instance =this.loadBalancerClient.choose("ribbon-app-service");// 訪問地址拼接StringBuilder builder = new StringBuilder("");builder.append("http://").append(instance.getHost()).append(":").append(instance.getPort()).append("/user/save").append("?username=").append(user.getUsername()).append("&password=").append(user.getPassword()).append("&remark=").append(user.getRemark());System.out.println("本地訪問地址:" + builder.toString());// 創建一個Rest訪問客戶端模板對象。RestTemplate template = new RestTemplate();// 約束響應結果類型ParameterizedTypeReference<Map<String, Object>> responseType =new ParameterizedTypeReference<Map<String, Object>>() {};// 遠程訪問application service。ResponseEntity<Map<String, Object>> response =template.exchange(builder.toString(), HttpMethod.GET,null, responseType);Map<String, Object> result = response.getBody();return result;}private Map<String, Object> saveFallback(User user){// 同步->異步,講user封裝成message消息,發送到RabbitMQ中。System.out.println("saveFallback方法執行:" + user);Map<String, Object> result = new HashMap<>();result.put("message", "服務器忙,請稍后重試!");return result;}4 application client配置文件application.yml
server:port: 8081spring:application:name: ribbon-app-clienteureka:client:service-url:defaultZone: http://localhost:8761/eureka/ribbon-app-service: # 遠程訪問這個命名的服務ribbon: # 底層Ribbon配置NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 就是具體的負載均衡策略類型全名# listOfServers: localhost:8080 # 多個地址用逗號分隔。hystrix: # 配置Hystrix相關信息command: # 配置HystrixCommand相關內容default: # 所有范圍execution:timeout:enabled: true # 使用Hystrix作為超時判定機制isolation:thread:timeoutInMilliseconds: 1000 # 具體的超時時間5 application client啟動類
package com.bjsxt.userclient;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;/*** EnableCircuitBreaker注解除了開啟Hystrix容災處理機制(掃描HystrixCommand注解)外,* 還會開啟Hystrix相關的數據監控服務。*/ @SpringBootApplication @EnableCircuitBreaker public class RibbonAppClientApp {public static void main(String[] args) {SpringApplication.run(RibbonAppClientApp.class, args);} }三、 服務熔斷(Ribbon中)(服務降級的強化版)
當一定時間內,異常請求比例(請求超時、網絡故障、服務異常等)達到閥值時,啟動熔斷器,熔斷器一旦啟動,則會停止調用具體服務邏輯,通過fallback快速返回托底數據,保證服務鏈的完整。
熔斷有自動恢復機制,如:當熔斷器啟動后,每隔5秒,嘗試將新的請求發送給服務提供者,如果服務可正常執行并返回結果,則關閉熔斷器,服務恢復。如果仍舊調用失敗,則繼續返回托底數據,熔斷器持續開啟狀態。
具體實現過程如下:(-----在服務降級的基礎上只需要修改application client容錯處理代碼的注解-----)
application client容錯處理代碼
/*** 遠程方法調用。訪問application service,訪問的地址是:http://localhost:8080/user/save* HystrixCommand注解 - Hystrix容災處理注解。當前方法做容災處理。* 屬性fallbackMethod - 如果遠程調用發生問題,調用本地的什么方法獲取降級結果(托底)。* 屬性commandProperties - 定義Hystrix容災邏輯的具體參數。* CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD - 單位時間內,多少個請求發生錯誤(默認值10),開啟* 熔斷(斷路由)* CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE - 單位時間內,錯誤請求百分比,開啟* 熔斷(斷路由)* CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS - 斷路由開啟后,休眠多少毫秒(默認值5000),不訪問* 遠程服務。*/@Override@HystrixCommand(fallbackMethod = "saveFallback", commandProperties = {@HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_REQUEST_VOLUME_THRESHOLD,value="10"),@HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_ERROR_THRESHOLD_PERCENTAGE,value="50"),@HystrixProperty(name=HystrixPropertiesManager.CIRCUIT_BREAKER_SLEEP_WINDOW_IN_MILLISECONDS,value="3000")})public Map<String, Object> save(User user) {// 根據服務的名稱,獲取服務實例。服務名稱就是配置文件yml中的spring.application.name// 服務實例包括,這個名稱的所有服務地址和端口。ServiceInstance instance =this.loadBalancerClient.choose("ribbon-app-service");// 訪問地址拼接StringBuilder builder = new StringBuilder("");builder.append("http://").append(instance.getHost()).append(":").append(instance.getPort()).append("/user/save").append("?username=").append(user.getUsername()).append("&password=").append(user.getPassword()).append("&remark=").append(user.getRemark());System.out.println("本地訪問地址:" + builder.toString());// 創建一個Rest訪問客戶端模板對象。RestTemplate template = new RestTemplate();// 約束響應結果類型ParameterizedTypeReference<Map<String, Object>> responseType =new ParameterizedTypeReference<Map<String, Object>>() {};// 遠程訪問application service。ResponseEntity<Map<String, Object>> response =template.exchange(builder.toString(), HttpMethod.GET,null, responseType);Map<String, Object> result = response.getBody();return result;}private Map<String, Object> saveFallback(User user){// 同步->異步,講user封裝成message消息,發送到RabbitMQ中。System.out.println("saveFallback方法執行:" + user);Map<String, Object> result = new HashMap<>();result.put("message", "服務器忙,請稍后重試!");return result;}四、 請求緩存(Ribbon中)(不推薦)(查詢頻率高,修改頻率低時謹慎使用)
請求緩存:通常意義上說,就是將同樣的GET請求結果緩存起來,使用緩存機制(如redis、mongodb)提升請求響應效率。
使用請求緩存時,需要注意非冪等性操作對緩存數據的影響。
請求緩存是依托某一緩存服務來實現的。在案例中使用redis作為緩存服務器,那么可以使用spring-data-redis來實現redis的訪問操作。
1 修改application service代碼(增加get和post方法)
package com.bjsxt.userservice.controller;import com.bjsxt.entity.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody;import java.util.HashMap; import java.util.Map;/*** 提供服務的控制器*/ @Controller public class UserController {@GetMapping("/post")@ResponseBodypublic Map<String, Object> post(){System.out.println("post method run");Map<String, Object> result = new HashMap<>();result.put("message", "post方法執行成功");return result;}@GetMapping("/get")@ResponseBodypublic Map<String, Object> get(){System.out.println("get method run");Map<String, Object> result = new HashMap<>();result.put("message", "get方法執行成功");return result;}@RequestMapping("/user/save")@ResponseBodypublic Map<String, Object> save(User user){try {Thread.sleep(2000);} catch (InterruptedException e) {e.printStackTrace();}System.out.println("新增用戶數據: " + user);Map<String, Object> result = new HashMap<>();result.put("code", "200"); // 返回的狀態碼result.put("message", "新增用戶成功"); // 返回的處理結果消息。return result;} }2 application client POM依賴添加
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId></dependency>3 application client 容錯處理代碼
package com.bjsxt.userclient.service.impl;import com.bjsxt.entity.User; import com.bjsxt.userclient.service.UserService; import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand; import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty; import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager; import com.netflix.loadbalancer.RandomRule; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.cache.annotation.CacheConfig; import org.springframework.cache.annotation.CacheEvict; import org.springframework.cache.annotation.Cacheable; import org.springframework.cloud.client.ServiceInstance; import org.springframework.cloud.client.loadbalancer.LoadBalancerClient; import org.springframework.core.ParameterizedTypeReference; import org.springframework.http.HttpMethod; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Service; import org.springframework.web.client.RestTemplate;import java.util.HashMap; import java.util.Map;/*** cacheNames - 可以實現緩存分組,是Hystrix做分組。* * @CacheConfig(cacheNames = "test_cache_names")* 在類上,增加@CacheConfig注解,用來描述當前類型可能使用cache緩存。* 如果使用緩存,則緩存數據的key的前綴是cacheNames。* cacheNames是用來定義一個緩存集的前綴命名的,相當于分組。*/ @Service @CacheConfig(cacheNames = "test_cache_names") public class UserServiceImpl implements UserService {@Autowiredprivate LoadBalancerClient loadBalancerClient;/*** 查詢,只要路徑和參數不變,理論上查詢結果不變。可以緩存,提升查詢效率。* @return*/@Cacheable("springcloud_cache")public Map<String, Object> get(){ServiceInstance instance = this.loadBalancerClient.choose("ribbon-app-service");StringBuilder builder = new StringBuilder("");builder.append("http://").append(instance.getHost()).append(":").append(instance.getPort()).append("/get");System.out.println(builder.toString());RestTemplate template = new RestTemplate();ParameterizedTypeReference<Map<String, Object>> type =new ParameterizedTypeReference<Map<String, Object>>() {};ResponseEntity<Map<String, Object>> responseEntity =template.exchange(builder.toString(), HttpMethod.GET, null, type);return responseEntity.getBody();}/*** 寫操作,數據寫操作執行后,緩存數據失效,應該清理緩存。* @return*/@CacheEvict("springcloud_cache")public Map<String, Object> post(){ServiceInstance instance = this.loadBalancerClient.choose("ribbon-app-service");StringBuilder builder = new StringBuilder("");builder.append("http://").append(instance.getHost()).append(":").append(instance.getPort()).append("/post");System.out.println(builder.toString());RestTemplate template = new RestTemplate();ParameterizedTypeReference<Map<String, Object>> type =new ParameterizedTypeReference<Map<String, Object>>() {};ResponseEntity<Map<String, Object>> responseEntity =template.exchange(builder.toString(), HttpMethod.GET, null, type);return responseEntity.getBody();} }4 application client 配置文件application.yml
server:port: 8082spring:application:name: ribbon-app-clientredis: # spring-data-redis配置信息host: 192.168.14.129 # 訪問的redis地址, 默認localhostport: 6379 # redis的端口,默認6379database: 0 # 訪問的redis的數據庫編號, 默認0eureka:client:service-url:defaultZone:- http://localhost:8761/eureka/ribbon-app-service: # 遠程訪問這個命名的服務ribbon: # 底層Ribbon配置NFLoadBalancerRuleClassName: com.netflix.loadbalancer.RandomRule # 就是具體的負載均衡策略類型全名# listOfServers: localhost:8080 # 多個地址用逗號分隔。hystrix: # 配置Hystrix相關信息command: # 配置HystrixCommand相關內容default: # 所有范圍execution:timeout:enabled: true # 使用Hystrix作為超時判定機制isolation:thread:timeoutInMilliseconds: 1000 # 具體的超時時間5 application client 啟動類(加@EnableCaching注解)
package com.bjsxt.userclient;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCaching; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;/*** EnableCircuitBreaker注解除了開啟Hystrix容災處理機制(掃描HystrixCommand注解)外,* 還會開啟Hystrix相關的數據監控服務。*/ @SpringBootApplication @EnableCircuitBreaker @EnableCaching public class RibbonAppClientApp {public static void main(String[] args) {SpringApplication.run(RibbonAppClientApp.class, args);} }五、 Openfeign的雪崩處理
在聲明式遠程服務調用Openfeign中,實現服務災難性雪崩效應處理也是通過Hystrix實現的。而feign啟動器spring-cloud-starter-openfeign中是包含Hystrix相關依賴的。如果只使用服務降級功能不需要做獨立依賴。如果需要使用Hystrix其他服務容錯能力,需要依賴spring-cloud-starter-netflix-hystrix資源。從Dalston版本后,feign默認關閉Hystrix支持。所以必須在全局配置文件中開啟feign技術中的Hystrix支持。具體實現如下:
1 服務降級
1.1 POM依賴
Openfeign的啟動器依賴spring-cloud-starter-openfeign中,自帶Hystrix處理相關依賴,如果只使用服務降級功能不需要做獨立依賴,通過配置即可開啟容錯處理機制。
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-openfeign</artifactId> </dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>1.2 application client 服務接口
package com.bjsxt.feign.client.service;import com.bjsxt.feign.api.UserServiceAPI; import com.bjsxt.feign.client.service.impl.UserServiceImpl; import org.springframework.cloud.openfeign.FeignClient;/*** 本地服務,是用于遠程訪問Application Service的本地服務接口。*/ @FeignClient(value="feign-app-service", fallback = UserServiceImpl.class) public interface UserService extends UserServiceAPI { }1.3 application client 服務接口實現
package com.bjsxt.feign.client.service.impl;import com.bjsxt.feign.client.service.UserService; import com.bjsxt.feign.entity.User; import com.netflix.hystrix.contrib.javanica.conf.HystrixPropertiesManager; import org.springframework.stereotype.Service;import java.util.HashMap; import java.util.Map;/*** 本地接口的實現類* 實現類中的每個方法,都是其對應的遠程調用服務的降級方法。*/ @Service public class UserServiceImpl implements UserService {@Overridepublic Map<String, Object> saveUser(User user) {Map<String, Object> result = new HashMap<>();result.put("message", "saveUser方法容錯處理。");return result;}@Overridepublic Map<String, Object> updateUser(User user) {Map<String, Object> result = new HashMap<>();result.put("message", "updateUser方法容錯處理。");return result;} }1.4 application client 配置文件application.yml 添加
超時管理默認ribbon管理,使用hystrix后由hystrix管理(所以還需配置超時管理)
feign:hystrix:enabled: truehystrix:command:default:execution:timeout:enable: trueisolation:thread:timeoutInMilliseconds: 10001.5 applicaiton client 啟動類(不需要改)
package com.bjsxt.feign.client;import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker; import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard; import org.springframework.cloud.openfeign.EnableFeignClients;/*** 需要提供一個新的注解* @EnableFeignClients - 開啟OpenFeign客戶端技術。掃描@FeignClient注解。* 默認掃描當前類所在包,及子包中所有的類型。**/ @SpringBootApplication @EnableCircuitBreaker @EnableFeignClients(basePackages = {"com.bjsxt.feign.client.service"}) public class FeignClientApp {public static void main(String[] args) {SpringApplication.run(FeignClientApp.class, args);} }2 服務熔斷
服務降級的升級版,只需要在服務降級的基礎上修改配置文件即可
hystrix:command:default:execution:timeout:enable: trueisolation:thread:timeoutInMilliseconds: 1000fallback:enable: true # 是否開啟服務降級處理。默認開啟。circuitBreaker:enable: true # 是否開啟熔斷機制。 默認開啟。requestVolumeThreshold: 3 # 單位時間內,錯誤多少次請求,開啟熔斷sleepWindowInMilliseconds: 3000 # 開啟熔斷口,多少毫秒不訪問遠程服務errorThresholdPercentage: 50 # 單位時間內,錯誤率達到多少,開啟熔斷forceOpen: false # 是否強制開啟熔斷,永遠不訪問遠程服務。默認falseforceClosed: false # 是否強制關閉熔斷,永遠訪問遠程服務。默認false六、 可視化的數據監控Hystrix-dashboard(近實時監控)
Hystrix Dashboard是一款針對Hystrix進行實時監控的工具,通過Hystrix Dashboard我們可以在直觀地看到各Hystrix Command的請求響應時間, 請求成功率等數據。
1 Ribbon中數據監控
1.1 POM依賴
Hystrix Dashboard是針對Hystrix容錯處理相關數據的監控工具。只要在使用了Hystrix技術的應用工程中導入對應依賴即可。
注意:
如果在Openfeign技術中開啟Hystrix Dashboard監控,則需要將spring-cloud-starter-netflix-hystrix啟動器導入POM文件,否則無法在啟動類上使用@EnableCircuitBreaker注解。
啟動類上必須使用@EnableCircuitBreaker注解
1.2 配置文件application.yml
management:endpoints:web:exposure:include:- info- health- hystrix.stream1.3 啟動類
(localhost:8080/actuator查看(localhost:8080/actuator/hystrix.stream))
2 openfeign 中數據監控
2.1 POM依賴
<dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency> <!-- cloud中監控數據是使用boot提供的actuator實現的。 --> <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId> </dependency> <dependency><groupId>org.springframework.cloud</groupId><artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId> </dependency>2.2 application.yml 配置
management:endpoints:web:exposure:include:- info- health- hystrix.stream2.3 啟動類加 @EnableCircuitBreaker注解
(localhost:8080/actuator查看(localhost:8080/actuator/hystrix.stream))
3 可視化監控
3.1 啟動類加 @EnableHystrixDashboard注解
(localhost:8081/hystrix 查看)
4 訪問Hystrix監控數據結果
通過瀏覽器訪問提供監控訪問路徑的應用,具體地址為:
http://ip:port/actuator/hystrix.stream
得到的監控結果如下:
這種監控數據的獲取都是JSON數據。且數據量級較大。不易于查看。可以使用Hystrix Dashboard提供的視圖界面來觀察監控結果。視圖界面訪問路徑為: http://ip:port/hystrix。
進入后的監控視圖界面具體含義如下:
總結
以上是生活随笔為你收集整理的SpringCloud Netflix Hystrix的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: GWY是什么意思 GWY具体是什么意思
- 下一篇: Feign接口转换