背景
前面我们实现了微服务的集群化,以及微服务之间的调用、负载均衡的实现,我们通过Ribbon
与RestTemplate
、feign
与接口类和注解
的两种方式分别进行了实现。Ribbon实现,feign实现。
接下来我们将介绍服务熔断与服务降级。
由于分布式体系结构中微服务的依赖关系十分复杂,服务调用失败的情况难以避免。因此我们需要通过一些服务熔断与服务降级的操作来规避这些问题。
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和C又调用其它的微服务,这就是扇出。如果扇出链路上的某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进行引起系统崩溃,这就是雪崩效应。
对于高并发的应用来说,单一的后端依赖可能会导致所有的服务器的资源都在几秒中饱和。而且这些微服务还可能引发其它服务之间的延迟增加、备份队列、线程和其它资源紧张,造成整个系统发生更多的级联故障。
这就需要对故障和延迟进行隔离和管理,避免单个服务的故障影响到整个系统。
Hystrix
Hystrix是一个用于处理分布式系统的延迟和容错的开源库。
在分布式系统里,依赖不可避免的会调用失败,比如超时、异常等,Hystrix
可以保证在某个服务异常的情况下,不会导致整个系统出问题,避免级联故障,以提高分布式系统的弹性。
断路器本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似保险丝熔断),向调用方返回一个预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方法无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
Hystrix的功能:
- 服务熔断
- 服务降级
- 服务限流
- 接近实时的服务监控
服务熔断
熔断机制是应对雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间过长,Hystrix
会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常后恢复调用链路。
在SpringCloud
框架里熔断机制通过Hystrix
实现,Hystrix
会监控微服务间调用的状况。当失败的调用达到阈值后,例如5秒内20次调用失败,就会启动熔断机制。
Hystrix
实现熔断机制的注解是@HystrixCommand
。
实现
此模块与springcloud-provider-dept-8001
模块的功能是基本一致的,因此核心代码用一样的即可,后续再视需要完善Hystrix
部分的代码。
本案例的核心代码直接从Windows文件管理器中的springcloud-provider-dept-8001
的src
目录中复制过来:
创建模块
编辑pom.xml
引入以下依赖:
<dependencies>
<!-- 需要拿到实体类,因此需要配置api Module。这个类是我们刚才自己写的Module! -->
<dependency>
<groupId>com.langjialing</groupId>
<artifactId>springcloud-api</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<scope>test</scope>
</dependency>
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
</dependency>
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>druid</artifactId>
</dependency>
<dependency>
<groupId>org.mybatis.spring.boot</groupId>
<artifactId>mybatis-spring-boot-starter</artifactId>
</dependency>
<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>
<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>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!-- 此依赖用于完善监控信息 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.3.RELEASE</version>
</dependency>
</dependencies>
修改application.yaml
修改DeptController
package com.langjialing.springcloud.controller;
import com.langjialing.springcloud.pojo.Dept;
import com.langjialing.springcloud.service.DeptService;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.experimental.Accessors;
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;
//提供Restful服务的控制器
@RestController
public class DeptController {
@Autowired
private DeptService deptService;
@HystrixCommand(fallbackMethod = "hystrixGet")
@GetMapping("/dept/get/{id}")
public Dept get(@PathVariable("id") Long id){
Dept dept = deptService.queryById(id);
if (dept == null) {
throw new RuntimeException("该ID:" + id + "没有对应的信息");
}
return dept;
}
// 备选方案
public Dept hystrixGet(@PathVariable("id") Long id) {
Dept dept = new Dept();
dept.setDeptno(id);
dept.setDname("该ID:" + id + "没有对应的信息,null--@HystrixCommand");
dept.setDb_source("no this database in MySQL");
return dept;
}
}
修改主启动类
package com.langjialing.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.cloud.netflix.hystrix.EnableHystrix;
@SpringBootApplication
@EnableEurekaClient // 开启注解,在服务启动后会自动注入到Eureka中
@EnableDiscoveryClient // 服务发现
@EnableHystrix // 开启Hystrix
public class DeptProviderHystrix_8001 {
public static void main(String[] args){
SpringApplication.run(DeptProviderHystrix_8001.class, args);
}
}
测试功能
注意:
- 这里除了启动
Eureka
注册中心之外,需要启动的是springcloud-provider-dept-hystrix-8001
模块; - 为了更好地观察到效果,其它的服务提供者模块应该处于关机状态。
总结
本文我们使用Hystrix
实现了服务熔断,接下来我们继续介绍Hystrix
实现服务降级。