在现在的微服务使用的过程中,经常会遇到依赖的服务不可用,那么如果依赖的服务不可用的话,会导致把自己的服务也会拖死,那么就产生了熔断,熔断顾名思义就是当服务处于不可用的时候采取半开关的状态,达到一定数量后就熔断器就打开。这就相当于家里边的保险丝,如果电压过高的话,保险丝就会断掉,起到保护电器的作用。

  目前支持熔断,降级的就是Hystrix,当然还有resilience4j还有Sentinel。今天咱们以Hystrix为主吧。其他的大家可以自行研究。

  Hystrix主要实现三个功能,接下来咱们继续展开。

  1. 资源隔离

  2. 熔断

  3. 降级

  资源隔离分为两种,一种是线程池隔离,一种是信号量semaphore隔离。线程池以请求的线程和执行的线程分为不同的线程执行,信号量是请求和执行采用相同的线程。

  当然,涉及到线程池的话,那么就支持异步,支持异步Future的话也就支持get的时候支持超时获取。信号量这些功能不支持,但是信号量是支持熔断,限流。他们的区别如下:

  线程切换 异步 超时 熔断 限流 资源开销
线程池
信号量

  HystrixCommand的命令执行大致如下图:

  依赖的pom如下:

        <!-- 依赖版本 -->
<hystrix.version>1.3.16</hystrix.version>
<hystrix-metrics-event-stream.version>1.1.2</hystrix-metrics-event-stream.version> <dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-core</artifactId>
<version>${hystrix.version}</version>
</dependency>
<dependency>
<groupId>com.netflix.hystrix</groupId>
<artifactId>hystrix-metrics-event-stream</artifactId>
<version>${hystrix-metrics-event-stream.version}</version>
</dependency>

  支持同步,异步,观察事件拦截,以及订阅方式,下面咱们直接看代码实现吧。大家一看就明白了:

import com.netflix.hystrix.HystrixCommand;
import com.netflix.hystrix.HystrixCommandGroupKey;
import rx.Observable;
import rx.Subscriber;
import rx.functions.Action1; import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException; /**
* @author huangqingshi
* @Date 2019-03-17
*/
public class HelloWorldCommand extends HystrixCommand<String> { private final String name; public HelloWorldCommand(String name) {
//指定命令组名
super(HystrixCommandGroupKey.Factory.asKey("myGroup"));
this.name = name;
} @Override
protected String run() throws Exception {
//逻辑封装在run里边
return "Hello:" + name + " thread:" + Thread.currentThread().getName();
} public static void main(String[] args) throws InterruptedException, ExecutionException, TimeoutException {
//每个Command只能调用一次,不能重复调用。重复调用会报异常。
HelloWorldCommand helloWorldCommand = new HelloWorldCommand("Synchronous-hystrix");
//execute同步调用 等同于:helloWorldCommand.queue().get();
String result = helloWorldCommand.execute();
System.out.println("result:" + result); helloWorldCommand = new HelloWorldCommand("Asynchronous-hystrix");
//异步调用
Future<String> future = helloWorldCommand.queue();
//get可以指定获取的时间100毫秒,默认为1秒
result = future.get(100, TimeUnit.MILLISECONDS);
System.out.println("result:" + result);
System.out.println("main thread:" + Thread.currentThread().getName()); testObserve(); } public static void testObserve() {
//注册观察者事件拦截
Observable<String> observable = new HelloWorldCommand("observe").observe();
//注册回调事件
observable.subscribe(new Action1<String>() {
@Override
public void call(String result) {
//result就是调用HelloWorldCommand的结果
System.out.println("callback:" + result);
}
});
//注册完成版的事件
observable.subscribe(new Subscriber<String>() {
@Override
public void onCompleted() {
System.out.println("onCompleted调用:onNext : onError之后调用");
} @Override
public void onError(Throwable throwable) {
//异常产生了之后会调用
System.out.println("onError:" + throwable.getMessage());
} @Override
public void onNext(String s) {
//获取结果后回调
System.out.println("onNext:" + s);
}
});
} }
执行结果如下:

result:Hello:Synchronous-hystrix thread:hystrix-myGroup-1
result:Hello:Asynchronous-hystrix thread:hystrix-myGroup-2
main thread:main
callback:Hello:observe thread:hystrix-myGroup-3
onNext:Hello:observe thread:hystrix-myGroup-3
onCompleted调用:onNext : onError之后调用

  接下来是线程池隔离的例子:

import com.netflix.hystrix.*;

/**
* @author huangqingshi
* @Date 2019-03-17
*/
public class ThreadPoolCommand extends HystrixCommand<String> { private String name; public ThreadPoolCommand(String name) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("threadPoolGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("threadPoolCommand"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(10) //至少10个请求,熔断器才进行错误计算 默认值20
.withCircuitBreakerSleepWindowInMilliseconds(5000) //熔断终端5秒后会进入半打开状态
.withCircuitBreakerErrorThresholdPercentage(50) //错误率达到50开启熔断保护
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
//10个核心线程
).andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10))
);
this.name = name;
} @Override
protected String run() throws Exception {
return "threadPoolCommand name:" + name;
} public static void main(String[] args) {
ThreadPoolCommand threadPoolCommand = new ThreadPoolCommand("threadPool");
String result = threadPoolCommand.execute();
System.out.println("result:" + result);
}
} 执行结果:
result:threadPoolCommand name:threadPool

  信号量隔离例子:

/**
* @author huangqingshi
* @Date 2019-03-17
*/
public class SemaphoreCommand extends HystrixCommand<String> { private String name; public SemaphoreCommand(String name) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("semaphoreGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("semaphoreCommand"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
//至少10个请求,熔断器才会进行错误率的计算 默认值20
.withCircuitBreakerRequestVolumeThreshold(10)
//熔断器中断请求5秒后会自动进入半打开状态,放部分流量进行重试 默认值5000ms
.withCircuitBreakerSleepWindowInMilliseconds(5000)
//错误率达到50开启熔断保护
.withCircuitBreakerErrorThresholdPercentage(50)
//设置隔离策略
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
//最大并发量10
.withExecutionIsolationSemaphoreMaxConcurrentRequests(10)
)
);
this.name = name;
} @Override
protected String run() throws Exception {
return "semaphore success name:" + name;
} @Override
protected String getFallback() {
return "semaphore fallback name:" + name;
} public static void main(String[] args) {
SemaphoreCommand semaphoreCommand = new SemaphoreCommand("semaphoreCommand");
String result = semaphoreCommand.execute();
System.out.println(result);
} }
执行结果:
semaphore success name:semaphoreCommand

  在执行的过程中,如果出现调用服务的时候出现错误的时候会先进行熔断,就是如果流量达到设置的量的时候进行统计,比如10个请求,然后如果出现错误率超过配置的错误率就会进行将熔断进行打开,打开之后会进行调用降级方法fallback。过了一段时间后,可以放行部分流量,如果流量正常了,则会将熔断器开关关闭。下图是来自官方文档截图,里边维护者一个bucket,每秒一个bucket,里边记录着成功,失败,超时,拒绝。这个周期是通过withCircuitBreakerSleepWindowInMilliseconds配置的。

  接下来咱们看一下降级,也就是熔断器打开的时候,会走fallback方法,继续看例子。

import com.netflix.hystrix.*;

/**
* @author huangqingshi
* @Date 2019-03-17
*/
public class ThreadPoolCommand extends HystrixCommand<String> { private String name; public ThreadPoolCommand(String name) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("threadPoolGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("threadPoolCommand"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter()
.withCircuitBreakerRequestVolumeThreshold(10) //至少10个请求,熔断器才进行错误计算 默认值20
.withCircuitBreakerSleepWindowInMilliseconds(5000) //熔断终端5秒后会进入半打开状态
.withCircuitBreakerErrorThresholdPercentage(50) //错误率达到50开启熔断保护
.withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)
//10个核心线程
).andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10))
);
this.name = name;
} @Override
protected String run() throws Exception {
return "threadPoolCommand name:" + name;
} public static void main(String[] args) {
ThreadPoolCommand threadPoolCommand = new ThreadPoolCommand("threadPool");
String result = threadPoolCommand.execute();
System.out.println("result:" + result);
}
}
执行结果:
result:executed fallback
并且抛出超时异常。因为程序故意设计超时的。

  当然,Hystrixcommand还支持primary或secondary的方式,可以先看看流程图:

  是否执行primary是通过参数primarySecondary.userPrimary为true时执行。false的时候执行secondary方式。

/**
* @author huangqingshi
* @Date 2019-03-18
*/
public class PrimarySecondaryFacade extends HystrixCommand<String> { private final static DynamicBooleanProperty usePrimary = DynamicPropertyFactory.getInstance().
getBooleanProperty("primarySecondary.usePrimary", true); private int id; public PrimarySecondaryFacade(int id) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("facadeGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("primarySecondCommand"))
//此处采用信号量,primary、secondary采用线程池
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().withExecutionIsolationStrategy(
HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE)
) );
this.id = id;
} @Override
protected String run() throws Exception {
if(usePrimary.get()) {
return new PrimaryCommand(id).execute();
} else {
return new SecondaryCommand(id).execute();
}
} @Override
protected String getFallback() {
return "static-fallback-" + id;
} @Override
protected String getCacheKey() {
return String.valueOf(id);
} private static class PrimaryCommand extends HystrixCommand<String> { private final int id; private PrimaryCommand(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("facadeGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("PrimaryCommand"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("PrimaryCommand"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().
withExecutionIsolationThreadTimeoutInMilliseconds(600)));
this.id = id;
} @Override
protected String run() {
return "responseFromPrimary-" + id;
} } private static class SecondaryCommand extends HystrixCommand<String> { private final int id; private SecondaryCommand(int id) {
super(Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey("facadeGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("SecondaryCommand"))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("SecondaryCommand"))
.andCommandPropertiesDefaults(HystrixCommandProperties.Setter().
withExecutionIsolationThreadTimeoutInMilliseconds(600)));
this.id = id;
} @Override
protected String run() {
return "responseFromSecondary-" + id;
} } public static class UnitTest { @Test
public void testPrimary() {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", true);
assertEquals("responseFromPrimary-100", new PrimarySecondaryFacade(100).execute());
} finally {
context.shutdown();
ConfigurationManager.getConfigInstance().clear();
}
} @Test
public void testSecondary() {
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
ConfigurationManager.getConfigInstance().setProperty("primarySecondary.usePrimary", false);
assertEquals("responseFromSecondary-100", new PrimarySecondaryFacade(100).execute());
} finally {
context.shutdown();
ConfigurationManager.getConfigInstance().clear();
}
}
} }

  好了,这个基本上就是Hystrix的基本功能,但是有个问题就是Hystrix已经不维护了,但是目前的稳定版大家也都在使用,所以列出来了。当然也推荐大家使用Sentinel,功能比较强大,就是自适应限流功能等,功能也非常强大,后续研究之后再出相关文章吧。这个文章就当大家的一个敲门砖吧,有问题请及时告知,谢谢。

  

聊聊微服务熔断降级Hystrix的更多相关文章

  1. 【5】JMicro微服务-熔断降级

    如非授权,禁止用于商业用途,转载请注明出处作者:mynewworldyyl   1. 使用服务熔断降级特性,必须先启动Pubsub服务,服务监听服务,熔断器服务3个服务 先启动Pubsub及服务监听两 ...

  2. 微服务熔断限流Hystrix之流聚合

    简介 上一篇介绍了 Hystrix Dashboard 监控单体应用的例子,在生产环境中,监控的应用往往是一个集群,我们需要将每个实例的监控信息聚合起来分析,这就用到了 Turbine 工具.Turb ...

  3. 从 Spring Cloud 开始,聊聊微服务架构实践之路

    [编者的话]随着公司业务量的飞速发展,平台面临的挑战已经远远大于业务,需求量不断增加,技术人员数量增加,面临的复杂度也大大增加.在这个背景下,平台的技术架构也完成了从传统的单体应用到微服务化的演进. ...

  4. SpringCloud微服务(03):Hystrix组件,实现服务熔断

    本文源码:GitHub·点这里 || GitEE·点这里 写在前面:阅读本文前,你可能需要熟悉一下内容. 微服务组件:Eureka管理注册中心 微服务组件:Ribbon和Feign服务调用 Sprin ...

  5. dubbo学习实践(4)之Springboot整合Dubbo及Hystrix服务熔断降级

    1. springboot整合dubbo 在provider端,添加maven引入,修改pom.xml文件 引入springboot,版本:2.3.2.RELEASE,dubbo(org.apache ...

  6. Spring Boot + Spring Cloud 实现权限管理系统 后端篇(二十):服务熔断(Hystrix、Turbine)

    在线演示 演示地址:http://139.196.87.48:9002/kitty 用户名:admin 密码:admin 雪崩效应 在微服务架构中,由于服务众多,通常会涉及多个服务层级的调用,而一旦基 ...

  7. 利用Spring Cloud实现微服务- 熔断机制

    1. 熔断机制介绍 在介绍熔断机制之前,我们需要了解微服务的雪崩效应.在微服务架构中,微服务是完成一个单一的业务功能,这样做的好处是可以做到解耦,每个微服务可以独立演进.但是,一个应用可能会有多个微服 ...

  8. SpringCloud 中集成Sentinel+Feign实现服务熔断降级

    Sentine 1.背景 Sentinel 是阿里中间件团队开源的,面向分布式服务架构的轻量级高可用流量控制组件,主要以流量为切入点,从流量控制.熔断降级.系统负载保护等多个维度来帮助用户保护服务的稳 ...

  9. SpringCloud Zuul2.X网关实现服务熔断降级(复制即用)

    版本: <properties> <spring-boot.version>.RELEASE</spring-boot.version> <spring-cl ...

随机推荐

  1. Confluence 6 附件存储配置

    在默认的情况下 Confluence 的附件存储在 home 目录中(例如,在文件系统). 希望对 Confluence 的附件存储进行配置: 在屏幕的右上角单击 控制台按钮 ,然后选择 Genera ...

  2. NIO(四)

    使用非直接缓冲区和直接缓冲区复制同一个文件,看一下时间差别 1.创建非直接缓冲区测试类 package com.cppdy.nio; import java.io.FileInputStream; i ...

  3. 【python】python为何多线程无法切换

    写了一个kafka传输消息,celery发布任务的脚本. 有四个线程,分别读取不同的kafka队列信息 问题是,只有第一个线程会启动,剩下的三个线程连start都运行不了. 而且这个问题不是一开始就发 ...

  4. uva11916 bsgs算法逆元模板,求逆元,组合计数

    其实思维难度不是很大,但是各种处理很麻烦,公式推导到最后就是一个bsgs算法解方程 /* 要给M行N列的网格染色,其中有B个不用染色,其他每个格子涂一种颜色,同一列上下两个格子不能染相同的颜色 涂色方 ...

  5. bzoj 4007

    非常好的树形dp 首先,有个很显然的状态:记状态f[i][j]表示以i为根节点的子树中选了j个叶节点作战,那么很显然有转移:f[i][j1+j2]=f[i<<1][j1]+f[i<& ...

  6. Git使用四:查看工作状态和历史提交

    查看当前的工作状态:git status On branch master:现在位于master分支里面nothing to commit, working tree clean:没有需要提交的文件, ...

  7. Linux基础三:linux目录结构和目录文件的浏览、管理及维护

    目录文件的浏览.管理及维护(一) 1.Linux文件系统的层次结构 1)Linux文件系统的树状结构:在Linux或UNIX操作系统中,所有的文件和目录都被组织成一个以根节点开始的倒置的树状结构. 2 ...

  8. Linux基础一:Linux的安装及相关配置

    1. 计算机操作系统简介    1) 操作系统的定义:操作系统是一个用来协调.管理和控制计算机硬件和软件资源的系统程序,它位于硬件和应用程序之间.    2) 操作系统的内核的定义:操作系统的内核是一 ...

  9. Java和C冒泡排序

    Java 示例代码: public class test { public static void main(String[] args) { String str = "321dca5&q ...

  10. zabbix 3.2.4 使用详解

    一:zabbix简介及原理 二:zabbix添加主机: /usr/share/zabbix/include/locales.inc.php   #这里为zabbix语言包路径‘zh_CN’ 为true ...