Hystrix介紹
在微服務場景中,通常會有很多層的服務調用。如果一個底層服務出現問題,故障會被向上傳播給用戶。我們需要一種機制,當底層服務不可用時,可以阻斷故障的傳播。這就是斷路器的作用。他是系統服務穩定性的最后一重保障。
在springcloud中斷路器組件就是Hystrix。Hystrix也是Netflix套件的一部分。他的功能是,當對某個服務的調用在一定的時間內(默認10s),有超過一定次數(默認20次)并且失敗率超過一定值(默認50%),該服務的斷路器會打開。返回一個由開發者設定的fallback。
fallback可以是另一個由Hystrix保護的服務調用,也可以是固定的值。fallback也可以設計成鏈式調用,先執行某些邏輯,再返回fallback。
Hystrix作用:
能夠實現 服務的降級,服務的熔斷,接近實時的監控
官網資料?https://github.com/Netflix/Hystrix/wiki/How-To-Use
Hystrix官宣,停更進維?https://github.com/Netflix/Hystrix
Hystrix重要概念
服務降級
? ? ? ? 服務器忙,請稍候再試,不讓客戶端等待并立刻返回一個友好提示,fallback
哪些情況會觸發降級
? ? ? ? 程序運行異常
? ? ? ? 超時
? ? ? ? 服務熔斷觸發服務降級
? ? ? ? 線程池/信號量打滿也會導致服務降級
服務熔斷
? ? ? ? 類比保險絲達到最大服務訪問后,直接拒絕訪問,拉閘限電,然后調用服務降級的方法并返回友好提示
就是保險絲?服務的降級->進而熔斷->恢復調用鏈路
服務限流??
? ? ? ? 秒殺高并發等操作,嚴禁一窩蜂的過來擁擠,大家排隊,一秒鐘N個,有序進行
hystrix案例
? 在上一節整合openfeign的基礎上整合hystrix 主要用到了這個三個服務
? 模擬故障
? 服務提供者有一個耗時的服務,消費者調用一次訪問特別慢,轉圈但是最終可以訪問
?但是不影響這個接口
?當開啟多線程測試,訪問量為2000去訪問
再次訪問
也開始轉圈了
導致原因
? ? ? 8001同一層次的其他接口服務被困死,因為tomcat線程里面的工作線程已經被擠占完畢
? ? ? 80此時調用8001,客戶端訪問響應緩慢,轉圈圈
上訴結論
? ? ? ?正因為有上述故障或不佳表現,才有我們的降級/容錯/限流等技術誕生
? ? ? ?如何解決?解決的要求
超時導致服務器變慢(轉圈)?超時不再等待
? ? ? ?出錯(宕機或程序運行出錯)?出錯要有兜底
解決?
? ? ? ? ?對方服務(8001)超時了,調用者(80)不能一直卡死等待,必須有服務降級
? ? ? ? ?對方服務(8001)down機了,調用者(80)不能一直卡死等待,必須有服務降級
? ? ? ? ?對方服務(8001)OK,調用者(80)自己出故障或有自我要求(自己的等待時間小于服務提供者),自己處理降級
服務降級
? ? ?pom文件加入依賴
<!--整合熔斷器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
? ? ? ?降低配置??@HystrixCommand
? ? ? ?8001先從自身找問題? ?設置自身調用超時時間的峰值,峰值內可以正常運行,超過了需要有兜底的方法處理,作服務降級fallback8001fallback
業務類啟用
package com.shiwen.controller;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.shiwen.entity.CommonResult;
import com.shiwen.entity.Payment;
import com.shiwen.service.PaymentService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;import java.util.concurrent.TimeUnit;/*** 支付表(Payment)表控制層** @author wangjie* @since 2020-09-27 17:19:14*/@RestController
@RequestMapping("payment")
public class PaymentController {/*** 服務對象*/@Autowiredprivate PaymentService paymentService;@Value("${}")private String serverPort;/*** 通過主鍵查詢單條數據** @param id 主鍵* @return 單條數據*/@ApiOperation(value = "根據id查詢數據")@GetMapping("select-one/{id}")public CommonResult selectOne(@PathVariable Long id) {Payment payment = (id);return new CommonResult(200,"成功"+serverPort,payment);}/*** 增加*/@ApiOperation(value = "添加數據")@PostMapping("add")public CommonResult addPayment(@RequestBody Payment payment){boolean insert = (payment);if(insert==true){return new CommonResult(200,"插入成功人不輸"+serverPort,true);}else{return new CommonResult(500,"插入失敗"+serverPort,false);}}/*** 編寫超時程序*/@GetMapping("timeout")@HystrixCommand(fallbackMethod = "timeOutMethod",commandProperties = {@HystrixProperty(name = "",value = "3000") //三秒以內正常})public CommonResult timeOutMethods(){try {(5);} catch (InterruptedException e) {();}return new CommonResult(200,"超時服務",serverPort);}//兜底的方法public CommonResult timeOutMethod(){return new CommonResult(200,"系統占時繁忙請稍后重試",serverPort);}}
啟動類添加@EnableHystrix
一旦調用服務方法失敗并拋出了錯誤信息后,會自動調用@HystrixCommand標注好的fallbackMethod調用類中的指定方法timeOutMethod圖示
?這樣寫一個方法一個兜底的方法代碼太過臃腫,通過注解@DefaultProperties(defaultFallback = "globalTimeOutMethod")配置全局的兜底方法
@RestController
@RequestMapping("/order")
@DefaultProperties(defaultFallback = "globalTimeOutMethod")
public class FeignController {@Autowiredprivate FeignService feignService;@GetMapping("/selete/{id}")public CommonResult getUserById(@PathVariable Long id){return feignService.selectOne(id);}@GetMapping("/timeout")//@HystrixCommand(defaultFallback = "orderTimeOutMethod",commandProperties = {@HystrixProperty(name = "",value = "1500")})@HystrixCommandpublic CommonResult getTimeOut(){return feignService.timeOutMethods();}public CommonResult orderTimeOutMethod(){return new CommonResult(200,"消費端兜底的方法","===================");}public CommonResult globalTimeOutMethod(){return new CommonResult(200,"消費端全局的兜底方法","=====================");}
}
還有一個問題是兜底的方法和業務代碼混合在一起 以下的降級實在80消費端完成的
第一步創建
FeignServiceImpl類實現FeignService接口 編寫每一個方法對應的兜底內容
@Component
public class FeignServiceImpl implements FeignService {@Overridepublic CommonResult selectOne(Long id) {return null;}@Overridepublic CommonResult timeOutMethods() {return new CommonResult(200,"我是一接口的形式實現兜底方法的","====================");}
}
第二步?FeignService 接口指名兜底的類fallback =
@FeignClient(value = "CLOUD-PAYMENT-SERVICE",fallback = )
public interface FeignService {@GetMapping("payment/select-one/{id}")CommonResult selectOne(@PathVariable("id") Long id);@GetMapping("payment/timeout")CommonResult timeOutMethods();
第三步controller調用
package com.shiwen.controller;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.shiwen.entity.CommonResult;
import com.shiwen.service.FeignService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;/*** @author wangjie* @Title: FeignController* @Description: openfeign服務的調用* @company: 西安石文軟件有限公司* @date 2020/10/1411:13*/
@RestController
@RequestMapping("/order")
//@DefaultProperties(defaultFallback = "globalTimeOutMethod")
public class FeignController {@Autowiredprivate FeignService feignService;@GetMapping("/selete/{id}")public CommonResult getUserById(@PathVariable Long id){return feignService.selectOne(id);}@GetMapping("/timeout")//@HystrixCommand(defaultFallback = "orderTimeOutMethod",commandProperties = {@HystrixProperty(name = "",value = "1500")})//@HystrixCommandpublic CommonResult getTimeOut(){return feignService.timeOutMethods();}public CommonResult orderTimeOutMethod(){return new CommonResult(200,"消費端兜底的方法","===================");}public CommonResult globalTimeOutMethod(){return new CommonResult(200,"消費端全局的兜底方法","=====================");}
}
yml編寫 開啟hystrix
feign:
hystrix:
enabled: true
測試依次啟動注冊中心,服務提供者,消費者
訪問http://localhost/order/timeout 當程序訪問超時就會走實現類了兜底
?服務熔斷
說白了就是程序不正常了,再這一段時間訪問并不會立馬崩掉,而是訪問失敗的次數大于設定的比率,就會跳閘,對服務實現降級,之后走的是兜底方法,當程序再次恢復正常時,并不會里面返回正確的數據。還是走的是兜底的方法,同樣需要在一段時間訪問成功的次數大于設定的比率,會再次跳閘。程序恢復正常(就是檢測到改節點微服務調用響應正常后,恢復調用鏈路)
服務熔斷需要配置的參數
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback",commandProperties = {
@HystrixProperty(name = "",value = "true"), //是否開啟斷路器
@HystrixProperty(name = "",value = "10"), //請求次數
@HystrixProperty(name = "",value = "10000"), //時間范圍
@HystrixProperty(name = "",value = "60"), //失敗率達到多少后跳閘
})
package com.shiwen.controller;import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.shiwen.entity.CommonResult;
import com.shiwen.entity.Payment;
import com.shiwen.service.PaymentService;
import io.swagger.annotations.ApiOperation;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.web.bind.annotation.*;import java.util.concurrent.TimeUnit;/*** 支付表(Payment)表控制層** @author wangjie* @since 2020-09-27 17:19:14*/@RestController
@RequestMapping("payment")
public class PaymentController {/*** 服務對象*/@Autowiredprivate PaymentService paymentService;@Value("${}")private String serverPort;/*** 通過主鍵查詢單條數據** @param id 主鍵* @return 單條數據*/@ApiOperation(value = "根據id查詢數據")@GetMapping("select-one/{id}")@HystrixCommand(fallbackMethod = "timeOutMethodAll",commandProperties = {@HystrixProperty(name = "",value = "true"), //是否開啟斷路器@HystrixProperty(name = "",value = "10"), //請求次數@HystrixProperty(name = "",value = "10000"), //時間范圍@HystrixProperty(name = "",value = "60"), //失敗率達到多少后跳閘})public CommonResult selectOne(@PathVariable Long id) {if(id<){int i=10/0;return new CommonResult(200,"成功"+serverPort,"程序出錯了");}else {Payment payment = (id);return new CommonResult(200,"成功"+serverPort,payment);}}/*** 增加*/@ApiOperation(value = "添加數據")@PostMapping("add")public CommonResult addPayment(@RequestBody Payment payment){boolean insert = (payment);if(insert==true){return new CommonResult(200,"插入成功人不輸"+serverPort,true);}else{return new CommonResult(500,"插入失敗"+serverPort,false);}}/*** 編寫超時程序*/@GetMapping("timeout")public CommonResult timeOutMethods(){try {(6);} catch (InterruptedException e) {();}return new CommonResult(200,"超時服務",serverPort);}//兜底的方法public CommonResult timeOutMethodAll(Long id){return new CommonResult(200,"系統占時繁忙請稍后重試",serverPort);}}