分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免失败!
1、服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”,如果扇出的链路上某个微服务的调用响应时间过长,或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几十秒内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多的级联故障,这些都表示需要对故障和延迟进行隔离和管理,以达到单个依赖关系的失败而不影响整个应用程序或系统运行。
我们需要,弃车保帅!
2、什么是Hystrix?
Hystrix是一个应用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整个体系服务失败,避免级联故障,以提高分布式系统的弹性。
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控 (类似熔断保险丝) ,向调用方返回一个服务预期的,可处理的备选响应 (FallBack) ,而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方的线程不会被长时间,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
3、Hystrix能干嘛?
- 服务降级
- 服务熔断
- 服务限流
- 接近实时的监控
- …
当一切正常时,请求流可以如下所示:
当许多后端系统中有一个潜在阻塞服务时,它可以阻止整个用户请求:
随着大容量通信量的增加,单个后端依赖项的潜在性会导致所有服务器上的所有资源在几秒钟内饱和。
应用程序中通过网络或客户端库可能导致网络请求的每个点都是潜在故障的来源。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,从而备份队列、线程和其他系统资源,从而导致更多跨系统的级联故障。
当使用Hystrix包装每个基础依赖项时,上面的图表中所示的体系结构会发生类似于以下关系图的变化。每个依赖项是相互隔离的,限制在延迟发生时它可以填充的资源中,并包含在回退逻辑中,该逻辑决定在依赖项中发生任何类型的故障时要做出什么样的响应:
4、服务熔断
熔断机制是赌赢雪崩效应的一种微服务链路保护机制。
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。检测到该节点微服务调用响应正常后恢复调用链路。
Hystrix 会监控微服务间调用的状况,当失败的调用到一定阀值缺省是5秒内20次调用失败,就会启动熔断机制。熔断机制的注解是:@HystrixCommand
。
服务熔断解决如下问题:
- 当所依赖的对象不稳定时,能够起到快速失败的目的;
- 快速失败后,能够根据一定的算法动态试探所依赖对象是否恢复。
5、操作步骤
服务熔断操作是在服务端
- 新建
springcloud-provider-dept-hystrix-8001
服务提供者模块- 拷贝 springcloud-provider-dept–8001 内的pom.xml、resource和Java代码进行初始化并调整
1)导jar包
spring-cloud-starter-hystrix
已废弃- 使用
spring-cloud-starter-netflix-hystrix
,并且要加版本号;因为自 SpringCloud 2020 起,已经去除了Hystrix
- 可以把版本信息放置在父工程的依赖管理
dependencyManagement
中
<!-- spring-cloud-starter-netflix-hystrix -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
<version>2.2.10.RELEASE</version>
</dependency>
2)@HystrixCommand
- 在
Controller
类中方法上添加@HystrixCommand
,并提供备选方法- 备选方法 和 服务方法的返回值类型一样
@RestController
public class DepartmentController {
@Autowired
private DepartmentService departmentService;
@GetMapping("/department/get/{id}")
@HystrixCommand(fallbackMethod = "hystrixGet")
public Department getDepartment(@PathVariable("id") Long id){
Department department = departmentService.queryById(id);
if(department == null){
throw new RuntimeException("获取部门失败!");
}
return department;
}
/**
* 备选方法
* @param id
* @return
*/
public Department hystrixGet(@PathVariable("id") Long id){
return new Department()
.setDeptId(id)
.setDeptName("当前ID的部门不存在!")
.setDbSource("不存在");
}
}
3)@EnableHystrix
- 在主启动类上添加;表示开启 Hystrix
- 旧版本中使用
@EnableCircuitBreaker
,已废弃;在新版本中由@EnableHystrix
替代
@SpringBootApplication
@EnableEurekaClient
@EnableDiscoveryClient
@EnableHystrix
public class DepartmentProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DepartmentProviderHystrix_8001.class,args);
}
}
4)测试
熔断服务
- 开启的服务
- 注册中心
- 服务提供者
- 服务消费者
不使用熔断机制
- 开启的服务
- 服务消费者