Spring cloud-熔断器Hystrix

Spring cloud-熔断器Hystrix

@author 无忧少年

@createTime 2019/10/20

​ 在前边,我们知道了如何利用RestTemplate+RibbonFeign的方式进行服务的调用。在微服务架构中,一个服务可能会调用很多的其他微服务应用,虽然做了多集群部署,但可能还会存在诸如网络原因或者服务提供者自身处理的原因,或多或少都会出现请求失败或者请求延迟问题,若服务提供者长期未对请求做出回应,服务消费者又不断的请求下,可能就会造成服务提供者服务崩溃,进而服务消费者也一起跟着不可用,严重的时候就发生了系统雪崩了。鉴于此,产生了断路器等一系列的服务保护机制。本次就来说下如何利用Hystrix进行容错处理

雪崩效应

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

​ 如果下图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。

在这里插入图片描述

熔断器

​ 熔断器,和现实生活中的空气开关作用很像。它可以实现快速失败,如果它在一段时间内侦测到许多类似的错误,会强迫其以后的多个调用快速失败,不再访问远程服务器,从而防止应用程序不断地尝试执行可能会失败的操作,使得应用程序继续执行而不用等待修正错误,或者浪费CPU时间去等到长时间的超时产生。熔断器也可以使应用程序能够诊断错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

​ 熔断器模式就像是那些容易导致错误的操作的一种代理。这种代理能够记录最近调用发生错误的次数,然后决定使用允许操作继续,或者立即返回错误

在这里插入图片描述 熔断器状态转换图

可以看出,熔断器一共有三种状态,之间转换关系如下:

  • 关闭状态
    当熔断器处于关闭状态时,请求是可以被放行的;
    当熔断器统计的失败次数触发开关时,转为打开状态。
  • 打开状态
    当熔断器处于打开状态时,所有请求都是不被放行的,直接返回失败;
    只有在经过一个设定的时间窗口周期后,熔断器才会转换到半开状态
  • 半开状态
    当熔断器处于半开状态时,当前只能有一个请求被放行;
    这个被放行的请求获得远端服务的响应后,假如是成功的,熔断器转换为关闭状态,否则转换到打开状态。

个人觉得,主要还是快速失败,避免请求堆积,压垮服务器。进而起到保护服务高可用的目的。

Hystrix实践

何为Hystrix

Hystrix是一个实现了超时机制和断路器模式的工具类库。

Hystrix是有Netflix开源的一个延迟和容错库,用于隔离访问远程系统、服务或第三方库,防止级联失败,从而提升系统的可用性和容错性。

Hystrix容错机制:

  • 包裹请求:使用HystrixCommand包裹对依赖的调用逻辑,每个命令在独立线程中执行,这是用到了设计模式“命令模式”。
  • 跳闸机制:当某服务的错误率超过一定阈值时,Hystrix可以自动或手动跳闸,停止请求该服务一段时间。
  • 资源隔离:Hystrix为每个依赖都维护了一个小型的线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速判定失败。
  • 监控:Hystrix可以近乎实时的监控运行指标和配置的变化。如成功、失败、超时、被拒绝的请求等。
  • 回退机制:当请求失败、超时、被拒绝,或当断路器打开时,执行回退逻辑。回退逻辑可自定义。
  • 自我修复:断路器打开一段时间后,会自动进入半开状态,断路器打开、关闭、半开的逻辑转换。

下图就是Hystrix的回退策略,防止级联故障。
在这里插入图片描述

常规方式整合Hystrix

创建个工程spring-cloud-hystrix工程。

0.引入POM依赖。

<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>

1.启动类,加入注解@EnableHystrix,同时申明一个实现负载均衡的RestTemplate

@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
public class HystrixApplication {
    private static Log log = LogFactory.getLog("HystrixApplication.java");

    public static void main(String[] args) {
        SpringApplication.run(HystrixApplication.class, args);
        log.info("-----------------Hystrix启动成功---------------------");
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplat() {
        return new RestTemplate();
    }
}

2.编写一个测试类,加入@HystrixCommand,指定fallbackMethod方法。

/**
 * ribbon 常规方式-示例
 * @author lisiwen
 */
@RestController
public class RibbonController {
    private static Log log= LogFactory.getLog("RibbonController.java");
    @Autowired
    RestTemplate restTemplate;

    @GetMapping("/ribbon")
    @HystrixCommand(fallbackMethod="fallback")
    public String hello() {
        log.info("使用restTemplate调用服务");
        return restTemplate.getForObject("http://server-provide/eurekaServer/HelloWorld", String.class);
    }

    /**
     * 发生熔断时调用的方法
     * @param throwable 发生异常时的异常信息
     * @return
     */
    public String fallback(Throwable throwable) {
        log.error("熔断发生了:{}", throwable);
        log.warn("restTemplate调用服务发生熔断");
        return "restTemplate调用服务发生熔断" ;
    }
}

​ 注意:这里fallback方法加入了一个参数throwable,当发生熔断时,可以获悉发生熔断的异常信息,便于定位问题和原因。

3.启动应用,访问: http://localhost:1003/ribbon 。正常情况下,spring-cloud-eureka-client应用正常运行时,返回正常结果:

在这里插入图片描述

在这里插入图片描述

	现在我们停止提供者服务,再次访问,可以看见已经进入熔断方法了: 

在这里插入图片描述

	控制台可以看见异常输出: 

在这里插入图片描述

​ 由于实例尚未被剔除注册中心的服务列表,所以提示是连接超时,等待一段时间后,再次访问服务,可以看见是提示实例不存在了:

在这里插入图片描述
注意:对于@HystrixCommand注解,我们可以放在任何一个调用函数里面,以此实现调用方法发生异常或者错误时,可以快速返回,避免持续请求,造成资源的耗尽。

Feign整合Hystrix

​ 如上小节说示例的,当我们方法很多时,要是分别编写一个fallback估计也是崩溃的,虽然可以使用一个通用的fallback,但未进行特殊设置下,也是无法知道具体是哪个方法发生熔断的。

​ 而对于Feign,我们可以使用一种更加优雅的形式进行。我们可以指定@FeignClient注解的fallback属性,或者是fallbackFactory属性,后者可以获取异常信息的。

修改spring-cloud-hystrix工程。

  1. 引入POM依赖。
<dependency>
    <groupId>org.springframework.cloud</groupId>
    <artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
  1. 启动类,加入注解@EnableHystrix,同时申明一个实现负载均衡的RestTemplate
@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
public class HystrixApplication {
    private static Log log = LogFactory.getLog("HystrixApplication.java");

    public static void main(String[] args) {
        SpringApplication.run(HystrixApplication.class, args);
        log.info("-----------------Hystrix启动成功---------------------");
    }

    @Bean
    @LoadBalanced
    public RestTemplate restTemplat() {
        return new RestTemplate();
    }
}
  1. 创建一个服务接口类IHelloClient.java,同时定义fallback或者fallbackFactory属性值。注意:两者 同时设置时,优先调用fallbackfallbackFactory不进行调用了。

    @FeignClient(name="server-provide",/*fallback=HelloClientFallbackFactoryServiceImpl.class,*/ fallbackFactory = HelloClientFallbackFactoryServiceImpl.class)
    public interface IHelloClientService {
    
        @RequestMapping(value="/eurekaServer/HelloWorld", method= RequestMethod.GET)
        String hello();
    }
    
    
  2. 创建fallbackfallbackFactory属性对应类。

@Component("fallback")
public class HelloClientFailServiceImpl implements IHelloClientService {
    private static Log log = LogFactory.getLog("HelloClientFailServiceImpl.java");

    @Override
    public String hello() {
        log.error("restTemplate调用[hello]服务发生熔断");
        return "restTemplate调用[hello]服务发生熔断";
    }
}
@Component
public class HelloClientFallbackFactoryServiceImpl implements FallbackFactory<IHelloClientService> {
    private static Log log = LogFactory.getLog("HelloClientFallbackFactory.java");

    @Autowired
    @Qualifier("fallback")
    IHelloClientService helloClientService;

    @Override
    public IHelloClientService create(Throwable cause) {
        log.error("feign调用发生异常,触发熔断", cause);
        return helloClientService;
    }
}

可以知道,正常fallback就是一个接口的实现类,当发送异常时,会调用此接口实现类进行服务调用。而FallbackFactory是也是一个接口实现类,需要实现feign.hystrix.FallbackFactory接口,在发生熔断时,调用create方法,同时返回被调用接口的实现类,以便进行fallback处理。

  1. 配置文件开启feign的熔断器功能。
feign.hystrix.enabled=true
  1. 编写一个测试类FeignController
@RestController
public class FeignController {
    private static Log log= LogFactory.getLog("FeignController.java");
    @Autowired
    IHelloClientService helloClientService;

    @GetMapping("/feign")
    public String hello2() {
        log.info("使用feign调用服务");
        return helloClientService.hello();
    }
}
  1. 再次启动应用,访问: http://localhost:1003/feign ,正常调用如下:

    在这里插入图片描述
    关闭服务提供者,再次访问,浏览器返回了错误提示:

在这里插入图片描述

同时,我们使用了FallbackFactory,控制台打印出了具体异常:

在这里插入图片描述 异常信息

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值