玩转SpringCloud专题(十)-SpringCloud之Hystrix断路器

1.Hystrix断路器

1.1.分布式系统面临的问题

复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免地失败。
在这里插入图片描述

服务雪崩
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其它的微服务,这就是所谓的“扇出”。如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”.

示例:
在微服务架构中通常会有多个服务层调用,基础服务的故障可能会导致级联故障,进而造成整个系统不可用的情况,这种现象被称为服务雪崩效应。服务雪崩效应是一种因“服务提供者”的不可用导致“服务消费者”的不可用,并将不可用逐渐放大的过程。

什么是灾难性的雪崩效应?我们通过结构图来说明,如下
在这里插入图片描述
正常情况下各个节点相互配置,完成用户请求的处理工作
在这里插入图片描述
当某种请求增多,造成"服务T"故障的情况时,会延伸的造成"服务U"不可用,及继续扩展,如下
在这里插入图片描述
最终造成下面这种所有服务不可用的情况
在这里插入图片描述
这就是我们讲的灾难性雪崩!

造成雪崩的原因可以归纳为以下三个:

服务提供者不可用(硬件故障,程序Bug,缓存击穿,用户大量请求)
重试加大流量(用户重试,代码逻辑重试)
服务调用者不可用(同步等待造成的资源耗尽)

最终的结果就是一个服务不可用,导致一系列服务的不可用,而往往这种后果是无法预料的。

1.2.Hystrix概述

Hystrix [hɪst’rɪks]的中文含义是豪猪,因其背上长满了刺而拥有自我保护能力。

Hystix,即熔断器。类似保险丝角色!

主页:https://github.com/Netflix/Hystrix/
在这里插入图片描述

Hystrix是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

在这里插入图片描述

1.3.熔断器的工作机制:

在这里插入图片描述

正常工作的情况下,客户端请求调用服务API接口:
在这里插入图片描述

当有服务出现异常时,直接进行失败回滚,服务降级处理:
在这里插入图片描述

当服务繁忙时,如果服务出现异常,不是粗暴的直接报错,而是返回一个友好的提示,虽然拒绝了用户的访问,但是会返回一个结果。

这就好比去买鱼,平常超市买鱼会额外赠送杀鱼的服务。等到逢年过节,超时繁忙时,可能就不提供杀鱼服务了,这就是服务的降级。

系统特别繁忙时,一些次要服务暂时中断,优先保证主要服务的畅通,一切资源优先让给主要服务来使用,在双十一、618时,京东天猫都会采用这样的策略。

2.Hystrix服务降级

2.1.场景介绍

先来看下正常服务调用的情况
在这里插入图片描述
当consumer调用provider服务出现问题的情况下:
在这里插入图片描述
此时我们对consumer的服务调用做降级处理
在这里插入图片描述

整体资源快不够了,忍痛将某些服务先关掉,待渡过难关,再开启回来。服务降级处理是在客户端实现完成的,与服务端没有关系。

Fallback相当于是降级操作。对于查询操作,我们可以实现一个fallback方法,当请求后端服务出现异常的时候,可以使用fallback方法返回的值。 fallback方法的返回值一般是设置的默认值或者来自缓存。

2.2.引入依赖

首先在user-consumer中引入Hystix依赖:

<!--服务熔断组件-->
<dependency>
	<groupId>org.springframework.cloud</groupId>
	<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

2.3.修改之前的Controller

在之前的Controller中添加熔断机制:

@RequestMapping(value = "/get/{id}", method = RequestMethod.GET)
// 一旦调用服务方法失败,会自动调用@HystrixCommand标注好的fallbackMethod调用类中的指定方法
@HystrixCommand(fallbackMethod = "processHystrix_Get")
public User get(@PathVariable("id") Long id) {
    User u = this.userService.get(id);
    return u;
}

//备选方案
public User processHystrix_Get(@PathVariable("id") Long id) {
    //模拟的备选数据,可以来源于缓存
    User u=new User();
    u.setId(110);
    u.setUsername("该ID:" + id + "没有没有对应的信息,null--@HystrixCommand");
    u.setNote("no this database in MySQL");
    return u;
}

2.4.修改主启动类

修改consumer并添加新注解@EnableCircuitBreaker

@SpringBootApplication
@EnableDiscoveryClient // 开启EurekaClient功能
@EnableFeignClients // 开启Feign功能
@EnableCircuitBreaker//对hystrixR熔断机制的支持
public class SpringcloudDemoConsumerApplication {

	public static void main(String[] args) {
		SpringApplication.run(SpringcloudDemoConsumerApplication.class, args);
	}
}

2.5. 服务熔断测试

3个eureka先启动
主启动类SpringcloudDemoConsumerApplication

访问测试
http://127.0.0.1:88/consumer/get/2
在这里插入图片描述
关闭服务提供者
在这里插入图片描述

3. 服务降级优化-彻底解耦

修改microservicecloud-api工程,根据已经有的DeptClientService接口新建一个实现FallbackFactory接口的类DeptClientServiceFallbackFactory

/**
 * @author bruceliu
 * @create 2019-08-04 15:11
 * @description
 */
@Component // 不要忘记添加
public class UserClientServiceFallbackFactory implements FallbackFactory<UserClientService> {


    @Override
    public UserClientService create(Throwable throwable) {
        return new UserClientService() {
            @Override
            public List<User> queryUsers() {
                return null;
            }

            @Override
            public User get(Long id) {
                User u=new User();
                u.setId(110);
                u.setUsername("该ID:\" + id + \"没有没有对应的信息,null--服务降级~~");
                u.setNote("no this database in MySQL----服务降级!!!");
                return u;
            }
        };
    }
}
  • 修改consumer工程,UserClientService接口在注解@FeignClient中添加fallbackFactory属性值
/**
 * @author bruceliu
 * @create 2019-05-04 18:49
 * @description Feign客户端
 */
@FeignClient(value = "SPRINGCLOUD-DEMO-SERVICE",fallbackFactory=UserClientServiceFallbackFactory.class)
public interface UserClientService {

    @RequestMapping("/all")
    public List<User> queryUsers();

    @RequestMapping("/get/{id}")
    public User get(@PathVariable("id") Long id);
}
  • 修改配置文件
# 开启服务熔断策略
feign.hystrix.enabled=true

3.1.测试

- 3个eureka先启动
- 微服务提供者启动
- 微服务消费者启动

正常访问测试:http://127.0.0.1:88/consumer/get/1
在这里插入图片描述
故意关闭微服务提供者
在这里插入图片描述
客户端自己调用提示此时服务端provider已经down了,但是我们做了服务降级处理,让客户端在服务端不可用时也会获得提示信息而不会挂起耗死服务器。

4. 服务熔断

熔断在降级的基础之上

熔断其实是在降级的基础上引入了重试的机制。当某个时间内失败的次数达到了多少次就会触发熔断机制。熔断机制是应对雪崩效应的一种微服务链路保护机制。
在这里插入图片描述

断路器很好理解,当Hystrix Command请求后端服务熔断器在10秒内发现请求总数超过20,并且错误百分比超过50%,,断路器会切换到开路状态(Open)。这时所有请求会直接失败而不会发送到后端服务。 断路器保持在开路状态一段时间后(默认5秒),自动切换到半开路状态(HALF-OPEN)。这时会判断下一次请求的返回情况, 如果请求成功,断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN)。 Hystrix的断路器就像我们家庭电路中的保险丝,一旦后端服务不可用,断路器会直接切断请求链,避免发送大量无效请求影响系统吞吐量, 并且断路器有自我检测并恢复的能力。

熔断器开关相互转换的逻辑图:
在这里插入图片描述
那么当断路器打开之后会发生什么呢?当熔断器在10秒内发现请求总数超过20,并且错误百分比超过50%,这个时候熔断器打开。打开之后,再有请求调用的时候,将不会调用主逻辑,而是直接调用降级逻辑,返回fallback。通过断路器,实现了自动地发现错误并将主逻辑切换为降级逻辑,减少响应延迟的效果。

在断路器打开之后,处理逻辑并没有结束,我们的降级逻辑已经被成了主逻辑,那么原来的主逻辑要如何恢复呢?对于这一问题,hystrix也为我们实现了自动恢复功能。当断路器打开,对主逻辑进行熔断之后,hystrix会启动一个休眠时间窗,在这个时间窗内,降级逻辑是临时的成为主逻辑,当休眠时间窗到期,断路器将进入半开状态,释放一次请求到原来的主逻辑上,如果此次请求正常返回,那么断路器将继续闭合,主逻辑恢复,如果这次请求依然有问题,断路器继续进入打开状态,休眠时间窗重新计时。

通过上面的一系列机制,hystrix的断路器实现了对依赖资源故障的端口、对降级策略的自动切换以及对主逻辑的自动恢复机制。这使得我们的微服务在依赖外部服务或资源的时候得到了非常好的保护,同时对于一些具备降级逻辑的业务需求可以实现自动化的切换与恢复,相比于设置开关由监控和运维来进行切换的传统实现方式显得更为智能和高效。

发布了57 篇原创文章 · 获赞 21 · 访问量 3933

猜你喜欢

转载自blog.csdn.net/wmlwml0000/article/details/105068078