天天看點

Spring Cloud-在Ribbon中使用斷路器監控Hystrix斷路器概念:斷路器示意圖:

在微服務架構中,根據業務來拆分成一個個的服務,服務與服務之間可以互相調用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign來調用。為了保證其高可用,單個服務通常會叢集部署。由于網絡原因或者自身的原因,服務并不能保證100%可用,如果單個服務出現問題,調用這個服務就會出現線程阻塞,此時若有大量的請求湧入,Servlet容器的線程資源會被消耗完畢,導緻服務癱瘓。服務與服務之間的依賴性,故障會傳播,會對整個微服務系統造成災難性的嚴重後果,這就是服務故障的“雪崩”效應。

針對上述問題,在Spring Cloud Hystrix中實作了線程隔離、斷路器等一系列的服務保護功能。它也是基于Netflix的開源架構 Hystrix實作的,該架構目标在于通過控制那些通路遠端系統、服務和第三方庫的節點,進而對延遲和故障提供更強大的容錯能力。Hystrix具備了服務降級、服務熔斷、線程隔離、請求緩存、請求合并以及服務監控等強大功能。

斷路器概念:

       斷路器模式源于Martin Fowler的Circuit Breaker一文。“斷路器”本身是一種開關裝置,用于在電路上保護線路過載,當線路中有電器發生短路時,“斷路器”能夠及時的切斷故障電路,防止發生過載、發熱、甚至起火等嚴重後果。

       在分布式架構中,斷路器模式的作用也是類似的,當某個服務單元發生故障(類似用電器發生短路)之後,通過斷路器的故障監控(類似熔斷保險絲),向調用方傳回一個錯誤響應,而不是長時間的等待。這樣就不會使得線程因調用故障服務被長時間占用不釋放,避免了故障在分布式系統中的蔓延。

斷路器示意圖:

SpringCloud Netflix實作了斷路器庫的名字叫Hystrix. 在微服務架構下,通常會有多個層次的服務調用. 下面是微服架構下, 浏覽器端通過API通路背景微服務的一個示意圖:

Spring Cloud-在Ribbon中使用斷路器監控Hystrix斷路器概念:斷路器示意圖:

一個微服務的逾時失敗可能導緻瀑布式連鎖反映,下圖中,Hystrix通過自主回報實作的斷路器, 防止了這種情況發生。

Spring Cloud-在Ribbon中使用斷路器監控Hystrix斷路器概念:斷路器示意圖:

圖中的服務B因為某些原因失敗,變得不可用,所有對服務B的調用都會逾時。當對B的調用失敗達到一個特定的閥值(5秒之内發生20次失敗是Hystrix定義的預設值), 鍊路就會被處于open狀态, 之後所有所有對服務B的調用都不會被執行, 取而代之的是由斷路器提供的一個表示鍊路open的Fallback消息. Hystrix提供了相應機制,可以讓開發者定義這個Fallbak消息.

open的鍊路阻斷了瀑布式錯誤, 可以讓被淹沒或者錯誤的服務有時間進行修複。這個fallback可以是另外一個Hystrix保護的調用, 靜态資料,或者合法的空值. Fallbacks可以組成鍊式結構,是以,最底層調用其它業務服務的第一個Fallback傳回靜态資料.

下面我們來簡單搭建下服務叢集部署+斷路器:

1.之前使用的spring-cloud-eureka-service注冊中心:

2.spring-cloud-eureka-provider-a,spring-cloud-eureka-provider-b,spring-cloud-eureka-provider-c這三個服務Demo上篇我們已經改造為同一個服務名、端口不同的服務,用于部署服務叢集:

3.在 Ribbon中使用斷路器:

建立項目spring-cloud-ribbon-consumer-hystrix,可以從之前的spring-cloud-ribbon-consumer複制過來,加入hystrix依賴,完整pom如下,

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>

	<groupId>com.example</groupId>
	<artifactId>spring-cloud-ribbon-consumer-hystrix</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<packaging>jar</packaging>

	<name>spring-cloud-ribbon-consumer-hystrix</name>
	<description>Demo project for Spring Boot</description>

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.0.4.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>

	<properties>
		<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
		<project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
		<java.version>1.8</java.version>
		<spring-cloud.version>Finchley.SR1</spring-cloud.version>
	</properties>

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.cloud</groupId>
			<artifactId>
				spring-cloud-starter-netflix-eureka-client
			</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
		</dependency>
		
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
	</dependencies>

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.cloud</groupId>
				<artifactId>spring-cloud-dependencies</artifactId>
				<version>${spring-cloud.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
			<dependency>
				<groupId>io.pivotal.spring.cloud</groupId>
				<artifactId>
					spring-cloud-services-dependencies
				</artifactId>
				<version>2.0.1.RELEASE</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<build>
		<plugins>
			<plugin>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-maven-plugin</artifactId>
			</plugin>
		</plugins>
	</build>


</project>
           

4.在啟動類添加@EnableHystrix注解,用來開啟Hystrix斷路器監控:

package com.example.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;

/**
 * 
 * @ClassName SpringCloudRibbonConsumerHystrixApplication
 * @Description 在 Ribbon中使用斷路器hystrix
 * @Author fangxc
 * @Date 2018年9月12日 下午5:19:58
 */
@EnableHystrix
@EnableDiscoveryClient
@SpringBootApplication
public class SpringCloudRibbonConsumerHystrixApplication {
	
	@LoadBalanced
    @Bean
    RestTemplate restTemplate() {
        return new RestTemplate();
    }

	public static void main(String[] args) {
		SpringApplication.run(SpringCloudRibbonConsumerHystrixApplication.class, args);
	}
}
           

5.修改ConsumerController.java,其中@HystrixCommand屬性較多,其中

  • fallbackMethod 降級方法
  • commandProperties 普通配置屬性,可以配置HystrixCommand對應屬性,例如采用線程池還是信号量隔離、熔斷器熔斷規則等等
  • ignoreExceptions 忽略的異常,預設HystrixBadRequestException不計入失敗
  • groupKey() 組名稱,預設使用類名稱
  • commandKey 指令名稱,預設使用方法名
package com.example.demo.controller;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;

@RestController
public class ConsumerController {
	
	@Autowired
	private  RestTemplate restTemplate;

	/**
	 * HystrixCommand:該注解對該方法建立了熔斷器的功能,并指定了defaultStores熔斷方法,熔斷方法直接傳回了一個字元串
	 * 				    該方法為hystrix包裹,可以對依賴服務進行隔離、降級、快速失敗、快速重試等等
	 * @return
	 */
	@HystrixCommand(defaultFallback = "defaultStores")
	@RequestMapping("/hello")
	public String consumerHello() {
		// 位址為服務提供方的服務名+對應服務下請求路徑
		return restTemplate.getForEntity("http://eureka-provider/", String.class).getBody();
	}
	
	public String defaultStores() {
        return "Ribbon + hystrix ,提供者服務挂了";
    }
}
           

6.依次啟動spring-cloud-eureka-service,spring-cloud-eureka-provider-a,spring-cloud-eureka-provider-b,spring-cloud-eureka-provider-c,spring-cloud-ribbon-consumer-hystrix:

然後通路注冊中心 ,正常顯示如下:

Spring Cloud-在Ribbon中使用斷路器監控Hystrix斷路器概念:斷路器示意圖:

說明a、b、c,ribbon-consumer-hystrix服務都注冊成功了。

停止 spring-cloud-eureka-provider-a 提供者,端口為:10001服務,浏覽器通路 http://localhost:20002/hello ,會出現下面三種傳回,說明斷路器已經生效,提示:Ribbon + hystrix ,提供者服務挂了

Spring Cloud-在Ribbon中使用斷路器監控Hystrix斷路器概念:斷路器示意圖:
Spring Cloud-在Ribbon中使用斷路器監控Hystrix斷路器概念:斷路器示意圖:
Spring Cloud-在Ribbon中使用斷路器監控Hystrix斷路器概念:斷路器示意圖:

至此在Ribbon中使用斷路器監控Hystrix簡單Demo完成。

繼續閱讀