Hystrix: 服务熔断、降级
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败!
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”、如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒中内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
我们需要·弃车保帅·
什么是Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个服务预期的,可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩
服务熔断是什么
熔断机制是对应雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是 @HystrixCommand。
服务熔断
1、新建 springcloud-provider-dept-hystrix-8001 模块
拷贝 springcloud-provider-dept-8001 模块,到新建的模块。
2、导入 hystrix 依赖
<?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">
<parent>
<artifactId>springcloud</artifactId>
<groupId>com.allen</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>springcloud-provider-dept-hystrix-8001</artifactId>
<dependencies>
<!--我们需要在此模块用到另一个模块的实体类,需要导入-->
<dependency>
<groupId>com.allen</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
<version>2.1.4</version>
</dependency>
<!-- test-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-test</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- jetty-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-jetty</artifactId>
</dependency>
<!-- 热部署-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!-- https://mvnrepository.com/artifact/org.springframework.cloud/spring-cloud-starter-eureka -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--actuator完善监控信息-->
<!-- https://mvnrepository.com/artifact/org.springframework.boot/spring-boot-starter-actuator -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
<version>2.4.1</version>
</dependency>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
</project>
3、修改控制器 HystrixDeptController, 添加 @HystrixCommand 注解
HystrixDeptController
注意备选方法不需要加注解 @GetMapping
package com.allen.springcloud.deptController;
import com.allen.springcloud.pojo.Dept;
import com.allen.springcloud.service.DeptService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
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.RestController;
/**
* @author Allen
* @date 2021/1/7 19:30
*/
//提供Restful服务
@RestController
public class HystrixDeptController {
@Autowired
private DeptService deptService;
@GetMapping("/dept/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryDeptById(id);
if(dept==null){
throw new RuntimeException("id is error");
}
return dept;
}
//备选方法
//无需注解 @GetMapping
public Dept hystrixGet(@PathVariable("id") Long id){
//链式调用,需要在pojo类上添加 @Accessors(chain = true) //链式写法
return new Dept()
.setDeptno(id)
.setDname("id is error")
.setDb_source("no this database");
}
}
4、开启注解支持
@EnableCircuitBreaker
//添加熔断支持
package com.allen.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
/**
* @author Allen
* @date 2021/1/7 19:39
*/
//启动类
@SpringBootApplication
@EnableEurekaClient //在启动服务后,自动注册到Eureka中
@EnableDiscoveryClient //服务发现
@EnableCircuitBreaker //添加熔断支持
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class,args);
}
}
5、测试
启动7003、7004注册中心,启动 DeptProviderHystrix_8001 提供者服务,启动 80消费者服务,
访问数据库已有数据:
访问数据库不存在数据:
服务降级
1、首先在 springcloud-api 模块的 service 包下新建 DeptClientServiceFallbackFactory
package com.allen.springcloud.service;
import com.allen.springcloud.pojo.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
/**
* @author Allen
* @date 2021/1/10 21:18
*/
@Component
public class DeptClientServiceFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept queryById(Long id) {
return new Dept()
.setDeptno(id)
.setDeptname("没有对应的信息")
.setDb_source("no this database");
}
@Override
public List<Dept> queryAll() {
return null;
}
@Override
public boolean addDept(Dept dept) {
return false;
}
};
}
}
2、在 service 包下的 DeptClientService 接口注解中添加fallbackFactory
package com.allen.springcloud.service;
import com.allen.springcloud.pojo.Dept;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import java.util.List;
/**
* @author Allen
* @date 2021/1/10 17:10
*/
@FeignClient(value = "SPRINGCLOUD-PROVIDER-DEPT",fallbackFactory = DeptClientServiceFallbackFactory.class)
public interface DeptClientService {
@GetMapping("/dept/get/{id}")
public Dept queryById(@PathVariable("id") Long id);
@GetMapping("/dept/list")
public List<Dept> queryAll();
@PostMapping("/dept/add")
public boolean addDept(Dept dept);
}
3、springcloud-consumer-dept-feign 模块配置文件,开启服务降级
server:
port: 80
# 开启降级 feign-hystrix
feign:
hystrix:
enabled: true
# eureka配置
eureka:
client:
register-with-eureka: false
service-url:
defaultZone: http://eureka7002.com:7002/eureka/, http://eureka7003.com:7003/eureka/, http://eureka7004.com:7004/eureka/
4、测试
测试前需明白,7004是注册中心,8001是提供者服务,Feign80通过调用api模块的service接口直接访问消费者服务。
首先开启7004注册中心,开启普通8001提供者服务,开启Feign80消费者服务。
浏览器输入 http://localhost/consumer/dept/get/2 ,此时,能正常访问数据!
然后关闭8001提供者服务,再次访问,出现:
服务熔断和服务降级的区别
- 服务熔断针对一个方法,服务降级针对一个服务(包含多个方法)。
- 熔断是防止服务器出现不可控异常,降级是为了合理分配内存资源,减小服务器压力。
- 熔断针对服务端,降级针对客户端(从整体请求负载考虑)