(1)需求背景html
它是系統負載太高,突發流量或者網絡等各類異常狀況介紹,經常使用的解決方案。git
在一個分佈式系統裏,一個服務依賴多個服務,可能存在某個服務調用失敗,好比超時、異常等,如何可以保證在一個依賴出問題的狀況下,不會致使總體服務失敗。github
好比:某微服務業務邏輯複雜,在高負載狀況下出現超時狀況。redis
內部條件:程序bug致使死循環、存在慢查詢、程序邏輯不對致使耗盡內存spring
外部條件:黑客攻擊、促銷、第三方系統響應緩慢。api
(2)解決思路springboot
解決接口級故障的核心思想是優先保障核心業務和優先保障絕大部分用戶。好比登陸功能很重要,當訪問量太高時,停掉註冊功能,爲登陸騰出資源。服務器
(3)解決策略網絡
熔斷,降級,限流,排隊。app
通常是某個服務故障或者是異常引發的,相似現實世界中的‘保險絲’,當某個異常條件被觸發,直接熔斷整個服務,而不是一直等到此服務超時,爲了防止防止整個系統的故障,
而採用了一些保護措施。過載保護。好比A服務的X功能依賴B服務的某個接口,當B服務接口響應很慢時,A服務X功能的響應也會被拖慢,進一步致使了A服務的線程都卡在了X功能
上,A服務的其它功能也會卡主或拖慢。此時就須要熔斷機制,即A服務不在請求B這個接口,而能夠直接進行降級處理。
服務器當壓力劇增的時候,根據當前業務狀況及流量,對一些服務和頁面進行有策略的降級。以此緩解服務器資源的的壓力,以保證核心業務的正常運行,同時也保持了客戶和
大部分客戶的獲得正確的相應。
自動降級:超時、失敗次數、故障、限流
(1)配置好超時時間(異步機制探測回覆狀況);
(2)不穩的的api調用次數達到必定數量進行降級(異步機制探測回覆狀況);
(3)調用的遠程服務出現故障(dns、http服務錯誤狀態碼、網絡故障、Rpc服務異常),直接進行降級。
人工降級:秒殺、雙十一大促降級非重要的服務。
相同點:
1)從可用性和可靠性觸發,爲了防止系統崩潰
2)最終讓用戶體驗到的是某些功能暫時不能用
不一樣點:
1)服務熔斷通常是下游服務故障致使的,而服務降級通常是從總體系統負荷考慮,由調用方控制
2)觸發緣由不一樣,上面顏色字體已解釋
Hystrix提供了以下的幾個關鍵參數,來對一個熔斷器進行配置:
circuitBreaker.requestVolumeThreshold //滑動窗口的大小,默認爲20 circuitBreaker.sleepWindowInMilliseconds //過多長時間,熔斷器再次檢測是否開啓,默認爲5000,即5s鍾 circuitBreaker.errorThresholdPercentage //錯誤率,默認50%
3個參數放在一塊兒,所表達的意思就是:
每當20個請求中,有50%失敗時,熔斷器就會打開,此時再調用此服務,將會直接返回失敗,再也不調遠程服務。直到5s鍾以後,從新檢測該觸發條件,判斷是否把熔斷器關閉,或者繼續打開。
這裏面有個很關鍵點,達到熔斷以後,那麼後面它就直接不去調該微服務。那麼既然不去調該微服務或者調的時候出現異常,出現這種狀況首先不可能直接把錯誤信息傳給用戶,因此針對熔斷
咱們能夠考慮採起降級策略。所謂降級,就是當某個服務熔斷以後,服務器將再也不被調用,此時客戶端能夠本身準備一個本地的fallback回調,返回一個缺省值。
這樣作,雖然服務水平降低,但好歹可用,比直接掛掉要強,固然這也要看適合的業務場景。
使用到的組件包括:Eureka、Feign包括如下三個項目:
(1)Eureka-server: 7001 註冊中心
(2)product-server :8001 商品微服務
(3)order-server : 9001 訂單微服務
註冊中心、商品微服務、在以前博客都已搭建,這裏就不重複寫。這裏只寫order-server微服務。具體能夠看上篇博客:SpringCloud(5)---Feign服務調用
<!--hystrix依賴,主要是用 @HystrixCommand --> <dependency> <groupId>org.springframework.cloud</groupId> <artifactId>spring-cloud-starter-netflix-hystrix</artifactId> </dependency>
server: port: 9001 #指定註冊中心地址 eureka: client: serviceUrl: defaultZone: http://localhost:7001/eureka/ #服務的名稱 spring: application: name: order-service #開啓feign支持hystrix (注意,必定要開啓,舊版本默認支持,新版本默認關閉) # #修改調用超時時間(默認是1秒就算超時) feign: hystrix: enabled: true client: config: default: connectTimeout: 2000 readTimeout: 2000
@SpringBootApplication @EnableFeignClients //添加熔斷降級註解 @EnableCircuitBreaker public class OrderApplication { public static void main(String[] args) { SpringApplication.run(OrderApplication.class, args); } }
/** * 商品服務客戶端 * name = "product-service"是你調用服務端名稱 * fallback = ProductClientFallback.class,後面是你自定義的降級處理類,降級類必定要實現ProductClient */ @FeignClient(name = "product-service",fallback = ProductClientFallback.class) public interface ProductClient { //這樣組合就至關於http://product-service/api/v1/product/find @GetMapping("/api/v1/product/find") String findById(@RequestParam(value = "id") int id); }
/** * 針對商品服務,錯降級處理 */ @Component public class ProductClientFallback implements ProductClient { @Override public String findById(int id) { System.out.println("ProductClientFallback中的降級方法"); //這對gai該接口進行一些邏輯降級處理........ return null; } }
注意:fallbackMethod = "saveOrderFail"中的saveOrderFail方法中的參數類型,個數,順序要和save如出一轍,不然會報找不到saveOrderFail方法。
@RestController @RequestMapping("api/v1/order") public class OrderController { @Autowired private ProductOrderService productOrderService; @RequestMapping("save") //當調用微服務出現異常會降級到saveOrderFail方法中 @HystrixCommand(fallbackMethod = "saveOrderFail") public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId){ return productOrderService.save(userId, productId); } //注意,方法簽名必定要要和api方法一致 private Object saveOrderFail(int userId, int productId){ System.out.println("controller中的降級方法"); Map<String, Object> msg = new HashMap<>(); msg.put("code", -1); msg.put("msg", "搶購人數太多,您被擠出來了,稍等重試"); return msg; } }
(1)正常狀況
先將訂單服務(order)和商品服務(product)同時啓動,如圖:
訂單服務調用商品服務正常
(2)異常狀況
此刻我將商品微服務停掉:只啓動訂單微服務,這時去調用商品服務固然會出現超時異常狀況。
在調接口,發現已經成功到降級方法裏
在看controller中的降級方法和ProductClientFallback降級方法的實現前後順序,它們的順序是不固定的,有可能controller中降級方法先執行,也可能ProductClientFallback降級方法先執行。
具體要看哪一個線程先得到cpu執行權。
主要是完善完善服務熔斷處理,報警機制完善結合redis進行模擬短信通知用戶下單失敗。
<!--springboot整合redis--> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency>
#服務的名稱 #redis
spring:
application:
name: order-service
redis:
database: 0
host: 127.0.0.1
port: 6379
timeout: 2000
主要看降級方法的不一樣
@RestController @RequestMapping("api/v1/order") public class OrderController { @Autowired private ProductOrderService productOrderService; //添加bean @Autowired private StringRedisTemplate redisTemplate; @RequestMapping("save") //當調用微服務出現異常會降級到saveOrderFail方法中 @HystrixCommand(fallbackMethod = "saveOrderFail") public Object save(@RequestParam("user_id")int userId, @RequestParam("product_id") int productId,HttpServletRequest request){ return productOrderService.save(userId, productId); } //注意,方法簽名必定要要和api方法一致 private Object saveOrderFail(int userId, int productId, HttpServletRequest request){ //監控報警 String saveOrderKye = "save-order"; //有數據表明20秒內已經發過 String sendValue = redisTemplate.opsForValue().get(saveOrderKye); final String ip = request.getRemoteAddr(); //新啓動一個線程進行業務邏輯處理 new Thread( ()->{ if (StringUtils.isBlank(sendValue)) { System.out.println("緊急短信,用戶下單失敗,請離開查找緣由,ip地址是="+ip); //發送一個http請求,調用短信服務 TODO redisTemplate.opsForValue().set(saveOrderKye, "save-order-fail", 20, TimeUnit.SECONDS); }else{ System.out.println("已經發送太短信,20秒內不重複發送"); } }).start(); Map<String, Object> msg = new HashMap<>(); msg.put("code", -1); msg.put("msg", "搶購人數太多,您被擠出來了,稍等重試"); return msg; } }
當20秒內連續發請求會提醒已發短信。
官方文檔:https://github.com/Netflix/Hystrix/wiki/Configuration#execution.isolation.strategy
我只是偶爾安靜下來,對過去的種種思忖一番。那些曾經的舊時光裏即使有過天真愚鈍,也不值得譴責。畢竟,日後的日子,還很長。不斷鼓勵本身,
天一亮,又是嶄新的起點,又是未知的征程(上校8)