前言
分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。不做任何处理的情况下,很容易导致服务雪崩。
服务雪崩:多个微服务之间调用的时候,假设微服务 A 调用微服务 B 和微服务 C,微服务 B 和 微服务 C 又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务 A 的调用就会占用越来越多的系统资源,进而引起系统崩溃,这就是所谓的“服务雪崩”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几分钟内饱和。比失败更糟糕的是,这些应用程序还可能导致服务之间的延迟增加,备份队列、线程和其它系统资源紧张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以免因为单个依赖关系的失败对整个应用程序或系统造成影响。
断路器
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应,而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。
服务熔断
介绍
服务熔断是应对服务雪崩的一种微服务链路保护机制。一般是某个服务故障或者异常引起,类似现实世界中的“保险丝”,当某个异常条件被触发,直接熔断整个服务,而不是一直等到此服务超时。
当扇出链路的某个微服务不可用或响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回“错误”地响应信息。 当检测到该节点微服务调用响应正常后恢复调用链路。在 SpringCloud 中的熔断机制是通过 Hystrix 实现。Hystrix 会监控微服务间的调用状况,当失败的调用到一定阈值,缺省是 5 秒内 20 次调用失败就会启动熔断机制。启用熔断机制的注解是 @HystrixCommand 。
Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多以来不可避免的会调用失败,比如超时、异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
使用
1、复制 "microservicecloud-provider-dept-8001" 子工程改名为 "microservicecloud-provider-dept-hystrix-8001",新增断路器依赖:
<!--Hystrix 断路器-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
</dependency>
2、修改微服务实例 Id:
# application.yml
server:
port: 8001
mybatis:
config-location: classpath:mybatis/mybatis.cfg.xml # mybatis 配置文件路径
type-aliases-package: zze.springcloud.entities # 所有 Entity 别名类所在包
mapper-locations:
- classpath:mybatis/mapper/**/*.xml # mapper 映射文件
spring:
application:
name: microservicecloud-provider-dept # 当前微服务名称
datasource:
type: com.alibaba.druid.pool.DruidDataSource # 数据源操作类型
driver-class-name: org.gjt.mm.mysql.Driver # mysql 驱动包
url: jdbc:mysql:///springcloud_8001 # 数据库连接 root
username: root
password: root
dbcp2:
min-idle: 5 # 数据库连接池的最小维持连接数
initial-size: 5 # 初始化连接数
max-total: 5 # 最大连接数
max-wait-millis: 200 # 等待连接获取的最大超时时间
eureka:
client: # 将当前工程作为 Eureka 客户端
service-url:
# 单机版
# defaultZone: http://localhost:7001/eureka # Eureka 服务端地址
defaultZone: http://www.eurekaserver1.com:7001/eureka,http://www.eurekaserver2.com:7002/eureka,http://www.eurekaserver3.com:7003/eureka
instance:
instance-id: microservicecloud-provider-dept-hystrix-8001
prefer-ip-address: true # 访问路径显示 IP
info:
host: ${java.rmi.server.hostname}
port: ${server.port}
app.name: microservicecloud-provider-dept-8001
build.artifactId: ${project.artifactId}
build.version: ${project.version}
3、修改 Controller,给 get
方法添加上熔断注解,指定回调方法:
// zze.springcloud.controller.DeptController
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import zze.springcloud.entities.Dept;
import zze.springcloud.service.DeptService;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/dept")
public class DeptController {
@Autowired
private DeptService deptService;
@PostMapping("/add")
public boolean add(@RequestBody Dept dept) {
return deptService.add(dept);
}
@GetMapping("/get/{id}")
@HystrixCommand(fallbackMethod = "fallback_get") // fallbackMethod 指定回调方法名,get 方法异常时会调用指定的方法
public Dept get(@PathVariable Long id) {
Dept dept = deptService.get(id);
if(null == dept){
throw new RuntimeException("没有 id 为" + id + "的这个部门");
}
return dept;
}
/**
* get 方法熔断回调
*/
private Dept fallback_get(@PathVariable Long id){
return new Dept().setDeptNo(id).setDeptName("该部门不存在").setDbSource("数据源中没有这个部门");
}
@GetMapping("/list")
public List<Dept> list() {
return deptService.list();
}
}
4、修改主启动类,使用注解添加 Hystrix 服务熔断支持:
// zze.springcloud.Application_8001
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker // Hystrix 熔断功能支持
public class Application_8001 {
public static void main(String[] args) {
SpringApplication.run(Application_8001.class, args);
}
}
5、测试:
1、启动 microservicecloud-provider-dept-hystrix-8001 服务
2、访问 http://localhost:8001/dept/get/1 时正常返回数据
3、访问 http://localhost:8001/dept/get/11 时执行了熔断方法,返回了自定义的数据信息:
消费端(调用端)操作,从上面测试结果中可以看到即使 Provider 服务挂了,Consumer 也可以返回可处理的数据而不是随之挂掉。
评论区