分布式系统面临的问题
复杂的分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的"扇出"。 如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”。
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序还 可能导致服务之间的延迟增加,备份队列,线程和其他系统资源緊张,导致整个系统发生更多的级联故障。这些都表示需要对故障和延迟进行隔离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
所以,通常当一个模块下的某个实例失败后,这时候这个模块依然还会接收流量,然后这个有问题的模块还调用了其他的模块,这样就会发生级联故障,或者叫雪崩
Hystrix
Hystrix是个用于处理分布式系统的延退和容错的开源库,在分布式系统里,许多依赖不可避兔的会调用失败,比如超时、异常等。Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
"断路器" 本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应( Fallback),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避兔了故障在分布式系统中的蔓延,乃至雪崩。
引入pom
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
三个核心:
服务降级(FallBack)
发生降级的情况
- 程序运行异常
- 超时
- 服务熔断触发服务降级
- 线程池/信号量 打满也会导致五福降级
服务降级案例:
服务提供者方:
@Override
// 添加如果超过三秒的话,进行服务降级,超过三秒调用paymentInfo_ERRORHandler() 方法
@HystrixCommand(fallbackMethod = "paymentInfo_ERRORHandler", commandProperties = {
@HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000") // 设定超时时间
})
public String paymentInfo_ERROR(Integer id) {
try {
TimeUnit.SECONDS.sleep(5);
log.info("线程池:" + Thread.currentThread().getName() + " paymentInfo_ERROR,id:" + id + "TimeUnit.SECONDS.sleep(5)");
} catch (InterruptedException e) {
e.printStackTrace();
}
return "线程池:" + Thread.currentThread().getName() + " paymentInfo_ERROR,id:" + id + "--TimeUnit.SECONDS.sleep(5)";
}
// 降级处理的执行方法
@Override
public String paymentInfo_ERRORHandler(Integer id) {
return "线程池:" + Thread.currentThread().getName() + " 系统繁忙,或运行报错,id:" + id + " o(╥﹏╥)o";
}
服务调用方
@Component
@FeignClient(value = "ANYBOOT-PROVIDER-HYSTRIX-PAYMENT" , fallback = PaymentFeignServiceImpl.class)
public interface PaymentFeignService {
@GetMapping(value = "/provider/payment/queryById/{id}")
public ServerResponse queryById(@PathVariable("id") String id);
@GetMapping("/provider/payment/feignTimeout")
public ServerResponse paymentFeignTimeout();
@GetMapping(value = "/provider/payment/paymentInfo_OK/{id}")
public ServerResponse paymentInfo_OK(@PathVariable("id") Integer id);
@GetMapping(value = "/provider/payment/paymentInfo_ERROR/{id}")
public ServerResponse paymentInfo_ERROR(@PathVariable("id") Integer id);
}
服务调用方降级处理:
@Component
public class PaymentFeignServiceImpl implements PaymentFeignService {
@Override
public ServerResponse queryById(String id) {
return ServerResponse.createByErrorMessage("---------PaymentFeignServiceImpl -> queryById(),o(╥﹏╥)o----------");
}
@Override
public ServerResponse paymentFeignTimeout() {
return ServerResponse.createByErrorMessage("---------PaymentFeignServiceImpl -> paymentFeignTimeout(),o(╥﹏╥)o----------");
}
@Override
public ServerResponse paymentInfo_OK(Integer id) {
return ServerResponse.createByErrorMessage("---------PaymentFeignServiceImpl -> paymentInfo_OK(),o(╥﹏╥)o----------");
}
@Override
public ServerResponse paymentInfo_ERROR(Integer id) {
return ServerResponse.createByErrorMessage("---------PaymentFeignServiceImpl -> paymentInfo_ERROR(),o(╥﹏╥)o----------");
}
}
服务熔断(break)
熔断器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控,某个异常条件被触发,直接熔断整个服务。向调用方法返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出吊牌用方法无法处理的异常,就保证了服务调用方的线程不会被长时间占用,避免故障在分布式系统中蔓延,乃至雪崩
(服务降级→进行熔断→服务调用链路)
// 服务熔断
@HystrixCommand(fallbackMethod = "paymentCircuitBreaker_fallback", commandProperties = {
@HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 是否开启断路器
@HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 请求次数
@HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 时间窗口期
@HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60") // 失败率达到多少后到达
})
public String PaymentCircuitBreaker(@PathVariable("id") Integer id) {
if (id < 0) {
throw new RuntimeException("******id,不能为负数");
}
String serialNumber = IdUtil.simpleUUID();
return Thread.currentThread().getName() + " 调用成功,流水号:" + serialNumber;
}
// 熔断处理后的降级处理
public String paymentCircuitBreaker_fallback(@PathVariable("id") Integer id) {
return "id 不能负数,请稍后再试,o(╥﹏╥)o id:" + id;
}
服务限流
常用与高并发等操作,严禁一蜂窝的过来拥挤,通过排队,一秒钟N个,有序进行。