特性

1.延迟和失败容忍

防止级联错误,错误回退,优雅降级。快速失败和恢复

线程和信号量隔离

2.实时监控和配置更改

3.并发

并行执行,请求缓存,自动批处理失败请求

总运行流程

当你发出请求后,hystrix是这么运行的

 详细解释个步骤

1. Construct a HystrixCommand or HystrixObservableCommand Object

HystrixCommand

用于返回单一的响应
HystrixObservableCommand
用于返回多个可自定义的响应
 

2. Execute the Command

对于HystrixCommand有4个执行方法
对于HystrixObservableCommand只有后两个
//阻塞方法,其实就是调用了queue().get()
execute() — blocks, then returns the single response received from the dependency (or throws an exception in case of an error) //非阻塞方法,直接返回Future,可以先做自己的事情,做完再.get()
queue() — returns a Future with which you can obtain the single response from the dependency //热观察,可以被立即执行,如果订阅了那么会重新通知,其实就是调用了toObservable()并内置ReplaySubject,详细可以参考RxJava
observe() — subscribes to the Observable that represents the response(s) from the dependency and returns an Observable that replicates that source Observable //冷观察,返回一个Observable对象,当调用此接口,还需要自己加入订阅者,才能接受到信息,详细可以参考RxJava
toObservable() — returns an Observable that, when you subscribe to it, will execute the Hystrix command and emit its responses 注:由于Hystrix底层采用了RxJava框架开发,所以没接触过的可能会一脸懵逼,需要再去对RxJava有所了解。
 

3. Is the Response Cached?

如果请求缓存可用,并且对于该请求的响应也在缓存中,那么命中的响应会以Observable直接返回
下图关于是请求缓存的整个生命周期


4. Is the Circuit Open?

执行command,hystrix会检查circuit是否打开,如果是打开的(失败率超过阈值)那么直接快速失败,否则进入下一流程
 

5. Is the Thread Pool/Queue/Semaphore Full?

线程池或者信号量是否已经满负荷,如果已经满负荷那么快速失败
 

6. HystrixObservableCommand.construct() or HystrixCommand.run()

两个断路器的入口,如果是继承HystrixObservableCommand,那么就调用construct()函数,如果是继承HystrixCommand,那么就调用run()函数。

7. Calculate Circuit Health

Hystrix记录了成功,失败,拒绝,超时四种报告

这些报告用于决定哪些用于断路,被断路的点在恢复周期内无法被后来的请求访问到。

8. Get the Fallback

快速失败会在以下几个场景触发

1.由construct() or run()抛出了一个异常

2.断路器已经打开的时候

3.没有空闲的线程池和队列或者信号量

4.一次命令执行超时

可以重写快速失败函数来自定义,

HystrixObservableCommand.resumeWithFallback()

HystrixCommand.getFallback()

9. 成功返回

整体的函数调用流程如下,其实这就是源码的调用流程

Coding

原生模式

基于hystrix的原生接口,也就是继承HystrixCommand或者HystirxObservableCommand。

public static class HelloHystrixCommand extends HystrixCommand<String>{

    public HelloHystrixCommand() {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
} /**
* 实际操作的函数
* @return
* @throws Exception
*/ public String run() throws Exception {
Thread.sleep(1500);
return "hello hystrix";
} /**
* 快速失败后调用函数
* @return
*/ protected String getFallback(){
return "404 :)";
} }
注解模式
利用netflix的开源框架javanica,在需要使用断路器的方法上加上注解,参考代码如下
@Autowired
private RestTemplate restTemplate; /**
* 使用断路器的方法
* 其中fallback是快速失败的调用函数
* @return
*/
@HystrixCommand(fallbackMethod = "fallback")
public String post2AnotherService(){
HttpHeaders headers = new HttpHeaders();
headers.set("Content-Type", "application/json;charset=UTF-8");
HttpEntity<Object> formEntity = new HttpEntity<Object>(headers); //发送post请求
String res = restTemplate.postForObject("http://template/template/hello_world?",formEntity,String.class);
return res;
} /**
* 当post2AnotherService函数出现问题,则调用此函数
* @return
*/
public String fallback(){
return "404 :)";
}

当调用post2AnotherService()的时候,由于函数返回超过规定时间默认是1s,就会执行fallback()函数,并返回。值得注意的是,如果对于fallback()也想使用降级机制,那么也可以加上@HystrixCommand

关于断路器

工作流程图

 

开关条件

关于断路器打开

·时间窗口内请求次数(限流)

如果在10s内,超过某个阈值的请求量,才会考虑断路(小于这个次数不会被断路)

配置是circuitBreaker.requestVolumeThreshold

默认10s 20次

·失败率

默认失败率超过50%就会被断路

配置是circuitBreaker.errorThresholdPercentage

关于断路器关闭

·重新尝试

在一定时间之后,重新尝试请求来决定是否继续打开或者选择关闭断路器

配置是circuitBreaker.sleepWindowInMilliseconds

默认5000ms

关于隔离

bulkhead pattern模式

Htstrix使用了bulkhead pattern模式,典型的例子就是线程隔离。

简单解释一下bulkhead pattern模式。一般情况我们都用一个线程池来管理所有线程,容易造成一个问题,粒度太粗,无法对线程进行分类管理,会导致局部问题影响全局。bulkhead pattern模式在于,采用多个线程池来管理线程,这样使得1个线程池资源出现问题时不会造成另一个线程池资源问题。尽量使问题最小化。

如图所示,采用了bulkhead pattern模式的效果

说完原理说实现,如何针对不同依赖采用不同的线程池管理呢

Hystrix给了我们三种key来用于隔离。

·CommandKey,针对相同的接口一般CommandKey值相同,目的是把HystrixCommand,HystrixCircuitBreaker,HytrixCommandMerics以及其他相关对象关联在一起,形成一个原子组。采用原生接口的话,默认值为类名;采用注解形式的话,默认值为方法名。

·CommandGroupKey,对CommandKey分组,用于真正的隔离。相同CommandGroupKey会使用同一个线程池或者信号量。一般情况相同业务功能会使用相同的CommandGroupKey。

·ThreadPoolKey,如果说CommandGroupKey只是逻辑隔离,那么ThreadPoolKey就是物理隔离,当没有设置ThreadPoolKey的时候,线程池或者信号量的划分按照CommandGroupKey,当设置了ThreadPoolKey,那么线程池和信号量的划分就按照ThreadPoolKey来处理,相同ThreadPoolKey采用同一个线程池或者信号量。

Coding

原生模式

可以通过HystrixCommand.Setter来自定义配置
HystrixCommandGroupKey.Factory.asKey(""))
HystrixCommandKey.Factory.asKey("")
HystrixThreadPoolKey.Factory.asKey("")

注解模式

可以直接在方法名上添加

@HystrixCommand(groupKey = "", commandKey = "", threadPoolKey = "")

关于请求缓存

工作流程图

优势

·复用性

这里的复用性指的是代码复用性

·一致性

也就是常说的幂等性,不管请求几次,得到的结果应该都是一样的

·减少重复工作

由于请求缓存是在HystrixCommand的construct()或run()运行之前运行,所有可以有效减少线程的使用

适用场景

请求缓存的优势显而易见,但是也不是银弹。

在读少写多的场景就显得不太合适,对于读的请求,需要add缓存。对于增删改的请求,需要把缓存remove。在增加系统资源开销的同时,又很鸡肋。

所以一般适合读多写少的场景。似乎所有缓存机制都有这个局限性吧

Coding

原生模式

继承HystrixCommand后,重写getCacheKey()方法,该方法默认返回的是null,也就是不使用请求缓存功能。相同key的请求会使用相同的缓存。

注解模式

在方法名上增加,并添加与cacheKeyMethod字符串相同的方法。两者共用入参。
@CacheResult(cacheKeyMethod = "getCacheKey")
public String post2AnotherService(String seed){
}
public String getCacheKey(String seed){ return seed; }

初始化HystrixRequestContext

还有关键的一步,在调用HystrixCommand之前初始化HystrixRequestContext,其实就是创建一个ThreadLocal的副本,共享请求缓存就是通过ThreadLocal来实现的。
HystrixRequestContext context=HystrixRequestContext.initializeContext();
操作完成后context.shutdown();
一般情况可以在过滤器中控制是初始化和关闭整个生命周期
//启动HystrixRequestContext
HystrixRequestContext context = HystrixRequestContext.initializeContext();
try {
chain.doFilter(req, res);
} finally {
//关闭HystrixRequestContext
context.shutdown();
}

关于请求合并(Requst Collapsing)

工作流程图

上半部分是模拟请求,下半部分是该请求的依赖设置,时间窗口默认是10ms,在这个时间窗口内,所有对于该接口的请求都会被加入队列,然后进行批处理。这样的好处在于,如果短时间内对于某个接口有大量请求,那么可以只处理一次就完成所有响应。

优势

全局线程合并

在tomcat容器中,所有请求共用一个进程,也就是一个JVM容器,在并发场景下会派生出许多线程,collapsing可以合并整个JVM中的请求线程,这样可以解决不同使用者同时请求的大量并发问题。

局部线程合并

可以合并单个tomcat请求线程,比如在10ms内有10个请求被同一线程处理(这不是像往常一样请求->处理,而是请求->加入请求队列,所有可以快速收集请求),那这些请求可以被合并。

对象建模和代码复杂度

在实际场景下,调用接口取数据的复杂度往往高于数据的复杂度,通俗来说就是取数据可以千变万化的取,而数据就那么几个接口。

collapsing可以帮助你更好的实现你的业务,比如多次请求合并结果后再广播出去。

适用场景

·并发量大接口

当并发量小,一个时间窗口内只有几个或没有请求,那么就白白浪费了请求合并的资源。

·请求耗时接口

时间窗口是固定的,假如一个请求实际耗时10ms,加上固定的时间窗口,最大延迟达到20ms,延迟被提高了100%。若一个请求实际耗时有1s,那么时间窗口的延迟就可以被忽略不计。

Coding

原生模式

/**
* 批量返回值类型
* 返回值类型
* 请求参数类型
*/
public class CommandCollapserGetValueForKey extends HystrixCollapser<List<String>, String, Integer> { private static Logger logger = LoggerFactory.getLogger(CommandCollapserGetValueForKey.class); private final Integer key; public CommandCollapserGetValueForKey(Integer key) {
this.key = key;
} /**
*获取请求参数
*/
public Integer getRequestArgument() {
return key;
} /**
*合并请求产生批量命令的具体实现
*/
protected HystrixCommand<List<String>> createCommand(final Collection<CollapsedRequest<String, Integer>> requests) {
return new BatchCommand(requests);
} /**
*批量命令结果返回后的处理,需要实现将批量结果拆分并传递给合并前的各原子请求命令的逻辑中
*/
protected void mapResponseToRequests(List<String> batchResponse, Collection<CollapsedRequest<String, Integer>> requests) {
int count = 0;
//请求响应一一对应
for (CollapsedRequest<String, Integer> request : requests) {
request.setResponse(batchResponse.get(count++));
}
} private static final class BatchCommand extends HystrixCommand<List<String>> {
private static Logger logger = LoggerFactory.getLogger(CommandCollapserGetValueForKey.BatchCommand.class); private final Collection<CollapsedRequest<String, Integer>> requests; private BatchCommand(Collection<CollapsedRequest<String, Integer>> requests) {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"))
.andCommandKey(HystrixCommandKey.Factory.asKey("GetValueForKey")));
this.requests = requests;
} @Override
protected List<String> run() {
ArrayList<String> response = new ArrayList<String>();
// 处理每个请求,返回结果
for (CollapsedRequest<String, Integer> request : requests) {
logger.info("request.getArgument()={}",request.getArgument());
// artificial response for each argument received in the batch
response.add("ValueForKey: " + request.getArgument());
}
return response;
}
}
}
调用的时候只需要new CommandCollapserGetValueForKey(1).queue()
在同一个时间窗口内,批处理的函数调用顺序为
getRequestArgument()->createCommand()->mapResponseToRequests()

关于配置

所有的配置在HystrixCommandProperties类中

每种配置都有4种优先级,以下为优先级从低到高的解释

1.基于代码的全局缺省值

2.基于properties配置表的全局配置

3.基于代码对配置更改

4.基于代码对配置动态更改

注:同一个配置,采用不同方法更改,那么配置的key会有不同

默认配置key

代码更改key

默认值

解释

hystrix.command.default.execution.isolation.strategy

hystrix.command.HystrixCommandKey.execution.isolation.strategy

THREAD

可选择的参数有THREAD, SEMAPHORE,表示隔离类型

hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds

hystrix.command.HystrixCommandKey.execution.isolation.thread.timeoutInMilliseconds

1000

降级的超时时间,单位ms

hystrix.command.default.execution.timeout.enabled

hystrix.command.HystrixCommandKey.execution.timeout.enabled

true

针对HystrixCommand.run()

是否使用超时降级策略

hystrix.command.default.execution.isolation.thread.interruptOnTimeout

hystrix.command.HystrixCommandKey.execution.isolation.thread.interruptOnTimeout

true

HystrixCommand.run()

超时后是否应该中断

hystrix.command.default.execution.isolation.thread.interruptOnCancel

hystrix.command.HystrixCommandKey.execution.isolation.thread.interruptOnCancel

false

HystrixCommand.run()

当发生cancel事件后是否应该取中断

hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests

hystrix.command.HystrixCommandKey.execution.isolation.semaphore.maxConcurrentRequests

10

当使用信号量隔离的时候,此配置有效。

官方给出5000请求只需要2个

hystrix.command.default.fallback.isolation.semaphore.maxConcurrentRequests

hystrix.command.HystrixCommandKey.fallback.isolation.semaphore.maxConcurrentRequests

10

HystrixCommand.getFallback()

最大并发数,超过此并发则拒绝请求

hystrix.command.default.fallback.enabled

hystrix.command.HystrixCommandKey.fallback.enabled

true

是否打开快速失败

hystrix.command.default.circuitBreaker.enabled

hystrix.command.HystrixCommandKey.circuitBreaker.enabled

true

是否打开熔断器

hystrix.command.default.circuitBreaker.requestVolumeThreshold

hystrix.command.HystrixCommandKey.circuitBreaker.requestVolumeThreshold

20

时间窗口内最小请求数,当小于这个请求数,即使全部失败也不会熔断

hystrix.command.default.circuitBreaker.sleepWindowInMilliseconds

hystrix.command.HystrixCommandKey.circuitBreaker.sleepWindowInMilliseconds

5000

熔断后,请求retry的时间间隔

hystrix.command.default.circuitBreaker.errorThresholdPercentage

hystrix.command.HystrixCommandKey.circuitBreaker.errorThresholdPercentage

50

失败率阈值,超过这个失败率就会熔断

hystrix.command.default.circuitBreaker.forceOpen

hystrix.command.HystrixCommandKey.circuitBreaker.forceOpen

false

是否强制开启熔断,这样会导致拒绝所有请求

hystrix.command.default.circuitBreaker.forceClosed

hystrix.command.HystrixCommandKey.circuitBreaker.forceClosed

false

是否强制关闭熔断,这样任何原因都无法触发熔断

注:优先级小于强制开启

hystrix.command.default.requestCache.enabled

hystrix.command.HystrixCommandKey.requestCache.enabled

true

是否打开请求缓存功能

hystrix.command.default.requestLog.enabled

hystrix.command.HystrixCommandKey.requestLog.enabled

true

HystrixCommand的执行和事件记录是否存入HystrixRequestLog

hystrix.collapser.default.maxRequestsInBatch

hystrix.collapser.HystrixCollapserKey.maxRequestsInBatch

Integer.MAX_VALUE

请求合并的最大请求数

hystrix.collapser.default.timerDelayInMilliseconds

hystrix.collapser.HystrixCollapserKey.timerDelayInMilliseconds

10

请求合并的时间窗口,也就是出现第一个请求后,在该时间间隔内的请求合并,超过则进入下一个时间窗口

hystrix.collapser.default.requestCache.enabled

hystrix.collapser.HystrixCollapserKey.requestCache.enabled

true

请求合并是否打开请求缓存

hystrix.threadpool.default.coreSize

hystrix.threadpool.HystrixThreadPoolKey.coreSize

10

线程池的大小,参考公式

requests per second at peak when
healthy × 99th percentile latency in seconds + some breathing room

例如:

30rps * 0.2s + breathing
room = 10

hystrix.threadpool.default.maximumSize

hystrix.threadpool.HystrixThreadPoolKey.maximumSize

10

线程池的最大大小,只会在设置了allowMaximumSizeToDivergeFromCoreSize的情况下生效,一般情况下和线程池大小相同

hystrix.threadpool.default.maxQueueSize

hystrix.threadpool.HystrixThreadPoolKey.maxQueueSize

-1

-1代表使用SynchronousQueue,其它值代表LinkedBlockingQueue.这两个队列用于ThreadPoolExecutor

hystrix.threadpool.default.queueSizeRejectionThreshold

hystrix.threadpool.HystrixThreadPoolKey.queueSizeRejectionThreshold

5

队列拒绝服务的阈值

hystrix.threadpool.default.keepAliveTimeMinutes

hystrix.threadpool.HystrixThreadPoolKey.keepAliveTimeMinutes

1

如果线程池的最大值大于线程池的大小,那么这个配置用于回收多久没被使用的线程。

hystrix.threadpool.default.allowMaximumSizeToDivergeFromCoreSize

hystrix.threadpool.HystrixThreadPoolKey.allowMaximumSizeToDivergeFromCoreSize

false

线程池的最大值是否可以和线程池的值不一样

hystrix.threadpool.default.metrics.rollingStats.timeInMilliseconds

hystrix.threadpool.HystrixThreadPoolKey.metrics.rollingStats.timeInMilliseconds

10000

度量的时间窗口

hystrix.threadpool.default.metrics.rollingStats.numBuckets

hystrix.threadpool.HystrixThreadPoolProperties.metrics.rollingStats.numBuckets

10

必须满足

metrics.rollingStats.timeInMilliseconds %
metrics.rollingStats.numBuckets == 0

//官方配置文档

https://github.com/Netflix/Hystrix/wiki/Configuration#circuitBreaker.sleepWindowInMilliseconds

总参考资料

https://github.com/Netflix/Hystrix/wiki

微服务(二)hystrix的更多相关文章

  1. .NET Core微服务二:Ocelot API网关

    .NET Core微服务一:Consul服务中心 .NET Core微服务二:Ocelot API网关 .NET Core微服务三:polly熔断与降级 本文的项目代码,在文章结尾处可以下载. 本文使 ...

  2. 微服务—熔断器Hystrix

    前言在微服务架构中,我们将系统拆分成了一个个的服务单元,各单元应用间通过服务注册与发现的方式互相依赖. 由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服 ...

  3. 我眼中的ASP.NET Core之微服务 (二)

    前言 接上一篇. 上一篇未完待续的原因是当时刚好是6-30号晚上马上12点了还没写完,然后我想赶在7月1号之前发出去,所以当时就发了.然后在发的时候出了一点问题,结果发出去的时候刚好是 7.1号 00 ...

  4. Spring Cloud 微服务二:API网关spring cloud zuul

    前言:本章将继续上一章Spring Cloud微服务,本章主要内容是API 网关,相关代码将延续上一章,如需了解请参考:Spring Cloud 微服务一:Consul注册中心 Spring clou ...

  5. SpringBoot-了解微服务(二)

    什么是微服务? 微服务是一种架构风格,它要求我们在开发一个应用的时候,这个应用必须构建成一系列小服务的组合: 可以通过http的方式进行互通. 要说微服务架构,先了解一下以前的单体应用架构 单体应用架 ...

  6. 微服务-熔断器 Hystrix 的原理与使用

    前言 分布式系统中经常会出现某个基础服务不可用造成整个系统不可用的情况, 这种现象被称为服务雪崩效应. 为了应对服务雪崩, 一种常见的做法是手动服务降级. 而Hystrix的出现,给我们提供了另一种选 ...

  7. 【一起学源码-微服务】Hystrix 源码二:Hystrix核心流程:Hystix非降级逻辑流程梳理

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲了配置了feign.hystrix.enabl ...

  8. 【一起学源码-微服务】Hystrix 源码一:Hystrix基础原理与Demo搭建

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一个系列文章讲解了Feign的源码,主要是Feign动态 ...

  9. 【一起学源码-微服务】Hystrix 源码三:Hystrix核心流程:Hystix降级、熔断等原理剖析

    说明 原创不易,如若转载 请标明来源! 欢迎关注本人微信公众号:壹枝花算不算浪漫 更多内容也可查看本人博客:一枝花算不算浪漫 前言 前情回顾 上一讲我们讲解了Hystrix在配合feign的过程中,一 ...

  10. Spring Cloud 微服务四:熔断器Spring cloud hystrix

    前言:在微服务架构中,一般都是进程间通信,有可能调用链都比较长,当有底层某服务出现问题时,比如宕机,会导致调用方的服务失败,这样就会发生一连串的反映,造成系统资源被阻塞,最终可能造成雪崩.在sprin ...

随机推荐

  1. MP3光标移动

    #include<iostream> #include<string.h> using namespace std; int main() { int N; cin>&g ...

  2. 怎样对ZBrush中的材料进行渲染和着色

    ZBrush可以实时的进行不断的渲染和着色. 对于绘制操作,ZBrush®增加了新的范围尺度,可以让你给基于像素的作品增加深度,材质,光照和复杂精密的渲染特效,真正实现了 2D 与 3D 的结合,模糊 ...

  3. 终端I/O之终端窗口的大小

    大多数UNIX系统都提供了一种功能,可以对当前终端窗口的大小进行跟踪,在窗口大小发生变化时,使内核通知前台进程组.内核为每个终端和伪终端保存一个winsize结构: Struct winsize { ...

  4. hdu4699 Editor 2013 多校训练第十场 D题 数列维护 splay | 线段树 | 栈!!!!!

    题意:维护一个文本编辑,并且查询最大前缀和. 写了splay,wa了13次 过了之后觉着特傻逼.发现题解两个栈就可以了,光标前后维护两个栈,维护前面的栈的前缀和 和 最大前缀和. 哎,傻逼,太弱了,还 ...

  5. 依赖反转原则DIP 与 asp.net core 项目结构

    DIP 依赖反转原则 Dependency Inversion Principle 的定义如下: 高级别的模块不应该依赖于低级别的模块, 他们都应该依赖于抽象. 假设Controller依赖于Repo ...

  6. 网络协议 9 - TCP协议(下):聪明反被聪明误

    网络协议 1 - 概述 网络协议 2 - IP 是怎么来,又是怎么没的? 网络协议 3 - 从物理层到 MAC 层 网络协议 4 - 交换机与 VLAN:办公室太复杂,我要回学校 网络协议 5 - I ...

  7. C语言的AT指令

    今天跟人聊嵌入式,对面是某国际硬盘生产商的嵌入式软件工程师,问了我很简单的问题,如何快速将一个变量赋给某个特定的地址. 按我们思路就是unsigned *a = address1:  *a = add ...

  8. 8-unittest中case管理

    1.关联 在接口测试中难免碰到接口B的参数值来源于接口A的返回结果,此现象即为关联.在unittest中怎么处理这种情况呢?此问题通过全局变量来解决,将变量定义为全局变量:globals()[‘var ...

  9. Java四种引用

    Java中提供了一个Reference抽象类,此类定义所有引用对象共有的操作,与垃圾收集器密切配合实现的.主要是为了决定某些对象的生命周期,有利于JVM进行垃圾回收.而继承此类的有四种引用,分别是St ...

  10. org.springframework.web.bind.annotation重定向的问题

    @RequestMapping(value="/redir/authcode") public ModelAndView getAuthCode(){ String authUrl ...