在微服務架構中,根據業務來拆分成一個個的服務,服務與服務之間可以互相調用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign來調用。為了保證其高可用,單個服務通常會叢集部署。由于網絡原因或者自身的原因,服務并不能保證100%可用,如果單個服務出現問題,調用這個服務就會出現線程阻塞,此時若有大量的請求湧入,Servlet容器的線程資源會被消耗完畢,導緻服務癱瘓。服務與服務之間的依賴性,故障會傳播,會對整個微服務系統造成災難性的嚴重後果,這就是服務故障的“雪崩”效應。
為了解決這個問題,業界提出了斷路器模型。
斷路器介紹
Netflix開源了Hystrix元件,實作了斷路器模式,SpringCloud對這一元件進行了整合。 在微服務架構中,一個請求需要調用多個服務是非常常見的,如下圖:
較底層的服務如果出現故障,會導緻連鎖故障。當對特定的服務的調用的不可用達到一個閥值(Hystric 是5秒20次) 斷路器将會被打開。
斷路打開後,可用避免連鎖故障,fallback方法可以直接傳回一個固定值。
在Ribbon中使用斷路器
改造之前的Ahut-Service-Ribbon項目
pom.xml檔案中添加hystrix的依賴
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
完整pom.xml
<?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.ahut</groupId>
<artifactId>service-ribbon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-ribbon</name>
<description>service ribbon</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.12.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>Edgware.SR3</spring-cloud.version>
</properties>
<dependencies>
<!-- web -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- eureka discovery -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<!-- 添加hystrix依賴 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<!-- ribbon -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
項目啟動類添加@EnableHystrix注解開啟Hystrix
package com.ahut;
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.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author cheng
* @className: ServiceRibbonApplication
* @description: service ribbon
* @dateTime
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
public class ServiceRibbonApplication
public static void main(String[] args) {
SpringApplication.run(ServiceRibbonApplication.class, args);
}
/**
* @description: 注入restTemplate, 并且開啟負載均衡
* @author cheng
* @dateTime
@Bean
@LoadBalanced
public RestTemplate createRestTemplate() {
return new
改造HelloServiceImpl類,在showPort方法上加上@HystrixCommand注解。該注解對該方法建立了熔斷器的功能,并指定了fallbackMethod熔斷方法,熔斷方法直接傳回了一個字元串,代碼如下:
package com.ahut.serviceImpl;
import com.ahut.service.HelloService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* @author cheng
* @className: HelloServiceImpl
* @description:
* @dateTime
@Service
public class HelloServiceImpl implements HelloService
@Autowired
private RestTemplate restTemplate;
/**
* @description: 展示伺服器端口
* @author cheng
* @dateTime
@HystrixCommand(fallbackMethod = "handleError")
@Override
public String showPort() {
return restTemplate.getForObject("http://AHUT-EUREKA-CLIENT/v1/showPort", String.class);
}
/**
* @description: 斷路器方法
* @author cheng
* @dateTime
public String handleError() {
return "I am so sorry,Server has error";
}
}
啟動項目:
- Ahut-Eureka-Server(注冊中心)
- Ahut-Eureka-Client-One(AHUT-EUREKA-CLIENT服務一)
- Ahut-Service-Ribbon(AHUT-SERVICE-RIBBON服務消費者)
通路eureka:
通路Ahut-Service-Ribbon http://localhost:8940/v1/hello
此時,關閉Ahut-Eureka-Client-One服務,再次通路Ahut-Service-Ribbon http://localhost:8940/v1/hello
這就說明當 Ahut-Eureka-Client-One 工程不可用的時候,Ahut-Service-Ribbon調用 Ahut-Eureka-Client-One的API接口時,會執行快速失敗,直接傳回一組字元串,而不是等待響應逾時,這很好的控制了容器的線程阻塞。
此時的項目架構
在Feign中使用斷路器
Feign是自帶斷路器的,在D版本的Spring Cloud中,它沒有預設打開。需要在配置檔案中配置打開它。
改造之前的Ahut-Service-Feign項目
在application.properties配置檔案中打開hystrix配置:
# 配置啟動斷路器
feign.hystrix.enabled=true
在FeignClient的HelloService接口的注解中加上fallback的指定類就行了:
package com.ahut.service;
import com.ahut.hystrix.HelloServiceHystrix;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author cheng
* @interfaceName: HelloService
* @description:
* @dateTime
@FeignClient(value = "AHUT-EUREKA-CLIENT", fallback = HelloServiceHystrix.class)
public interface HelloService
@RequestMapping(value = "/v1/showPort")
String showPort();
/**
* @description: 示範帶參數
* @author cheng
* @dateTime
//@GetMapping(value = "/demo")
//void demo(@RequestParam(value = "name") String name);
HelloServiceHystrix需要實作HelloService 接口,并注入到Ioc容器中,代碼如下:
package com.ahut.hystrix;
import com.ahut.service.HelloService;
import org.springframework.stereotype.Component;
/**
* @author cheng
* @className: HelloServiceHystrix
* @description: 斷路器
* @dateTime
@Component
public class HelloServiceHystrix implements HelloService
/**
* @description: 斷路器方法
* @author cheng
* @dateTime
@Override
public String showPort() {
return "I am so sorry,Server has error";
}
}
啟動項目:
- Ahut-Eureka-Server(注冊中心)
- Ahut-Eureka-Client-One(AHUT-EUREKA-CLIENT服務一)
- Ahut-Service-Feign(AHUT-SERVICE-FEIGN服務消費者)
通路eureka
通路Ahut-Service-Feign項目 http://localhost:8930/v1/hello:
此時關閉Ahut-Eureka-Client-One項目,再次通路Ahut-Service-Feign項目 http://localhost:8930/v1/hello:
斷路器起作用
此時的項目架構
Hystrix Dashboard (斷路器:Hystrix 儀表盤)
改造Ahut-Service-Ribbon項目,Ahut-Service-Feign項目的改造和這個一樣
添加依賴:
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
完整的pom.xml
<?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.ahut</groupId>
<artifactId>service-ribbon</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>
<name>service-ribbon</name>
<description>service ribbon</description>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>1.5.12.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>Edgware.SR3</spring-cloud.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</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>
</dependencies>
</dependencyManagement>
<build>
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
</project>
在主程式啟動類中加入@EnableHystrixDashboard注解,開啟hystrixDashboard:
package com.ahut;
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.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* @author cheng
* @className: ServiceRibbonApplication
* @description: service ribbon
* @dateTime
@SpringBootApplication
@EnableDiscoveryClient
@EnableHystrix
@EnableHystrixDashboard
public class ServiceRibbonApplication
public static void main(String[] args) {
SpringApplication.run(ServiceRibbonApplication.class, args);
}
/**
* @description: 注入restTemplate, 并且開啟負載均衡
* @author cheng
* @dateTime
@Bean
@LoadBalanced
public RestTemplate createRestTemplate() {
return new
啟動項目:
- Ahut-Eureka-Server(注冊中心)
- Ahut-Eureka-Client-One(AHUT-EUREKA-CLIENT服務一)
- Ahut-Service-Ribbon(AHUT-SERVICE-RIBBON服務消費者)
通路eureka:
通路hystrix儀表盤 http://localhost:8940/hystrix
修改以下部分:
- 複制浏覽器的位址欄,粘貼到輸入框,并且加上.stream
-
自定義一個Title
修改完後,點選monitor stream,進入下一個頁面
中間通路一下Ahut-Service-Ribbon項目 http://localhost:8940/v1/hello