在微服务架构中,根据业务来拆分成一个个的服务,服务与服务之间可以相互调用(RPC),在Spring Cloud可以用RestTemplate+Ribbon和Feign来调用。为了保证其高可用,单个服务通常会集群部署。由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就会出现线程阻塞,此时若有大量的请求涌入,Servlet容器的线程资源会被消耗完毕,导致服务瘫痪。服务与服务之间的依赖性,故障会传播,会对整个微服务系统造成灾难性的严重后果,这就是服务故障的“雪崩”效应。
针对上述问题,在Spring Cloud Hystrix中实现了线程隔离、断路器等一系列的服务保护功能。它也是基于Netflix的开源框架 Hystrix实现的,该框架目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix具备了服务降级、服务熔断、线程隔离、请求缓存、请求合并以及服务监控等强大功能。
断路器概念:
断路器模式源于Martin Fowler的Circuit Breaker一文。“断路器”本身是一种开关装置,用于在电路上保护线路过载,当线路中有电器发生短路时,“断路器”能够及时的切断故障电路,防止发生过载、发热、甚至起火等严重后果。
在分布式架构中,断路器模式的作用也是类似的,当某个服务单元发生故障(类似用电器发生短路)之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个错误响应,而不是长时间的等待。这样就不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延。
断路器示意图:
SpringCloud Netflix实现了断路器库的名字叫Hystrix. 在微服务架构下,通常会有多个层次的服务调用. 下面是微服架构下, 浏览器端通过API访问后台微服务的一个示意图:
一个微服务的超时失败可能导致瀑布式连锁反映,下图中,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:
然后访问注册中心 ,正常显示如下:
说明a、b、c,ribbon-consumer-hystrix服务都注册成功了。
停止 spring-cloud-eureka-provider-a 提供者,端口为:10001服务,浏览器访问 http://localhost:20002/hello ,会出现下面三种返回,说明断路器已经生效,提示:Ribbon + hystrix ,提供者服务挂了
至此在Ribbon中使用断路器监控Hystrix简单Demo完成。