文档章节

白话SpringCloud | 第五章:服务容错保护(Hystrix)

oKong
 oKong
发布于 09/23 16:45
字数 3297
阅读 400
收藏 5

前言

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

一点知识

按照此系列的惯例,我们先来了解下一些相关的知识。

注:以下部分内容转至大佬纯洁的微笑:熔断器Hystrix

容错处理手段

容错处理是指软件运行时,能对由非正常因素引起的运行错误给出适当的处理或信息提示,使软件运行正常结束——百度百科

从百度百科的解释中可以看出,简单理解,所谓的容错处理其实就是捕获异常了,不让异常影响系统的正常运行,正如java中的try catch一样。

而在微服务调用中,自身异常可自行处理外,对于依赖的服务若发生错误,或者调用异常,或者调用时间过长等原因时,避免长时间等待,造成系统资源耗尽。 一般上都会通过设置请求的超时时间,如http请求中的ConnectTimeoutReadTimeout;再或者就是使用熔断器模式,隔离问题服务,防止级联错误等。

雪崩效应

在微服务架构中,存在很多的微服务单元,各个微服务之间通过网络进行通讯,难免出现依赖关系,若某一个单元出现故障,就很容易因依赖关系而引发故障的蔓延,产生“雪崩效应”,最终导致整个系统的瘫痪。

下面这张图,相比大家都有看过了。

雪崩效应

如图所示:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,并将不可用像滚雪球一样放大到C和D时,雪崩效应就形成了。也就应了那句话:星星之火,可以燎原!

熔断器

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

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

熔断器状态转换图

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

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

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

Hystrix实践

何为Hystrix

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

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

Hystrix容错机制:

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

下图就是Hystrix的回退策略,防止级联故障。

Hystrix fallback prevents cascading failures

常规方式整合Hystrix

创建个工程spring-cloud-hystrix工程。 0.引入POM依赖。

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

1.启动类,加入注解@EnableHystrix,同时申明一个实现负载均衡的RestTemplate。(关于消费者服务可查看:第四章:服务消费者(RestTemple+Ribbon+Feign),这里不再阐述了。)

/**
 * 熔断器示例
 * @author oKong
 *
 */
@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
@Slf4j
public class HystrixApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(HystrixApplication.class, args);
        log.info("sprign-cloud-hystrix启动!");
    }

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

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

RibbonController.java

/**
 * ribbon 常规方式-示例
 * @author oKong
 *
 */
@RestController
@Slf4j
public class RibbonController {

    @Autowired
    RestTemplate restTemplate;
    
    @GetMapping("/ribbon")
    @HystrixCommand(fallbackMethod="fallback")
    public String hello(String name) {
        log.info("使用restTemplate调用服务,参数name:{}", name);
        return restTemplate.getForObject("http://eureka-client/hello?name=" + name, String.class);
    }
    
    /**
     * 发生熔断时调用的方法
     * @param name
     * @param throwable 发生异常时的异常信息
     * @return
     */
    public String fallback(String name,Throwable throwable) {
        log.error("熔断发生了:{}", throwable);
        log.warn("restTemplate调用服务发生熔断,参数name:{}", name);
        return "restTemplate调用服务发生熔断,参数name:" + name;
    }
}

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

3.启动应用,访问:http://127.0.0.1:8038/ribbon?name=oKong 。正常情况下,spring-cloud-eureka-client应用正常运行时,返回正常结果:

正常情况

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

异常情况

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

异常信息

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

No instances available for eureka-client

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

Feign整合Hystrix

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

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

修改spring-cloud-hystrix工程。

0.引入Feigin的POM依赖。

     <!-- feign -->
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-starter-openfeign</artifactId>
        </dependency>    

1.启动类,加入@EnableFeignClients启用Feign.

**
 * 熔断器示例
 * @author oKong
 *
 */
@SpringBootApplication
@EnableHystrix
@EnableDiscoveryClient
@EnableFeignClients
@Slf4j
public class HystrixApplication {

    public static void main(String[] args) throws Exception {
        SpringApplication.run(HystrixApplication.class, args);
        log.info("sprign-cloud-hystrix启动!");
    }

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

    /**
     * 定义接口
     * @param name
     * @return
     */
    @RequestMapping(value="/hello", method=RequestMethod.GET)
    public String hello(@RequestParam("name") String name);
}
  1. 创建fallbackfallbackFactory属性对应类。

HelloClientFailImpl.java

@Component("fallback")
@Slf4j
public class HelloClientFailImpl implements IHelloClient{
    
    @Override
    public String hello(String name) {
        log.error("restTemplate调用[hello]服务发生熔断,参数name:{}", name);
        return "restTemplate调用[hello]服务发生熔断,参数name:" + name;
    }
}

HelloClientFallbackFactory/java

@Component
@Slf4j
public class HelloClientFallbackFactory implements FallbackFactory<IHelloClient>{

    @Autowired
    @Qualifier("fallback")
    IHelloClient helloClient;
    
    @Override
    public IHelloClient create(Throwable cause) {
        log.error("feign调用发生异常,触发熔断", cause);
        return helloClient;
    }

}

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

3.配置文件开启feign的熔断器功能。

feign.hystrix.enabled=true

或者,申明一个Feign.Builder类也是可以的,我们从org.springframework.cloud.openfeign.FeignClientsConfiguration可以看出,启用feign的条件:

HystrixFeignConfiguration

所以正常,我们只需要在配置文件中加入feign.hystrix.enabledtrue即可,注意:此属性在IDE下未进行提示的。 或者就如此类一样,申明一个bean:

@Bean
public Feign.Builder feignHystrixBuilder() {
            return HystrixFeign.builder();
        }

也是可以的。

4.编写一个测试类FeignController

/**
 * feign 熔断器示例
 * @author oKong
 *
 */
@RestController
@Slf4j
public class FeignController {

    @Autowired
    IHelloClient helloClient;
    
    @GetMapping("/feign")
    public String hello(String name) {
        log.info("使用feign调用服务,参数name:{}", name);
        return helloClient.hello(name);
    }
    
}

5.再次启动应用,访问:http://127.0.0.1:8038/feign?name=oKong ,正常调用如下:

正常调用

关闭服务提供者,再次访问,浏览器返回了错误提示:

fallback

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

异常信息


针对熔断超时时间等相关设置,可以通过@HystrixCommand注解的各属性进行配置,主要还是commandProperties属性值,具体的参数可查看com.netflix.hystrix.HystrixCommandProperties类,也可以针对某个调用方法进行特殊设置。具体的可以看看这篇文章:hystrix的基本介绍和配置属性说明,或者可以去大佬程序员DD博客查阅下关于Hystrix相关知识点:服务容错保护(Hystrix断路器)【Dalston版】服务容错保护(Hystrix依赖隔离)【Dalston版】,版本虽然是D版的,但原理是差不多的~


参考资料

  1. http://www.ityouknow.com/springcloud/2017/05/16/springcloud-hystrix.html

  2. https://cloud.spring.io/spring-cloud-static/Finchley.SR1/single/spring-cloud.html#_circuit_breaker_hystrix_clients

总结

本章节主要讲解了如何整合Hystrix。本身Hystrix已经包含了服务降级依赖隔离熔断器等功能了,我们使用时并没进行特殊设置,默认都是生效的。对于一些关于Hystrix的高级用法,比如信号量隔离、线程池的设置等等,还有一些像超时时间等,由于此方面了解的不多,这里就不班门弄斧了。大家可去官方网站或者谷歌搜索下相关材料下,对于一些业务场景,可进行一些自定义设置。主要还是针对@HystrixCommand注解的相关配置。关于调用统一异常的处理相关实践,比如当提供方异常时,调用方如何进行统一异常处理,或者服务不可用时,进行统一的异常捕获,告知外围调用者服务不可用等信息。这些相关实践部分会在之后的实践篇系列文章中进行阐述的,也许不是最佳的实践,仅希望提供一个参考方案吧。这里就不表了,敬请期待~

最后

目前互联网上大佬都有分享SpringCloud系列教程,内容可能会类似,望多多包涵了。原创不易,码字不易,还希望大家多多支持。若文中有错误之处,还望提出,谢谢。

老生常谈

  • 个人QQ:499452441
  • 微信公众号:lqdevOps

公众号

个人博客:http://blog.lqdev.cn

源码示例:https://github.com/xie19900123/spring-cloud-learning

原文地址:http://blog.lqdev.cn/2018/09/23/SpringCloud/chapter-five/

© 著作权归作者所有

共有 人打赏支持
oKong
粉丝 569
博文 61
码字总数 146986
作品 0
福州
高级程序员
私信 提问
史上最简单的 SpringCloud 教程 | 终章

版权声明:本文为博主原创文章,欢迎转载,转载请注明作者、原文超链接 ,博主地址:http://blog.csdn.net/forezp。 https://blog.csdn.net/forezp/article/details/70148833 转载请标明出处...

方志朋
2017/04/12
0
0
史上最简单的 SpringCloud 教程

《史上最简单的 SpringCloud 教程》系列: 史上最简单的 SpringCloud 教程 | 第一篇: 服务的注册与发现(Eureka) 史上最简单的SpringCloud教程 | 第二篇: 服务消费者(rest+ribbon) 史上最...

外星人et59
04/21
0
0
白话SpringCloud | 第零章:前言

说在前面 大清早醒来,觉得睡不着了。还是起来,写写博客。但最后发现关于的安全相关的还是比较多内容的,也比较专业,怕是一个多小时完不成的,也罢,那就来写写关于前言吧。 说明 《白话S...

oKong
09/04
0
0
Springcloud应用在阿里云Kubernetes上的IP互通实践

问题 在应用微服务化方案中,Springcloud是比较常见的选择,毕竟其对于Java 的程序员来说比较友好,基于Springboot的编程方式也使得门槛比较低。但是在将Springcloud的应用运行到Kubernetes容...

了哥-duff
06/25
0
0
springcloud eureka 服务注册中心

------------------------------------------------------------------------------------------------------------ springcloud (零) springboot 基础 下载 springcloud (一)服务注册中心 ......

curiousby
2017/02/08
0
0

没有更多内容

加载失败,请刷新页面

加载更多

Web安全之XSS攻击与防御小结

Web安全之XSS攻防 1. XSS的定义 跨站脚本攻击(Cross Site Scripting),缩写为XSS。恶意攻击者往Web页面里插入恶意Script代码,当用户浏览该页之时,嵌入其中Web里面的Script代码会被执行,从...

前端小攻略
20分钟前
1
0
JavaScript中的继承及实现代码

JS虽然不像是JAVA那种强类型的语言,但也有着与JAVA类型的继承属性,那么JS中的继承是如何实现的呢? 一、构造函数继承 在构造函数中,同样属于两个新创建的函数,也是不相等的 function Fn...

peakedness丶
23分钟前
1
0
记一次面试最常见的10个Redis"刁难"问题

导读:在程序员面试过程中Redis相关的知识是常被问到的话题。作为一名在互联网技术行业打击过成百上千名的资深技术面试官,本文作者总结了面试过程中经常问到的问题。十分值得一读。 Redis在...

小刀爱编程
36分钟前
13
0
TiDB Lab 诞生记 | TiDB Hackathon 优秀项目分享

本文由红凤凰粉凤凰粉红凤凰队的成员主笔,他们的项目 TiDB Lab 在本届 TiDB Hackathon 2018 中获得了二等奖。TiDB Lab 为 TiDB 培训体系增加了一个可以动态观测 TiDB / TiKV / PD 细节的动画...

TiDB
49分钟前
4
0
当区块链遇到零知识证明

本文由云+社区发表 当区块链遇到零知识证明 什么是零知识证明 零知识证明的官方定义是能够在不向验证者任何有用的信息的情况下,使验证者相信某个论断是正确的。这个定义有点抽象,下面笔者举...

腾讯云加社区
58分钟前
5
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部