天天看點

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

目錄

  • `Hystrix` 簡介
  • `Hystrix` 服務熔斷産生的背景
    • 引起雪崩效應常見場景
  • 初探 `Hystrix`
    • `Hystrix` 設計目标,作用
    • `Hystrix` 如何實作這些設計目标
  • `Hystrix` 入門
    • 項目概覽
    • `Hystrix` 的使用(不整合 `Feign`)
    • `Hystrix-study-user` 服務的主啟動類
    • `Hystrix-study-user` 服務的 `Feign` 的用戶端接口
    • `Hystrix-study-user` 服務的服務降級類
    • `Hystrix-study-user` 服務的 `Service` 實作類
    • `Hystrix-study-user` 服務的配置檔案
    • 服務熔斷測試
  • `@HystrixCommand` 注解的簡單使用
    • `Hystrix-study-user` 服務的 `Service` 實作類添加方法
    • `Hystrix-study-activity` 服務的 `Controller` 類
    • 服務降級測試
    • 修改 `Hystrix-study-activity` 服務的 `Controller` 類再測試
  • `@HystrixCommand` 注解的常見使用
  • `Hystrix` 與 `Ribbon` 的逾時時間
  • `Hystrix` 的斷路器工作原理
    • 斷路器開啟的條件
    • 斷路器關閉的條件
  • 服務降級與服務熔斷差別
    • 服務熔斷
    • 服務降級
    • 差別
      • 相同點
      • 不同點

Hystrix

簡介

Hystrix

Netlifx

開源的一款容錯架構,防雪崩利器,具備服務降級,服務熔斷,依賴隔離,監控(

Hystrix Dashboard

)等功能

官網:https://github.com/Netflix/Hystrix/wiki/How-To-Use

Hystrix

服務熔斷産生的背景

分布式系統環境下,服務間類似依賴非常常見,一個業務調用通常依賴多個基礎服務。如下圖,對于同步調用,當庫存服務不可用時,商品服務請求線程被阻塞,當有大批量請求調用庫存服務時,最終可能導緻整個商品服務資源耗盡,無法繼續對外提供服務。并且這種不可用可能沿請求調用鍊向上傳遞,這種現象被稱為雪崩效應

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

引起雪崩效應常見場景

  • 硬體故障:如伺服器當機,機房斷電,光纖被挖斷等
  • 流量激增:如異常流量,重試加大流量等
  • 緩存擊穿:一般發生在應用重新開機,所有緩存失效時,以及短時間内大量緩存失效時。大量的緩存不命中,使請求直擊後端服務,造成服務提供者超負荷運作,引起服務不可用
  • 程式

    BUG

    :如程式邏輯導緻記憶體洩漏,

    JVM

    長時間

    FullGC

  • 同步等待:服務間采用同步調用模式,同步等待造成的資源耗盡

初探

Hystrix

Hystrix

設計目标,作用

  • 服務的降級
  • 服務的熔斷
  • 服務的限流
  • 提供近實時的監控與告警

Hystrix

如何實作這些設計目标

  • 使用指令模式将所有對外部服務(或依賴關系)的調用包裝在

    HystrixCommand

    HystrixObservableCommand

    對象中,并将該對象放在單獨的線程中執行
  • 每個依賴都維護着一個線程池(或信号量),線程池被耗盡則拒絕請求(而不是讓請求排隊)
  • 記錄請求成功,失敗,逾時和線程拒絕
  • 服務錯誤百分比超過了門檻值,熔斷器開關自動打開,一段時間内停止對該服務的所有請求
  • 請求失敗,被拒絕,逾時或熔斷時執行降級邏輯
  • 近實時地監控名額和配置的修改

Hystrix

入門

項目概覽

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

Hystrix

的使用(不整合

Feign

)

在使用

Feign

元件作為接口調用遠端服務時,是不需要添加

Hystrix

的依賴的。因為

Feign

預設已經內建了

Hystrix

Ribbon

。如果單獨使用

Hystrix

元件時,可以導入以下依賴

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
           

Hystrix-study-user

服務的主啟動類

@SpringBootApplication
@EnableEurekaClient
@EnableFeignClients
@EnableCircuitBreaker // 開啟斷路器
@EnableHystrixDashboard // 開啟 Hystrix 的監控儀表盤
public class UserApplication {

	private static final Logger log = LoggerFactory.getLogger(UserApplication.class);

	public static void main(String[] args) {
		SpringApplication.run(UserApplication.class, args);
		log.info("===============springcloud user啟動了=================");
	}

	// 解決 hystrix-dashBoard 儀表盤不能通路
	@SuppressWarnings({ "rawtypes", "unchecked" })
	@Bean
	public ServletRegistrationBean getServlet() {
		HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
		ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
		registrationBean.setLoadOnStartup(1);
		registrationBean.addUrlMappings("/hystrix.stream");
		registrationBean.setName("HystrixMetricsStreamServlet");
		return registrationBean;
	}
}
           

Hystrix-study-user

服務的

Feign

的用戶端接口

@FeignClient(name = "STUDY-ACTIVITY", fallback = UserFeignFallback.class)
public interface UserFeign {

	@RequestMapping(path = { "/activity/getCoupon" }, method = RequestMethod.POST)
	String getCoupon(@RequestBody Integer id);

	@RequestMapping(path = { "/activity/getCouponTimeOut" }, method = RequestMethod.POST)
	String getCouponTimeOut(@RequestBody Integer id);

	@RequestMapping(path = { "/demo/timeOut" }, method = RequestMethod.POST)
	String timeOut(@RequestParam Integer mills);

	@RequestMapping(path = { "/timeOut" }, method = RequestMethod.POST)
	String tripTest(@RequestParam Integer mills);
}
           

Hystrix-study-user

服務的服務降級類

@Component
public class UserFeignFallback implements UserFeign {

	@Override
	public String getCoupon(Integer id) {
		return null;
	}

	@Override
	public String getCouponTimeOut(Integer id) {
		return "------超過2000毫秒時,直接進入服務降級處理------";
	}

	@Override
	public String timeOut(Integer mills) {
		return "---------------您的請求【逾時】或【失敗】,已進入服務降級模式了----------------";
	}

	@Override
	public String tripTest(Integer mills) {
		 return "-------------請求未通過-----------";
	}
}
           

Hystrix-study-user

服務的

Service

實作類

@Service
public class UserServiceImpl implements UserService {

	private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

	@Autowired
	private UserFeign userFeign;

	// 使用注解 @HystrixCommand 指定服務熔斷的回退方法
	@HystrixCommand(fallbackMethod = "firstLoginError")
	@Override
	public String firstLogin(Integer id) {
		// 采用 Feign 用戶端來調用服務 Hystrix-study-activity
		String result = userFeign.getCoupon(id);
		log.info("===================result的值為:" + result + "======================");
		return result;
	}

	// 當服務 Hystrix-study-activity 不可用時,這時讓其回調回退方法
	public String firstLoginError(Integer id) {
		return "---------您請求的服務暫時不可用,請稍後再試--------------";
	}
}	
           

Hystrix-study-user

服務的配置檔案

server.port=8080

spring.application.name=study-user

eureka.client.service-url.defaultZone=http://eureka7001.com:8761/eureka/
#事實上,springcloud預設已為Feign整合了Hystrix,要想為Feign打開Hystrix支援,隻需要設定feign.hystrix.enabled=true即可。
feign.hystrix.enabled=true
#補充:在springcloud Dalston之前的版本中,Feign預設開啟Hystrix支援,無需設定feign.hystrix.enabled=true.
#從springcloud Dalston版本開始,Feign的Hystrix支援預設關閉,需要手動設定開啟

#配置Hystrix的逾時時間
#default全局有效,service id指定應用有效
hystrix.command.default.execution.timeout.enabled=true #預設為true
#預設值為 1000毫秒(對于每一個标有注解 @FeignClient 的接口的所有抽象方法生效)
#hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds=9000

#顯示伺服器詳細的健康資訊
management.endpoint.health.show-details=always

#暴露全部的監控資訊(解決hystrix-dashBoard儀表盤不能通路)
management.endpoint.web.exposure.include="*"
           

服務熔斷測試

分别啟動

Hystrix-study-activity,Hystrix-study-eureka,Hystrix-study-user

三個服務,使用

Postman

測試如下

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

此時再關閉

Hystrix-study-activity

服務

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

說明執行了

@HystrixCommand(fallbackMethod = "firstLoginError")

服務熔斷的回退方法

@HystrixCommand

注解的簡單使用

依然使用上面的入門項目,隻不過不需要引入

Hystrix

的依賴了,直接使用

Feign

用戶端進行調用。在使用

Feign

元件作為接口調用遠端服務時,是不需要添加

Hystrix

的依賴的。因為

Feign

預設已經內建了

Hystrix

Ribbon

。如果單獨使用

Hystrix

元件時,可以導入以下依賴

Hystrix-study-user

服務的

Service

實作類添加方法

execution.isolation.thread.timeoutInMilliseconds

:該屬性用來配置方法執行的逾時時間。我們在之前對于降級處理時間的配置,都是在全局配置檔案

application.yml

中配置的,

commandProperties

可以讓我們在一些具有獨特要求的方法上,單獨進行一些配置操作

// 設定Hystrix的服務降級逾時時間,超過2000毫秒時,直接進入服務降級處理	
@HystrixCommand(commandProperties = {
		@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "2000") })
@Override
public String firstLoginTimeOut(Integer id) {
	String result = userFeign.getCouponTimeOut(id);
	log.info("===================result的值為:" + result + "======================");
	return result;
}
           

Hystrix-study-activity

服務的

Controller

@Controller
@RequestMapping(path = { "/activity" })
public class ActivityController {

	private static final Logger log = LoggerFactory.getLogger(ActivityController.class);

	@RequestMapping(path = { "/getCouponTimeOut" }, method = RequestMethod.POST)
	@ResponseBody
	public String getCouponTimeOut(@RequestBody Integer id) {
		try {
			Random random = new Random();
			TimeUnit.SECONDS.sleep(random.nextInt(10) % (7) + 4);
		} catch (InterruptedException e) {
			e.printStackTrace();
		}
		log.info("=============該使用者首次登陸(注冊),領取優惠券失敗============");
		return "error";
	}
}
           

服務降級測試

分别啟動

Hystrix-study-activity,Hystrix-study-eureka,Hystrix-study-user

三個服務,使用

Postman

測試如下

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

說明執行了

UserFeignFallback

服務降級處理類的相應方法

修改

Hystrix-study-activity

服務的

Controller

類再測試

@Controller
@RequestMapping(path = { "/activity" })
public class ActivityController {

	private static final Logger log = LoggerFactory.getLogger(ActivityController.class);

	@RequestMapping(path = { "/getCouponTimeOut" }, method = RequestMethod.POST)
	@ResponseBody
	public String getCouponTimeOut(@RequestBody Integer id) {
		log.info("=============該使用者首次登陸(注冊),領取優惠券成功============");
		return "SUCCESS";
	}
}
           

再測試結果

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

@HystrixCommand

注解的常見使用

@Service
public class UserServiceImpl implements UserService {

    private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

    @Autowired
    private UserFeign userFeign;
   
    @HystrixCommand(threadPoolKey = "time", threadPoolProperties = {
            @HystrixProperty(name = "coreSize", value = "2"),
            @HystrixProperty(name = "maxQueueSize", value = "20")}, commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "9000")})
    @Override
    public String timeOut(Integer mills) {
        log.info("-----------mills:的值為:" + mills + "--------------");
        return userFeign.timeOut(mills);
    }

    @HystrixCommand(threadPoolKey = "time_1", threadPoolProperties = {
    		@HystrixProperty(name = "coreSize", value = "2"),
            @HystrixProperty(name = "maxQueueSize", value = "20")}, commandProperties = {
            @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "9000")})
    @Override
    public String timeOut_1(Integer mills) {
        log.info("-----------mills:的值為:" + mills + "--------------");
        return userFeign.timeOut(mills);
    }

  /**
    * Hystrix的斷路器測試
    * 
    * 模拟測試:3秒鐘内,請求次數達到2次,并且失敗率在50%以上,斷路器做跳閘動作。跳閘後的活動視窗設定為3秒
    *
    * 服務的健康狀态檢查:http://ip:port/actuator/health
    * 	Hystrix的健康狀态為:status: "UP"
    * 	Hystrix的斷路器跳閘後狀态為:status: "CIRCUIT_OPEN"
    * 	此時要删除服務的降級處理類 UserFeignFallback,才能檢視健康狀态
     */
    @HystrixCommand(commandProperties = {
            @HystrixProperty(name = "metrics.rollingPercentile.timeInMilliseconds", value = "3000"),
            @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "2"),
            @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "50"),
            @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "3000")})
    @Override
    public String tripTest(Integer mills) {
        return userFeign.tripTest(mills);
    }
}
           

鑒于篇幅,文章不再贅述,詳情可以檢視:https://www.cnblogs.com/zhenbianshu/p/9630167.html

Hystrix

Ribbon

的逾時時間

  • 如果

    hystrix.command.default.execution.timeout.enabled = true(預設)

    ,則會有兩個執行方法逾時的配置:一個就是

    ribbon

    ReadTimeout

    ,一個就是熔斷器

    hystrix

    timeoutInMilliseconds

    ,此時誰的值小誰生效
  • 如果

    hystrix.command.default.execution.timeout.enabled = false

    ,則熔斷器不進行逾時熔斷,而是根據

    ribbon

    ReadTimeout

    抛出的異常而熔斷,也就是取決于

    ribbon

  • ribbon

    ConnectTimeout

    配置的是請求服務的逾時時間,除非服務找不到,或者網絡原因,這個時間才會生效
  • ribbon

    還有

    MaxAutoRetries

    對目前執行個體的重試次數,

    MaxAutoRetriesNextServer

    對切換執行個體的重試次數,如果

    ribbon

    ReadTimeout

    逾時,或者

    ConnectTimeout

    連接配接逾時,會進行重試操作
  • 由于

    ribbon

    的重試機制,通常熔斷的逾時時間需要配置的比

    ReadTimeout

    長,

    ReadTimeout

    ConnectTimeout

    長,否則還未重試就熔斷了
  • 為了確定重試機制的正常運作,理論上(以實際情況為準)建議

    hystrix

    的逾時時間為:

    (1 + MaxAutoRetries + MaxAutoRetriesNextServer) * ReadTimeout

Hystrix

的斷路器工作原理

Hystrix解讀Hystrix 簡介Hystrix 服務熔斷産生的背景初探 HystrixHystrix 入門@HystrixCommand 注解的簡單使用@HystrixCommand 注解的常見使用Hystrix 與 Ribbon 的逾時時間Hystrix 的斷路器工作原理服務降級與服務熔斷差別

斷路器開啟的條件

  • 當請求達到閥值時候:預設

    10

    秒内

    20

    次請求,當請求失敗率達到預設

    50%

    的時,此時斷路器将會開啟,所有的請求都不會執行

斷路器關閉的條件

  • 當斷路器開啟

    5

    秒(預設)時,這時斷路器是半開狀态, 會允許其中一個請求執行
  • 如果執行成功,則斷路器會關閉;如果失敗,則繼續開啟。循環重複這兩個流程

服務降級與服務熔斷差別

服務熔斷

熔斷機制是應對雪崩效應的一種微服務鍊路保護機制,一般來說,每個服務都需要熔斷機制的

  • 如高壓電路中,如果某個地方的電壓過高,熔斷器就會熔斷,對電路進行保護。在微服務架構中,熔斷機制也是起着類似的作用
  • 當扇對外連結路的某個微服務不可用,或響應逾時,或當機,或異常時,進而熔斷該節點微服務的調用,快速傳回錯誤的響應資訊。當檢測到該節點微服務調用響應正常後,恢複調用鍊路

使用注解

@HystrixCommand(fallbackMethod = " ")

來指定服務熔斷的回退方法

@Service
public class UserServiceImpl implements UserService {

	private static final Logger log = LoggerFactory.getLogger(UserServiceImpl.class);

	@Autowired
	private UserFeign userFeign;

	// 使用注解 @HystrixCommand 指定服務熔斷的回退方法
	@HystrixCommand(fallbackMethod = "firstLoginError")
	@Override
	public String firstLogin(Integer id) {
		// 采用 Feign 用戶端來調用服務 Hystrix-study-activity
		String result = userFeign.getCoupon(id);
		log.info("===================result的值為:" + result + "======================");
		return result;
	}

	// 當服務 Hystrix-study-activity 不可用時,這時讓其回調回退方法
	public String firstLoginError(Integer id) {
		return "---------您請求的服務暫時不可用,請稍後再試--------------";
	}
}	
           

服務降級

  • 降級是指自己的待遇下降了,從

    RPC

    調用環節來講,就是說整體資源快不夠了,忍痛将某些服務單元先關掉,關閉後還要傳回一些可處理的備選方法,待渡過難關,再開啟回來
  • 降級一般需要對業務有層級之分(比如降級一般是從最外圍服務開始)
@FeignClient(name = "STUDY-ACTIVITY", fallback = UserFeignFallback.class)
public interface UserFeign {

	@RequestMapping(path = { "/activity/getCouponTimeOut" }, method = RequestMethod.POST)
	String getCouponTimeOut(@RequestBody Integer id);

	@RequestMapping(path = { "/demo/timeOut" }, method = RequestMethod.POST)
	String timeOut(@RequestParam Integer mills);
}
           
@Component
public class UserFeignFallback implements UserFeign {

	@Override
	public String getCouponTimeOut(Integer id) {
		return "------超過2000毫秒時,直接進入服務降級處理------";
	}

	@Override
	public String timeOut(Integer mills) {
		return "---------------您的請求【逾時】或【失敗】,已進入服務降級模式了----------------";
	}
}
           

差別

相同點

  • 目的很一緻,都是從服務的可用性可靠性着想,為防止系統的整體緩慢甚至崩潰,采用的技術手段
  • 最終表現類似,對于兩者來說,最終讓使用者體驗到的是某些功能暫時不可達或不可用

不同點

  • 觸發原因不太一樣:服務熔斷一般是某個服務(下遊服務)故障或異常引起,而服務降級一般是從系統整體負荷考慮的
  • 管理層次不太一樣:熔斷其實是一個架構級的處理,每個服務都需要(無層級之分),而降級一般需要對業務有層級之分(比如降級一般是從最外圍服務開始)

繼續閱讀