Spring Cloud Hystrix - 服务容错

服务容错和Hystrix

在微服务架构中,由于某个服务的不可用导致一系列的服务崩溃,被称之为雪崩效应。所以防御服务的雪崩效应是必不可少的,在Spring Cloud中防雪崩的利器就是Hystrix,Spring Cloud Hystri是基于Netflix Hystrix实现的。Hystrix的目标在于通过控制那些访问远程系统、服务和第三方库的节点,从而对延迟和故障提供更强大的容错能力。Hystrix 具备服务降级、服务容错、服务熔断、线程和信号隔离、请求缓存、请求合并以及服务监控等强大功能。

Hystrix中的资源隔离:

在Hystrix中, 主要通过线程池来实现资源隔离. 通常在使用的时候我们会根据调用的远程服务划分出多个线程池. 例如调用产品服务的Command放入A线程池, 调用账户服务的Command放入B线程池. 这样做的主要优点是运行环境被隔离开了. 这样就算调用服务的代码存在bug或者由于其他原因导致自己所在线程池被耗尽时, 不会对系统的其他服务造成影响. 但是带来的代价就是维护多个线程池会对系统带来额外的性能开销. 如果是对性能有严格要求而且确信自己调用服务的客户端代码不会出问题的话, 可以使用Hystrix的信号模式(Semaphores)来隔离资源.

关于服务降级:

  • 优先核心服务,非核心服务不可用或弱可用
  • 在Hystrix中可通过HystrixCommand注解指定可降级的服务
  • 在fallbackMethod(回退函数)中具体实现降级逻辑。对于查询操作, 我们可以实现一个fallback方法, 当请求后端服务出现异常的时候, 可以使用fallback方法返回的值. fallback方法的返回值一般是设置的默认值或者来自缓存

触发降级

本小节我们来模拟一下触发服务降级的情况,首先在订单服务项目的pom.xml文件中,加入Spring Cloud Hystrix依赖。如下:

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

添加好依赖后修改一下启动类的注解。修改后代码如下:

package org.zero.springcloud.order.server;

import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

@SpringCloudApplication
@EnableFeignClients(basePackages = "org.zero.springcloud.product.client")
public class OrderApplication {

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

在controller包中,新建一个 HystrixController ,我们在这个类里做实验。在这个类里,我们调用了商品服务中的查询商品信息接口。为了模拟服务宕机触发降级,所以此时我已经把商品服务关闭了。具体代码如下:

package org.zero.springcloud.order.server.controller;

import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

/**
 * @program: sell_order
 * @description: Hystrix Demo
 * @author: 01
 * @create: 2018-08-28 20:10
 **/
@Slf4j
@RestController
@RequestMapping("/hystrix/demo")
public class HystrixController {

    /**
     * 通过@HystrixCommand注解指定可降级的服务,fallbackMethod参数指向的是回调函数,函数名称可自定义
     *
     * @return String
     */
    @HystrixCommand(fallbackMethod = "fallback")
    @GetMapping("/getProductInfoList")
    public String getProductInfoList() {
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.postForObject("http://127.0.0.1:8519/buyer/product/listForOrder",
                Collections.singletonList("157875196366160022"), String.class);
    }

    /**
     * 触发降级后的回调函数
     *
     * @return String
     */
    public String fallback() {
        return "太拥挤了, 请稍后重试~";
    }
}

启动项目,访问结果如下:

从测试结果可以看到,由于商品服务关闭了,导致无法调用相应的接口。触发了服务降级后,调用了注解中指定的回调函数,并返回了相应的提示。

触发服务降级不一定是服务调用失败,因为服务降级的主要触发原因是抛出了异常,所以只要这个方法中抛出了未被捕获的异常都会触发服务降级。如下示例:

@HystrixCommand(fallbackMethod = "fallback")
@GetMapping("/getProductInfoList")
public String getProductInfoList() {
    throw new RuntimeException("发生了异常");
}

在某些情况下,我们可能只需要定义一个默认的回调处理函数即可,那么我们就可以使用@DefaultProperties注解来定义默认的回调函数,这样就不需要每个 @HystrixCommand 注解都指定一个回调函数了。如下示例:

package org.zero.springcloud.order.server.controller;

import com.netflix.hystrix.contrib.javanica.annotation.DefaultProperties;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;

import java.util.Collections;

/**
 * @program: sell_order
 * @description: Hystrix Demo
 * @author: 01
 * @create: 2018-08-28 20:10
 **/
@Slf4j
@RestController
@RequestMapping("/hystrix/demo")
@DefaultProperties(defaultFallback = "defaultFallback")
public class HystrixController {

    /**
     * 定义了@DefaultProperties后,只需通过@HystrixCommand注解指定可降级的服务即可
     *
     * @return String
     */
    @HystrixCommand
    @GetMapping("/getProductInfoList")
    public String getProductInfoList() {
        RestTemplate restTemplate = new RestTemplate();
        return restTemplate.postForObject("http://127.0.0.1:8519/buyer/product/listForOrder",
                Collections.singletonList("157875196366160022"), String.class);
    }

    /**
     * 触发降级后的回调函数
     *
     * @return String
     */
    public String defaultFallback() {
        return "太拥挤了, 请稍后重试~";
    }
}

超时设置

使用 @HystrixCommand 注解的接口是有一个默认超时时间的,当调用某个服务的耗时超过这个时间也会触发服务降级,默认的超时时间是1秒。我们也可以去自定义这个超时时间,如下示例:

@HystrixCommand(commandProperties = {
        // 设置超时时间为3秒
        @HystrixProperty(name = "execution.isolation.thread.timeoutInMilliseconds", value = "3000")
})
@GetMapping("/getProductInfoList")
public String getProductInfoList() {
    RestTemplate restTemplate = new RestTemplate();
    return restTemplate.postForObject("http://127.0.0.1:8519/buyer/product/listForOrder",
            Collections.singletonList("157875196366160022"), String.class);
}

Hystrix断路器

断路器就像电路中的断路器一样,当短路发生时,它第一时刻熔断,切断了故障电路,保护其他用电单元。

在分布式架构中,断路器的作用类似,当某个服务单元发生了故障,通过断路器的故障监控,直接切断原来的主逻辑调用,强迫以后的多个服务调用不再访问远程服务器,防止应用程序继续执行或等待超时。熔断器也可以监控服务单元的错误是否已经修正,如果已经修正,应用程序会再次尝试调用操作。

在微服务架构中,系统被拆分成了一个个小的服务单元,各自运行在自己的线程中,各单元之间通过注册与订阅的方式互相远程调用,此时若网络故障或是某一服务挂掉则会出现调用延迟,进一步导致调用方的对外服务也出现延迟,如果调用方的请求不断增加,服务单元线程资源无法释放,队列装满,最终导致故障的蔓延,故断路器就是解决这种问题的。

断路器模式:

当Hystrix Command请求后端服务失败数量超过一定比例(默认50%), 断路器会切换到开路状态(Open). 这时所有请求会直接失败而不会发送到后端服务. 断路器保持在开路状态一段时间后(默认5秒), 自动切换到半开路状态(HALF-OPEN). 这时会判断下一次请求的返回情况, 如果请求成功, 断路器切回闭路状态(CLOSED), 否则重新切换到开路状态(OPEN). 即有自我检测并恢复的能力.

代码示例:

@HystrixCommand(commandProperties = {
        @HystrixProperty(name = "circuitBreaker.enabled", value = "true"), // 开启熔断机制
        @HystrixProperty(name = "circuitBreaker.requestVolumeThreshold", value = "10"), // 设置当请求失败的数量达到10个后,打开断路器,默认值为20
        @HystrixProperty(name = "circuitBreaker.sleepWindowInMilliseconds", value = "10000"), // 设置打开断路器多久以后开始尝试恢复,默认为5s
        @HystrixProperty(name = "circuitBreaker.errorThresholdPercentage", value = "60"),  // 设置出错百分比阈值,当达到此阈值后,打开断路器,默认50%
})
@GetMapping("/getProductInfoList")
public String getProductInfoList(@RequestParam("number") Integer number) {
    if (number % 2 == 0) {
        return "success";
    }

    RestTemplate restTemplate = new RestTemplate();
    return restTemplate.postForObject("http://127.0.0.1:8519/buyer/product/listForOrder",
            Collections.singletonList("157875196366160022"), String.class);
}

使用配置项

在代码里写配置可能不太方便维护,我们也可以在配置文件中使用配置项进行配置。例如超时时间配置如下:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000

若指定配置某一个方法的超时时间,将default换成相应方法名即可。如下示例:

hystrix:
  command:
    getProductInfoList:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000

断路器的的配置方式也是一样的,如下示例:

hystrix:
  command:
    default:
      execution:
        isolation:
          thread:
            timeoutInMilliseconds: 3000
      circuitBreaker:
        enabled: true
        requestVolumeThreshold: 10
        sleepWindowInMilliseconds: 10000
        errorThresholdPercentage: 60

feign-hystrix的使用

我们在订单服务中,使用了feign组件去调用商品服务实现服务间的通信。而feign内部已包含了hystrix,所以也可以实现服务降级。首先在订单服务项目的配置文件中,增加如下配置:

feign:
  hystrix:
    enabled: true  # 开启hystrix

到商品服务项目的client模块中,新增一个 ProductClientFallback 类,并实现ProductClient接口。代码如下:

package org.zero.springcloud.product.client;

import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.zero.springcloud.product.common.DecreaseStockInput;
import org.zero.springcloud.product.common.ProductInfoOutput;

import java.util.List;

/**
 * @program: sell_product
 * @description: 触发服务降级时会调用相应的方法
 * @author: 01
 * @create: 2018-08-29 21:39
 **/
@Slf4j
@Component
public class ProductClientFallback implements ProductClient {

    @Override
    public List<ProductInfoOutput> productInfoList(List<String> productIdList) {
        log.info("productInfoList() 触发了服务降级");
        return null;
    }

    @Override
    public void decreaseStock(List<DecreaseStockInput> cartDTOList) {
        log.info("decreaseStock() 触发了服务降级");
    }
}

然后在 ProductClient 接口的@FeignClient注解里增加 fallback 属性,并指定以上编写的实现类。当某个接口触发降级时,就会调用实现类里的方法。代码如下:

package org.zero.springcloud.product.client;

import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.zero.springcloud.product.common.DecreaseStockInput;
import org.zero.springcloud.product.common.ProductInfoOutput;

import java.util.List;

/**
 * @program: sell_order
 * @description: 配置需要调用的接口地址
 * @author: 01
 * @create: 2018-08-19 12:14
 **/
@FeignClient(name = "PRODUCT", fallback = ProductClientFallback.class)
public interface ProductClient {

    /**
     * 调用商品服务-按id查询商品列表
     * 注意,接口地址需要填写完整
     *
     * @param productIdList productIdList
     * @return List<ProductInfo>
     */
    @PostMapping("/buyer/product/listForOrder")
    List<ProductInfoOutput> productInfoList(@RequestBody List<String> productIdList);

    /**
     * 调用商品服务-扣库存
     *
     * @param cartDTOList cartDTOList
     */
    @PostMapping("/buyer/product/decreaseStock")
    void decreaseStock(@RequestBody List<DecreaseStockInput> cartDTOList);
}

编写完以上的代码后,不要忘了安装到maven本地仓库中,安装命令如下:

mvn clean -Dmaven.test.skip=true install

回到订单服务,在启动类上增加@ComponentScan注解,扩大包扫描范围:

package org.zero.springcloud.order.server;

import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;

@SpringCloudApplication
@ComponentScan(basePackages = "org.zero.springcloud")
@EnableFeignClients(basePackages = "org.zero.springcloud.product.client")
public class OrderApplication {

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

重启订单服务项目,访问创建订单接口,如下:

控制台输出如下:

注:此时我已关闭了商品服务,所以才会触发服务降级

如果是超时导致服务降级的话,可以在配置文件中配置feign的超时时间,如下:

feign:
  client:
    config:
      default:
        connectTimeout: 5000
        readTimeout: 5000
        loggerLevel: basic

hystrix-dashboard

hystrix-dashboard是一个可视化的熔断监视工具,我们本小节来看看如何在项目中使用这个工具。我们以订单服务项目为例,首先在pom.xml文件中,增加如下依赖:

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

在启动类中,增加@EnableHystrixDashboard注解。代码如下:

package org.zero.springcloud.order.server;

import org.springframework.boot.SpringApplication;
import org.springframework.cloud.client.SpringCloudApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;
import org.springframework.context.annotation.ComponentScan;

@EnableHystrixDashboard
@SpringCloudApplication
@ComponentScan(basePackages = "org.zero.springcloud")
@EnableFeignClients(basePackages = "org.zero.springcloud.product.client")
public class OrderApplication {

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

在config包下新建一个 HystrixConfig 配置类,用于配置 HystrixMetricsStreamServlet 。代码如下:

package org.zero.springcloud.order.server.config;

import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

/**
 * @program: sell_order
 * @description: 配置HystrixMetricsStreamServlet
 * @author: 01
 * @create: 2018-08-29 22:22
 **/
@Configuration
public class HystrixConfig {
    @Bean
    public HystrixMetricsStreamServlet hystrixMetricsStreamServlet() {
        return new HystrixMetricsStreamServlet();
    }

    @Bean
    public ServletRegistrationBean registration(HystrixMetricsStreamServlet servlet) {
        ServletRegistrationBean<HystrixMetricsStreamServlet> registrationBean = new ServletRegistrationBean<>();
        registrationBean.setServlet(servlet);
        //是否启用该registrationBean
        registrationBean.setEnabled(true);
        registrationBean.addUrlMappings("/hystrix.stream");
        return registrationBean;
    }
}

完成以上代码的编写后,重启项目,访问http://localhost:9080/hystrix,会进入到如下页面中:

通过Hystrix Dashboard主页面的文字介绍,我们可以知道,Hystrix Dashboard共支持三种不同的监控方式:

我这里使用的是单体应用的监控,点击Monitor Stream后,进入到如下页面,在此页面可以看到这个项目的请求信息:

原文地址:http://blog.51cto.com/zero01/2173377

时间: 2024-11-07 15:13:24

Spring Cloud Hystrix - 服务容错的相关文章

笔记:Spring Cloud Hystrix 服务容错保护

由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加,最后就会因等待出现故障的依赖方响应形成任务积压,最终导致自身服务的瘫痪. 在微服务架构中,存在着大量的服务单元,若一个单元出现故障,就很容易因依赖关系而引发故障的蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构更加不稳定,为了解决这样的问题,产生了断路器等一系列的服务保护机制. 在分布式架构中

第五章 服务容错保护:Spring Cloud Hystrix

在微服务架构中,我们将系统拆分为很多个服务,各个服务之间通过注册与订阅的方式相互依赖,由于各个服务都是在各自的进程中运行,就有可能由于网络原因或者服务自身的问题导致调用故障或延迟,随着服务的积压,可能会导致服务崩溃.为了解决这一系列的问题,断路器等一系列服务保护机制出现了. 断路器本身是一种开关保护机制,用于在电路上保护线路过载,当线路中有电器发生短路时,断路器能够及时切断故障电路,防止发生过载.发热甚至起火等严重后果. 在分布式架构中,断路器模式的作用也是类似的. 针对上述问题,Spring

第五章 服务容错保护: Spring Cloud Hystrix

在微服务架构中, 存在着那么多的服务单元, 若一个单元出现故障, 就很容易因依赖关系而引发故障的蔓延,最终导致整个系统的瘫痪,这样的架构相较传统架构更加不稳定.为了解决这样的问题, 产生了断路器等一系列的服务保护机制 Spring Cloud Hystrix实现了断路器. 线程隔离等一系列服务保护功能.它也是基于Netflix的开源框架Hystrix实现的, 该框架的目标在于通过控制那些访问远程系统. 服务和第三方库的节点, 从而对延迟和故障提供更强大的容错能力.Hystrix具备服务降级. 服

SpringCloud---服务容错保护---Spring Cloud Hystrix

1.概述 1.1 在分布式架构中,存在着许多的服务单元,若一个单元出现故障,很容易因依赖关系引发故障的蔓延,最终导致整个系统的瘫痪: 为了解决这样的问题,产生了断路器等服务保护机制: 1.2 分布式架构中,当某个服务单元发生故障之后,通过断路器的故障监控,向调用方返回一个错误响应,而不是长时间等待,这样不会使得线程因调用故障服务被长时间占用不释放,避免了故障在分布式系统中的蔓延: 1.3 Spring Cloud Hystrix实现了断路器.线程隔离等一系列服务保护功能: 基于Netflix的开

微服务架构之spring cloud hystrix&amp;hystrix dashboard

在前面介绍spring cloud feign中我们已经使用过hystrix,只是没有介绍,spring cloud hystrix在spring cloud中起到保护微服务的作用,不会让发生的异常无边界的蔓延下去,很像我们电路中的保险设置,有超压或者线路有问题就即时的断开,保护用电设备不被损坏,这篇文章就来介绍spring cloud hystrix及其hystrix dashboard. (一) 版本说明 a) Spring boot 2.0.6.RELEASE b) Spring clou

在阿里云容器服务上开发基于Docker的Spring Cloud微服务应用

本文为阿里云容器服务Spring Cloud应用开发系列文章的第一篇. 一.在阿里云容器服务上开发Spring Cloud微服务应用(本文) 二.部署Spring Cloud应用示例 三.服务发现 四.服务间通信与集成 五.服务智能路由 六.集中配置管理 七.高可用和容错 八.监控和日志 九.服务的部署和发布策略 微服务概述 单体应用通常指在一个程序中满足多个业务或技术领域的需求,不同的需求领域内化为模块.假定我们要开发一个Web应用,通常的MVC模式可以满足要求.针对不同领域有不少代码生成工具

spring cloud微服务分布式云架构集成项目

Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的.在学习Spring Cloud之前大家必须了解一下相关项目,希望可以帮助到大家. Spring Cloud Config 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储.Git以及Subversion. Spring Cloud Bus 事件.消息总线,用于在集群(例如,配置变化事件)中传

Spring Cloud微服务分布式云架构-集成项目简介

Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的.在学习Spring Cloud之前大家必须了解一下相关项目,希望可以帮助到大家. Spring Cloud Config 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储.Git以及Subversion. Spring Cloud Bus ?事件.消息总线,用于在集群(例如,配置变化事件)中

spring cloud微服务分布式云架构 - Spring Cloud集成项目简介

Spring Cloud集成项目有很多,下面我们列举一下和Spring Cloud相关的优秀项目,我们的企业架构中用到了很多的优秀项目,说白了,也是站在巨人的肩膀上去整合的.在学习Spring Cloud之前大家必须了解一下相关项目,希望可以帮助到大家. Spring Cloud Config 配置管理工具包,让你可以把配置放到远程服务器,集中化管理集群配置,目前支持本地存储.Git以及Subversion. Spring Cloud Bus ?事件.消息总线,用于在集群(例如,配置变化事件)中