1. 什么是雪崩效应?

微服务环境,各服务之间是经常相互依赖的,如果某个不可用,很容易引起连锁效应,造成整个系统的不可用,这种现象称为服务雪崩效应。

如图,引用国外网站的图例:https://www.javatpoint.com/fault-tolerance-with-hystrix#,如图系统各种服务相互调用,一旦一个服务出现问题,假如系统没有熔断器,很容易影响其它模块使用



可用自己画图表示这种情况,如图:A作为服务提供者,B为A的服务消费者,C和D是B的服务消费者。A不可用引起了B的不可用,像滚雪球一样放大到C和D时,雪崩效应就形成了。当然也不一定是服务提供者出现问题,也有可能是消费者出现问题



从两个方面来分析服务雪崩产生的原因:

  • 服务提供者

    服务提供者出现问题,一般都是影响调用的服务消费者,然后造成连锁反应
  • 服务消费者

    服务消费者方面,主要表现在同步调用等待结果导致资源紧张,ps:还有一种特殊情况是,服务既是服务提供者,又是服务消费者

2. 什么是熔断器模式

熔断器(CircuitBreaker),英文是CircuitBreaker,软件设计中的熔断器模式实现,思路是用一个函数调用在断路器保护对象,对故障监控。失败达到一定阈值后,断路器工作,接口调用返回一个错误,以达到保护系统,预防线程资源被大量占用,造成系统雪崩的情况

引用https://martinfowler.com/bliki/CircuitBreaker.html的图例,如图给出了一个简单的软件中的熔断器模式设计方案:

服务的健康状况 = 请求失败数 / 请求总数

ps:熔断器的开关状态转换是通过当前服务健康状况和设定阈值比较决定的

  • 服务健康状况低于设定的阈值时,熔断器开关是关闭的,如果当前服务健康状况大于设置阈值,开关打开
  • 熔断器的开关打开后,所有请求都会被拦截,过一段时间后,开关状态变为半开(half open)
  • 熔断器半开(half open)状态是允许一个请求通过的,当该请求调用成功时, 熔断器恢复到关闭状态.,若该请求失败, 熔断器继续保持打开状态

3. 什么是Netflix Hystrix?

Hystrix 是由 Netflix 发布的针对微服务分布式系统的熔断保护中间件,是一种很好地预防服务雪崩的中间件,其实比较像电路中的保险丝,一旦某个服务不可用,导致暂用了线程资源等情况发生时,熔断器开启,不允许其它服务继续调用,导致系统雪崩

引用官网Wiki的解释:

In a distributed environment, inevitably some of the many service dependencies will fail. Hystrix is a library that helps you control the interactions between these distributed services by adding latency tolerance and fault tolerance logic. Hystrix does this by isolating points of access between the services, stopping cascading failures across them, and providing fallback options, all of which improve your system’s overall resiliency.



中文翻译:在分布式环境中,不可避免的一些很多服务依赖关系将会失败。Hystrix是一个库,可以帮助你控制这些分布式服务之间的交互通过添加延迟宽容和容错逻辑。Hystrix通过孤立点之间的访问服务,停止在级联故障,并提供后备选项,所有这些改善您的系统的整体弹性。

4、Hystrix的工作原理

引用官网wiki的图片,简单介绍Hystrix的工作原理,点此查看大图



Hystrix的工作过程:

  1. 构造一个HystrixCommand或HystrixObservableCommand对象
  2. 执行命令
  3. 响应是否已缓存?
  4. 电路开路了吗?
  5. 线程池队列/信号量是否已满?
  6. HystrixObservableCommand.construct() 或者 HystrixCommand.run()
  7. 计算电路健康
  8. 获取后备
  9. 返回成功的回应
  • 1、构造一个HystrixCommand或HystrixObservableCommand对象

构建一个 HystrixCommand 或者 HystrixObservableCommand 对象,将请求包装到 Command 对象中

  • 2、执行命令

    Hystrix执行命令有如下4种方法:

    ps:前两种仅适用于简单HystrixCommand对象,不适用于HystrixObservableCommand
  • execute() :阻止,然后返回从依赖项接收的单个响应(或在发生错误的情况下引发异常)
  • queue():返回一个Future,您可以从中获得依赖项的单个响应
  • observe():订阅,该Observable代表表示从依赖项返回的响应,并返回Observable复制的。
  • toObservable():返回一个Observable,当您订阅它时,将执行Hystrix命令并发出其响应
K             value   = command.execute();
Future<K> fValue = command.queue();
Observable<K> ohValue = command.observe(); //hot observable
Observable<K> ocValue = command.toObservable(); //cold observable
  • 3、响应是否已缓存?

    判断当前请求是否有缓存,如果在缓存中就直接返回缓存的内容。详情可以参考官方比较详细介绍:https://github.com/Netflix/Hystrix/wiki/How-it-Works#RequestCaching

  • 4、电路开路了吗?

    判断断路器是否处于打开的状态,如果是打开状态,那么 Hystrix 就不再会去执行命令,直接跳到第 8 步,获取 fallback 方法,执行 fallback 逻辑。如果断路器没有打开,那么继续执行

  • 5、线程池队列/信号量是否已满?

    Hystrix的隔离模式有两种:

    • 线程池队列模式
    • 信号量模式

      如果是线程池隔离模式,会判断线程池队列的容量,如果是信号量隔离模式,会判断信号量的值,如果线程池和信号量都已经满了,那么同样请求不会再执行,会直接跳到第 8 步(fallback过程),如果未满那么继续执行
  • 6、HystrixObservableCommand.construct() 或者 HystrixCommand.run()

    在这里,Hystrix通过如下方法调用对依赖项的请求,有两种方法,其中一种执行:

    • HystrixCommand.run():返回一个响应或者抛出一个异常   
    • HystrixObservableCommand.construct():返回一个可观测的,发出响应(s)或发送一个onError通知
  • 7、计算电路健康

    Hystrix向断路器报告成功,失败,拒绝和超时,断路器保持滚动的一组计算统计信息,它使用这些统计信息来确定电路何时应“跳闸”,在该时间点它会将随后的所有请求短路,直到经过恢复期为止,在此之后,在首先检查某些运行状况检查之后,情况正常,电路会再次闭合

  • 8、获取后备

    所谓的获取后备,其实就是系统发生异常时,执行后备函数,也就是fallback操作,Hystrix尝试在命令执行失败时恢复到您的后备状态:当construct()或引发异常run()(6.),由于电路断开而使命令短路(4.),命令的线程池和队列或信号量为最大容量(5.),或者命令已超过其超时长度。

  • 9、返回成功的响应

    如果Hystrix命令成功执行,它将以的形式将一个或多个响应返回给调用方Observable,官方图例说明:

5、Hystrix的设计原则

ok,接着归纳一下Hystrix的主要设计原则,或者特征,参考官方的wiki,我们可以看到Hystrix的一些主要特征

  • 封装请求

    Hystrix封装请求由 HystrixCommand 或者 HystrixObservableCommand 类实现,将请求包装到 Command 对象中,接着执行,主要4种方法
K             value   = command.execute();
Future<K> fValue = command.queue();
Observable<K> ohValue = command.observe(); //hot observable
Observable<K> ocValue = command.toObservable(); //cold observable
  • 资源隔离

    资源隔离减少风险的方式被称为:Bulkheads(舱壁隔离模式)

引用https://segmentfault.com/a/1190000005988895的图例:



在Hystrix软件设计中也是基于这种设计理念,舱壁隔离模式。Hystrix的隔离模式有两种: 线程池队列模式、信号量模式

  • 熔断器模式

    Hystrix采用了熔断器模式,相当于电路中的保险丝,系统出现紧急问题,立刻禁止所有请求,已达到保护系统的作用

  • 命令模式

    Hystrix使用命令模式(继承HystrixCommand类)来实现具体的服务调用逻辑(run方法), 并在命令模式中添加了服务调用失败后的fallback逻辑,这是命令模式的很好应用

  • 要求折叠

通过实现HystrixCollapser类,实现这种场景,可以将多个请求折叠到单个后端依赖项调用

引用官网图片,下图显示了两种情况下的线程和网络连接数:首先是没有连接,然后是请求折叠

  • 请求缓存

    HystrixCommand和HystrixObservableCommand实现可以定义一个缓存键然后请求中用于de-dupe调用上下文concurrent-aware的方式

6、Netflix Hystrix例子实践

Hystrix常被应用于微服务项目中,feign、ribbon等中间件都有默认集成,本例子基于spring cloud进行实践

环境准备:

  • JDK 1.8
  • SpringBoot2.2.3
  • SpringCloud(Hoxton.SR6)
  • Maven 3.2+
  • 开发工具
    • IntelliJ IDEA
    • smartGit

创建一个SpringBoot Initialize项目,详情可以参考我之前博客:SpringBoot系列之快速创建项目教程

可以引入Eureka Discovery Client



Eureka Discovery Client默认集成spring-cloud-netflix-hystrix

不加上Eureka Discovery Client的情况,需要自己单独添加Hystrix

Hoxton.SR6版本不支持@HystrixCommand?所以需要自己加上配置:

 <dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-javanica</artifactId>
<version>RELEASE</version>
</dependency>

本博客的是基于spring-cloud-starter-netflix-eureka-client进行试验,试验前要运行eureka服务端,eureka服务提供者,代码请参考上一章博客



加上@EnableCircuitBreaker支持服务降级

package com.example.springcloud.hystrix;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.RestController; @SpringBootApplication
@EnableCircuitBreaker
@EnableEurekaClient
public class SpringcloudHystrixApplication { public static void main(String[] args) {
SpringApplication.run(SpringcloudHystrixApplication.class, args);
} }

新建bootstrap.yml,yaml配置:

server:
port: 8082
# 必须指定application name
spring:
application:
name: ribbon-hystrix-service-consumer
eureka:
client:
service-url:
defaultZone: http://localhost:8761/eureka/
healthcheck:
enabled: false
# 支持服务发现
fetch-registry: true
# 不支持服务注册
register-with-eureka: false
instance:
status-page-url-path: http://localhost:8761/actuator/info
health-check-url-path: http://localhost:8761/actuator/health
prefer-ip-address: false
instance-id: ribbon-hystrix-service-consumer8082
metadata-map:
cluster: ribbon

@LoadBalanced支持负载均衡:

package com.example.springcloud.hystrix.configuration;

import com.netflix.ribbon.proxy.annotation.Hystrix;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.client.RestTemplate; /**
* <pre>
* RestConfiguration
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/07/31 09:43 修改内容:
* </pre>
*/
@Configuration
public class RestConfiguration {
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
}

User.java:

package com.example.springcloud.hystrix.bean;

import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import lombok.Data; import java.io.Serializable; /**
* <pre>
* User
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/07/27 17:38 修改内容:
* </pre>
*/
@JsonIgnoreProperties(ignoreUnknown = true)
@Data
public class User implements Serializable { private String name;
private String blog; @Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", blog='" + blog + '\'' +
'}';
}
}

@HystrixCommand(fallbackMethod = "userApiFallback")指定异常后的回调方法

package com.example.springcloud.hystrix.controller;

import com.example.springcloud.hystrix.bean.User;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.client.RestTemplate; /**
* <pre>
* RestController
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/08/01 16:59 修改内容:
* </pre>
*/
@org.springframework.web.bind.annotation.RestController
@Slf4j
public class RestController { @Autowired
RestTemplate restTemplate; /**
* @HystrixCommand注解指定异常时调用的方法
* @Author mazq
* @Date 2020/08/01 18:17
* @Param [username]
* @return
*/
@GetMapping("/findUser/{username}")
@HystrixCommand(fallbackMethod = "userApiFallback")
public User index(@PathVariable("username")String username){
return restTemplate.getForObject("http://EUREKA-SERVICE-PROVIDER/api/users/"+username,User.class);
} public User userApiFallback(String username) {
log.info("fallback方法,接收的参数:username = {}",username);
User user = new User();
user.setName("defaultUser");
user.setBlog("https://smilenicky.blog.csdn.net");
return user;
} }



7、Feign项目使用Hystrix

pom配置:

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

openfeign中间件是默认集成Hystrix的,所以主要fallback参数指定具体实现类既可

package com.example.springcloud.hystrix.component;

import com.example.springcloud.hystrix.bean.User;
import org.springframework.cloud.openfeign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod; @FeignClient(name = "eureka-service-provider", fallback = FeignHystrixClientFallback.class)
public interface FeignHystrixClient {
@RequestMapping(value = "/api/users/{username}",method = RequestMethod.GET)
User findGithubUser(@PathVariable("username") String username);
}

实现FeignHystrixClient 接口

package com.example.springcloud.hystrix.component;

import com.example.springcloud.hystrix.bean.User;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component; /**
* <pre>
* FeignHystrixClientFallback
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/08/03 09:58 修改内容:
* </pre>
*/
@Slf4j
@Component
public class FeignHystrixClientFallback implements FeignHystrixClient { @Override
public User findGithubUser(String username) {
log.info("fallback方法,接收的参数:username = {}",username);
User user = new User();
user.setName("defaultUser");
user.setBlog("https://smilenicky.blog.csdn.net");
return user;
}
}

8、Hystrix dashboard监控

Hystrix dashboard提供了对微服务模块进行监控的功能



maven配置

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

@EnableHystrixDashboard加在Application类上,开启Hystrix dashboard监控

允许后,访问,http://localhost:8082/hystrix,格式为http://localhost:port/hystrix



spring-boot-starter-actuator其实就已经有提供监控的,链接http://localhost:8082/actuator/hystrix.stream,Hystrix dashboard其实是对这些数据进行界面可视化监控,所以项目要先集成spring-boot-starter-actuator

ps:spring-boot-starter-actuator 2.2.3版本要加上actuator前端,才能访问,网上很多教程都是基于之前版本,不需要加上,而本博客基于2.2.3 SpringBoot 版本



ok,接着发现2.2.3版本的bug,f12调试,发现前端报错,导致Hystrix dashboard监控页面一直loading

找到github issue:https://github.com/MadeInChina/spring-cloud-netflix/commit/afc1d989767d0a21524b865dafeebc37d4c78e04,处理方法是反编译jar,找到如图文件,修改,然后再放回jar



这种比较麻烦,或许可以回退一下版本,用回2.2.2版本,maven配置:

 <dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
<exclusions>
<exclusion>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-netflix-hystrix-dashboard</artifactId>
<version>2.2.2.RELEASE</version>
</dependency>

jquery版本换成2.1.1



ok,还要加上如下配置

package com.example.springcloud.hystrix.configuration;

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; /**
* <pre>
*
* </pre>
*
* <pre>
* @author mazq
* 修改记录
* 修改后版本: 修改人: 修改日期: 2020/08/04 16:19 修改内容:
* </pre>
*/
@Configuration
public class WebConfiguration { @Bean
public ServletRegistrationBean getServlet() {
HystrixMetricsStreamServlet streamServlet = new HystrixMetricsStreamServlet();
ServletRegistrationBean registrationBean = new ServletRegistrationBean(streamServlet);
registrationBean.setLoadOnStartup(1);
registrationBean.addUrlMappings("/actuator/hystrix.stream");
registrationBean.setName("HystrixMetricsStreamServlet");
return registrationBean;
}
}

ok,处理好bug,页面输入http://localhost:8082/actuator/hystrix.stream,我们监控8082这种微服务模块的情况,接口调用都正常





ps:页面输入http://localhost:8082/actuator/hystrix.stream,delay,title可以不填,Hystrix还支持turbine进行集群监控,后续有时间可以写博客补充

附录:

ok,本博客参考官方教程进行实践,仅仅作为入门的学习参考资料,详情可以参考Spring Cloud官方文档:https://docs.spring.io/spring-cloud-netflix/docs/2.2.x-SNAPSHOT/reference/html/#circuit-breaker-spring-cloud-circuit-breaker-with-hystrix,Hystrix官网:https://github.com/Netflix/Hystrix/wiki/How-it-Works

代码例子下载:code download

优质学习资料参考:

SpringCloud系列之服务容错保护Netflix Hystrix的更多相关文章

  1. 【Spring Cloud】服务容错保护:Hystrix(四)

    一.雪崩效应 在微服务架构中,由于服务和服务之间可以互相调用,一项工作的完成可能会依赖调用多个微服务模块,但由于网络原因或者自身的原因,服务并不能保证100%可用,如果单个服务出现问题,调用这个服务就 ...

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

    前言 前一章节,我们知道了如何利用RestTemplate+Ribbon和Feign的方式进行服务的调用.在微服务架构中,一个服务可能会调用很多的其他微服务应用,虽然做了多集群部署,但可能还会存在诸如 ...

  3. Spring Cloud(四):服务容错保护 Hystrix【Finchley 版】

    Spring Cloud(四):服务容错保护 Hystrix[Finchley 版]  发表于 2018-04-15 |  更新于 2018-05-07 |  分布式系统中经常会出现某个基础服务不可用 ...

  4. spring cloud 入门系列四:使用Hystrix 实现断路器进行服务容错保护

    在微服务中,我们将系统拆分为很多个服务单元,各单元之间通过服务注册和订阅消费的方式进行相互依赖.但是如果有一些服务出现问题了会怎么样? 比如说有三个服务(ABC),A调用B,B调用C.由于网络延迟或C ...

  5. Spring Cloud (8) 服务容错保护-Hystrix依赖隔离

    依赖隔离 docker使用舱壁模式来实现进程的隔离,使容器与容器之间不会互相影响.而Hystrix则使用该模式实现线程池的隔离,它会为每一个Hystrix命令创建一个独立的线程池,这样就算在某个Hys ...

  6. SpringCould-------使用Hystrix 实现断路器进行服务容错保护

    消费: <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.or ...

  7. SpringCloud系列——Feign 服务调用

    前言 前面我们已经实现了服务的注册与发现(请戳:SpringCloud系列——Eureka 服务注册与发现),并且在注册中心注册了一个服务myspringboot,本文记录多个服务之间使用Feign调 ...

  8. Spring Cloud Hystrix 服务容错保护 5.1

    Spring Cloud Hystrix介绍 在微服务架构中,通常会存在多个服务层调用的情况,如果基础服务出现故障可能会发生级联传递,导致整个服务链上的服务不可用为了解决服务级联失败这种问题,在分布式 ...

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

    在微服务架构中,我们将系统拆分为很多个服务,各个服务之间通过注册与订阅的方式相互依赖,由于各个服务都是在各自的进程中运行,就有可能由于网络原因或者服务自身的问题导致调用故障或延迟,随着服务的积压,可能 ...

随机推荐

  1. MySQL后记

    MySQL后记 这篇博客的目的是记录一些容易被忽略的MySQL的知识点,以及部分pymysql模块的注意点. MySQL中的DDL与DML DDL:数据定义语言(Data Definition Lan ...

  2. python eval函数,将列表样式的字符串转化为列表

    python eval函数,将列表样式的字符串转化为列表 >>> str_1 = '[1,2,3,4,5,6]'>>> type(str_1)<type 's ...

  3. celery 基础教程(一):工作流程,架构以及概念

    1.工作流程 celery通过消息进行通信,通常使用一个叫Broker(中间人)来协client(任务的发出者)和worker(任务的处理者). clients发出消息到队列中,broker将队列中的 ...

  4. 数据可视化之DAX篇(四) 熟练使用EARLIER函数,轻松获取当前行信息

    https://zhuanlan.zhihu.com/p/64400583 前面利用PowerBI做数据分析的时候都是对整列的字段进行操作,并没有做更细化的分析,比如分析数据的每一行.提取某一行的数据 ...

  5. 循序渐进VUE+Element 前端应用开发(16)--- 组织机构和角色管理模块的处理

    在前面随笔<循序渐进VUE+Element 前端应用开发(15)--- 用户管理模块的处理>中介绍了用户管理模块的内容,包括用户列表的展示,各种查看.编辑.新增对话框的界面处理和后台数据处 ...

  6. 011.Nginx防盗链

    一 盗链 1.1 盗链概述 盗链指的是在自己的界面展示非本服务器上的内容,通过技术手段获得其他服务器的资源.绕过他人资源展示页面,在自己页面向用户提供此内容,从而减轻自己服务器的负担,因为真实的空间和 ...

  7. layui :iframe 与 layer 的位置问题

    最近有个项目是用 Layui 搭的,但是在layer的使用上遇到了问题. 简单的说,在父页面声明layer,在子页面中的子页面用window.parent调用那个父页面的layer. 讲道理应该是和i ...

  8. 【mysql】- 索引简介篇

    简介 我们都知道mysql使用存储引擎的是InnoDB,InnoDB使用的索引的对应的数据结构是B+树 结构图: 如上图所示,我们实际用户记录是存放在B+树的最底层的节点上,这些节点也被称为叶子节点或 ...

  9. jmeter接口测试 -- Base64加密(函数助手添加自定义函数)

    图片转码 base64 致谢参考博客: https://www.cnblogs.com/qiaoyeye/p/7218770.html https://www.cnblogs.com/lasdaybg ...

  10. control+B进入layout文件的xml文本编辑模式

    control+B进入layout文件的xml文本编辑模式