作用

防止 多个服务相互交互时某个服务运行缓慢导致调用方线程挂起,高并发情况下 导致挂起线太多 引起调用方的服务不可用

能够在服务发生故障或者通过断路器监控向调用方返回一个错误 而不是长时间的等待

Spring Cloud Hystrix 实现了线程隔离 断路器等功能 是基于开源框架Netflix 实现的

Hystrix具备服 务降级、 服务熔断、 线程和信号隔离、 请求缓存、 请求合并以及服务监控等强大功能

简单例子

1.Provier增加一个增对hystirx测试的Contorller

@Controller
public class HistryxTestServiceContorller { @Qualifier("eurekaRegistration")
@Autowired
private Registration registration; // 服务注册 @RequestMapping(value = "/histryxTest1")
@ResponseBody
public String histryxTest1(){
List<String> serverInfo = new ArrayList<String>();
serverInfo.add("ServiceId:"+registration.getServiceId());
serverInfo.add("ServiceUri:"+registration.getUri());
serverInfo.add("ServiceHost:"+registration.getHost());
serverInfo.add("ServiceSchema:"+ registration.getScheme());
serverInfo.add("ServicePort:"+registration.getPort());
serverInfo.add("ServiceMetadata:"+ registration.getMetadata());
return StringUtils.join(serverInfo,",");
} @RequestMapping(value = "/histryxTimeOutTest")
@ResponseBody
public String histryxTimeOutTest() throws InterruptedException {
int sleepIndex=new Random().nextInt(4000);
Thread.sleep(sleepIndex);
List<String> serverInfo = new ArrayList<String>(); serverInfo.add("ServiceId:"+registration.getServiceId());
serverInfo.add("ServiceUri:"+registration.getUri());
serverInfo.add("ServiceHost:"+registration.getHost());
serverInfo.add("ServiceSchema:"+ registration.getScheme());
serverInfo.add("ServicePort:"+registration.getPort());
serverInfo.add("ServiceMetadata:"+ registration.getMetadata());
return StringUtils.join(serverInfo,",")+",sleep:"+sleepIndex+"ms";
} }

2.Concumer增加一个测试Hystrix的Contorller

@Controller
public class HystrixTestContorller {
@Autowired
RestTemplate restTemplate; @RequestMapping(value = "/histryxTest1")
@ResponseBody
public String histryxTest1(){
return restTemplate.getForEntity("http://PROVIDER/histryxTest1",String.class).getBody(); } @RequestMapping(value = "/histryxTimeOutTest")
@ResponseBody
public String histryxTimeOutTest() throws InterruptedException {
return restTemplate.getForEntity("http://PROVIDER/histryxTimeOutTest",String.class).getBody(); } public String failback(){
return "error";
}
}

3.Provider增加2个配置文件对应不同端口

application-peer1.yml

spring:
application:
name: provider #服务名字
server:
port: 8081
eureka:
instance:
# 10s未收到心跳,剔除instance 要比心跳时间大
lease-expiration-duration-in-seconds: 30000
# 心跳时间
lease-renewal-interval-in-seconds: 5999
hostname: localhost #当前实例的主机名字
client:
serviceUrl:
defaultZone: http://peer1:1111/eureka #,http://peer2:1112/eureka #注册中心地址

application-peer2.yml

spring:
application:
name: provider #服务名字
server:
port: 8082
eureka:
instance:
# 10s未收到心跳,剔除instance 要比心跳时间大
lease-expiration-duration-in-seconds: 30000
# 心跳时间
lease-renewal-interval-in-seconds: 5999
hostname: localhost #当前实例的主机名字
client:
serviceUrl:
defaultZone: http://peer1:1111/eureka #,http://peer2:1112/eureka #注册中心地址
#management:

4.打包provider 启动2个provider指向不同的配置文件

java -jar /Users/liqiang/Desktop/java开发环境/javadom/springcloudhelloword/spring-cloud-stream/target/spring-cloud-stream-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer1
java -jar /Users/liqiang/Desktop/java开发环境/javadom/springcloudhelloword/spring-cloud-stream/target/spring-cloud-stream-0.0.1-SNAPSHOT.jar --spring.profiles.active=peer2

5.查看知否成功注册

6.访问consumer

会发现 会线性轮训8081 和8082

7.关闭其中一个服务 轮训到这个服务的时候回报

8测试超时熔断

不加熔断的情况下会一致等待执行完毕

9 consumer增加熔断

pom文件增加hystrix依赖

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

10.@EnableCircuitBreake开启熔断自动化配置

@SpringBootApplication
@EnableDiscoveryClient //开启服务发现
@EnableCircuitBreaker//开启断路器功能或者整体使用@SpringCloudApplication注解替代。详情点进去看
public class ConsumerApplication { public static void main(String[] args) {
SpringApplication.run(ConsumerApplication.class, args);
}
//LoadBalanced 通过代理RestTemplate 实现客户端负载均衡的能力
@LoadBalanced
@Bean
RestTemplate restTemplate(){
return new RestTemplate();
}
}

11.在需要在启用熔断的类增加注解

@Controller
public class HystrixTestContorller {
@Autowired
RestTemplate restTemplate; //指定熔断调用的方法
@HystrixCommand(fallbackMethod = "failback")
@RequestMapping(value = "/histryxTest1")
@ResponseBody
public String histryxTest1(){
return restTemplate.getForEntity("http://PROVIDER/histryxTest1",String.class).getBody(); }
//指定熔断调用的方法
@HystrixCommand(fallbackMethod = "failback")
@RequestMapping(value = "/histryxTimeOutTest")
@ResponseBody
public String histryxTimeOutTest() throws InterruptedException {
return restTemplate.getForEntity("http://PROVIDER/histryxTimeOutTest",String.class).getBody(); }
public String failback(){
return "error";
}
}

根据上面的测试关闭服务后会直接返回erro  或者hystirx默认1秒超时  上面超时也会直接返回error (这样就解决了服务依赖某个服务响应慢导致服务挂起 引起雪崩效应)

hystrix超时是默认开启的 可以通过hystrix.command.default.execution.timeout.enabled=true #是否启用超时

HystrixCommand&HystrixObservableCommand原理

1.创建HystrixCommand或HystrixObservableCommand对象(命令模式  分为命令执行者Receiver 抽象命令Command 调用者 IInvoker实现 命令执行者和调用者的解耦)

HystrixCommand 用于依赖服务返回单个结果

HystrixObservableCommand 用于依赖服务返回多个服务

个人理解命令模式

/**
* 抽象命令
*/
public interface ICommand {
public String execute();
}
/**
* 命令具体实现
*/
public class Command implements ICommand {
public Receiver receiver;
public Command(Receiver receiver){
this.receiver=receiver;
} @Override
public String execute() {
/**
* 命令和调用者的解耦
* 这里就可以判断是否熔断 是否超时 是否错误 调用 receiver.failback();
* 或者一些数据指标的统计
*
*/
return receiver.action();
}
}
/**
* 命令执行者
*/
public class Receiver {
public RestTemplate restTemplate;
public Receiver(RestTemplate restTemplate){
this.restTemplate=restTemplate;
}
//执行命令
public String action(){
return restTemplate.getForEntity("http://PROVIDER/hello",String.class).getBody();
}
//命令失败后执行操作(撤回逻辑等)
public String failback(){
return "error";
}
}
/**
* 调用者
*/
public class Invoker {
ICommand command;
public Invoker(ICommand iCommand){
this.command=iCommand;
}
public String action(){
return this.command.execute();
} } Receiver receiver=new Receiver(restTemplate);
ICommand command=new Command(receiver);
Invoker invoker=new Invoker(command);
invoker.action();

2、执行命令

HystrixComrnand

execute()  同步执行  发生错误直接跑出异常

queue() 返回 Future 异步执行

通过HystrixCommand.getFallback()来 实现服务降级逻辑。

HystrixObservableCommand

observe()  返回Observable对象,它代表了操作的多个结果,它是 一个Hot Observable。

toObservable()  同样会返回Observable对象, 也代表了操作的多个结果, 但它返回的是 一个Cold Observable。

通过Command.resumeW江hFallback()

3.判断结果是否被缓存如果缓存则直接返回缓存

4.如果缓存没命中判断缓存是否打开如果打开则直接执行failback 否则直接跳到第五步

5.判断 请求队列/信号量/线程池是否占满(线程池塘是判断 依赖服务特有的线程池塘)

6HystrixObservableCommand.construct()或HystrixCommand.run() 取决于是用HystrixCommand还是HystrixObservableCommand

7.Hystrix会将“成功”、 “失败”、 “拒绝”、 “ 超时” 等信息报告给断路器,而断路器会维 护 一 组计数器来统计这些数据。 断路器会根据这些统计信息来判断是否熔断/断路

8.failback处理 

   第4步 第5步   第6步发生异常时 HystrixComrnand通过HystrixCommand.getFallback()来 实现服务降级逻辑。 通过Command.resumeWithFallback()

• execute(): 抛出异常。

• queue(): 正常返回Future对象,但是当 调用get()来获取结果的时候会抛出异 常。

• observe(): 正常返回Observable 对象, 当订阅它的时候, 将立即通过调用订 阅者的onError方法来通知中止请求。

• toObservable(): 正常返回Observable对象, 当订阅它的时候, 将通过调用 订阅者的onError方法来通知中止请求。

9.响应结果

• toObservable(): 返回最原始的 Observable, 必须通过订阅它才会真正触发

命令的执行流程。

• observe(): 在toObservable()产生原始Observable 之后立即 订阅它, 让 命令能够马上开始异步执行 , 并返回一 个Observable 对象, 当调用它的 subscribe 时, 将重新产生结果和通知给订阅者。

• queue(): 将 toObservable()产生的原始Observable通过toBlocking() 方法转换成BlockingObservable对象, 并调用它的toFuture()方法 返回异

步的Future对象。

• execute():在queue()产生异步结果Future对象之后,通过调用get()方法 阻塞并等待结果的返回。

hystirx都是以Observable返回结果 只是通过Observable可以转换成多种结果 值  future

断路器原理

HystrixCircuitBreaker

public interface HystrixCircuitBreaker {
//判断是否被执行
boolean allowRequest();
//断路器是否打开
boolean isOpen();
//闭合断路器 成功时调用
void markSuccess();
//将熔断器状态重新置为开启状态,并把circuitOpened设置为当前的时间戳。 error调用
void markNonSuccess();
boolean attemptExecution();
/**
* 定义了一个什么都不做的断路器实现,它允许所有 请求, 并且断路器状态始终闭合。
*/
public static class NoOpCircuitBreaker implements HystrixCircuitBreaker {
} /**
* HystrixCircuitBreaker 实现类
* 成员变量:HystrixCommandProperties 定义HystrixCommand的配置信息
* 成员变量 HystrixCommandMetrics 定义hystrixCommand的度量指标
* 成员变量 AtomicBoolean 定义断路器是否打开
* 成员变量AtomicLong c江cuitOpenedOrLastTestedTime 定义上一次打开断路器的时间戳
*/
public static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {......}
/**
* 维护了一个Hystrix命令与HystrixCircuitBreaker的关系
* 集合: ConcurrentHashMap<String, HystrixCircui七Breaker> circuit一 BreakersByCommand,
* 其中 String 类型的 key 通过 HystrixCommandKey 定义,
* 每一个 Hystrix 命令需要有一个 key 来标识, 同时一个 Hystrix
* 命令也会在该集合中 找到它对应的断路器 HystrixCircuitBreaker 实例。
*/
public static class Factory {
private static ConcurrentHashMap<String, HystrixCircuitBreaker> circuitBreakersByCommand = new ConcurrentHashMap(); public Factory() {
} public static HystrixCircuitBreaker getInstance(HystrixCommandKey key, HystrixCommandGroupKey group, HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
HystrixCircuitBreaker previouslyCached = (HystrixCircuitBreaker)circuitBreakersByCommand.get(key.name());
if (previouslyCached != null) {
return previouslyCached;
} else {
HystrixCircuitBreaker cbForCommand = (HystrixCircuitBreaker)circuitBreakersByCommand.putIfAbsent(key.name(), new HystrixCircuitBreaker.HystrixCircuitBreakerImpl(key, group, properties, metrics));
return cbForCommand == null ? (HystrixCircuitBreaker)circuitBreakersByCommand.get(key.name()) : cbForCommand;
}
} public static HystrixCircuitBreaker getInstance(HystrixCommandKey key) {
return (HystrixCircuitBreaker)circuitBreakersByCommand.get(key.name());
} static void reset() {
circuitBreakersByCommand.clear();
}
}
}

HystrixCircuitBreakerImpl

 public static class HystrixCircuitBreakerImpl implements HystrixCircuitBreaker {
//断路器对应实例的属性集合对象
private final HystrixCommandProperties properties;
//用来让HystrixCommand记录各类度量指标的对象
private final HystrixCommandMetrics metrics;
//用来记录断路器的状态,默认是关闭状态
private final AtomicReference<HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status> status;
//断路器打开的时间戳,默认-1,表示断路器未打开
private final AtomicLong circuitOpened;
// 这个是通过Rxjava实现的对HystrixCommandMetrics结果的观察者对象,当HystrixCommandMetrics值发生变化时会通知观察者。
private final AtomicReference<Subscription> activeSubscription; protected HystrixCircuitBreakerImpl(HystrixCommandKey key, HystrixCommandGroupKey commandGroup, HystrixCommandProperties properties, HystrixCommandMetrics metrics) {
this.status = new AtomicReference(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.CLOSED);
this.circuitOpened = new AtomicLong(-1L);
this.activeSubscription = new AtomicReference((Object)null);
this.properties = properties;
this.metrics = metrics;
Subscription s = this.subscribeToStream();
this.activeSubscription.set(s);
} private Subscription subscribeToStream() {
//观察者 当HystrixCommandMetrics的度量指标发生变化时,观察者实现的业务逻辑
return this.metrics.getHealthCountsStream().observe().subscribe(new Subscriber<HealthCounts>() {
public void onCompleted() {
} public void onError(Throwable e) {
} public void onNext(HealthCounts hc) {
if (hc.getTotalRequests() >= (long)(Integer)HystrixCircuitBreakerImpl.this.properties.circuitBreakerRequestVolumeThreshold().get() && hc.getErrorPercentage() >= (Integer)HystrixCircuitBreakerImpl.this.properties.circuitBreakerErrorThresholdPercentage().get() && HystrixCircuitBreakerImpl.this.status.compareAndSet(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.CLOSED, HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.OPEN)) {
HystrixCircuitBreakerImpl.this.circuitOpened.set(System.currentTimeMillis());
} }
});
} public void markSuccess() {
if (this.status.compareAndSet(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.HALF_OPEN, HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.CLOSED)) {
this.metrics.resetStream();
Subscription previousSubscription = (Subscription)this.activeSubscription.get();
if (previousSubscription != null) {
previousSubscription.unsubscribe();
} Subscription newSubscription = this.subscribeToStream();
this.activeSubscription.set(newSubscription);
this.circuitOpened.set(-1L);
} } public void markNonSuccess() {
if (this.status.compareAndSet(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.HALF_OPEN, HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.OPEN)) {
this.circuitOpened.set(System.currentTimeMillis());
} } public boolean isOpen() {
//强制开启断路器 配置
if ((Boolean)this.properties.circuitBreakerForceOpen().get()) {
return true;
} else if ((Boolean)this.properties.circuitBreakerForceClosed().get()) {//强制关闭断路器 配置
return false;
} else {
return this.circuitOpened.get() >= 0L;//根据断路器开启时间判断断路器的开启状态
}
}
//判断是否允许请求接口(每次请求接口都会判断)
public boolean allowRequest() {
if ((Boolean)this.properties.circuitBreakerForceOpen().get()) {
return false;
} else if ((Boolean)this.properties.circuitBreakerForceClosed().get()) {
return true;
} else if (this.circuitOpened.get() == -1L) {
return true;
} else {
return ((HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status)this.status.get()).equals(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.HALF_OPEN) ? false : this.isAfterSleepWindow();
}
}
//判断时间有没有过休眠期
private boolean isAfterSleepWindow() {
long circuitOpenTime = this.circuitOpened.get();
long currentTime = System.currentTimeMillis();
long sleepWindowTime = (long)(Integer)this.properties.circuitBreakerSleepWindowInMilliseconds().get();
return currentTime > circuitOpenTime + sleepWindowTime;
}
//尝试执行接口请求
public boolean attemptExecution() {
if ((Boolean)this.properties.circuitBreakerForceOpen().get()) {
return false;
} else if ((Boolean)this.properties.circuitBreakerForceClosed().get()) {
return true;
} else if (this.circuitOpened.get() == -1L) {
return true;
} else if (this.isAfterSleepWindow()) {
return this.status.compareAndSet(HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.OPEN, HystrixCircuitBreaker.HystrixCircuitBreakerImpl.Status.HALF_OPEN);
} else {
return false;
}
} /**
*CLOSED 关闭
*OPEN 打开
*HALF_OPEN :
* 当断路器打开后,对应接口的请求会有段休眠期,这个休眠期内接口请求不会被正真的执行,但是如果休眠期时间过了
* 这个时候断路器允许一次真实的接口请求,如果这次请求失败,则断路器打开(OPEN),循环上面的动作,如果请求成功则断路器关闭(CLOSED)。
*/
static enum Status {
CLOSED,
OPEN,
HALF_OPEN;
private Status() {
}
}
}

线程隔离

hystrix采用每个依赖服务都拥有各自的线程池

好处:

1.某个依赖服务自身出线问题不会影响其他依赖服务

2.不受其他不稳定因素依赖服务的影响

3.某个依赖服务关闭  快速释放

面临的问题:

各个依赖服务独立开启线程池塘的额外开销 不过在hystix的统计信息中 微乎其微

hystrix除了可以通过线程池控制并发数还可以对单个依赖服务通过信号量控制  信号量控制比线程池开销更小

只需要将隔离策略参数execution.isolation.strategy设置为SEMAPHORE' Hystrix 会使用信号量替代线程池来控制依赖服务的并发。(默认10)

Spring Cloud-hystrix(六)的更多相关文章

  1. Spring Cloud(六):Hystrix 监控数据聚合 Turbine【Finchley 版】

    Spring Cloud(六):Hystrix 监控数据聚合 Turbine[Finchley 版]  发表于 2018-04-17 |  更新于 2018-05-07 |  上一篇我们介绍了使用 H ...

  2. Spring Cloud 微服务笔记(六)Spring Cloud Hystrix

    Spring Cloud Hystrix Hystrix是一个延迟和容错库,旨在隔离远程系统.服务和第三方库,阻止链接故障,在复杂的分布式系统中实现恢复能力. 一.快速入门 1)依赖: <dep ...

  3. Spring Cloud Hystrix理解与实践(一):搭建简单监控集群

    前言 在分布式架构中,所谓的断路器模式是指当某个服务发生故障之后,通过断路器的故障监控,向调用方返回一个错误响应,这样就不会使得线程因调用故障服务被长时间占用不释放,避免故障的继续蔓延.Spring ...

  4. 一起来学Spring Cloud | 第六章:服务网关 ( Zuul)

    本章节,我们讲解springcloud重要组件:微服务网关Zuul.如果有同学从第一章看到本章的,会发现我们已经讲解了大部分微服务常用的基本组件. 已经讲解过的: 一起来学Spring Cloud | ...

  5. 笔记:Spring Cloud Hystrix 异常处理、缓存和请求合并

    异常处理 在 HystrixCommand 实现的run方法中抛出异常,除了 HystrixBadRequestException之外,其他异常均会被Hystrix 认为命令执行失败并触发服务降级处理 ...

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

    由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加 ...

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

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

  8. 试水Spring Cloud Hystrix

    Spring Cloud Hystrix是一个容错库,它实现了断路器模式,使得当服务发生异常时,会自动切断连接,并将请求引导至预设的回调方法. 服务端 在Spring Tool Suite的文件菜单中 ...

  9. spring cloud: Hystrix(五):如禁止单个FeignClient使用hystrix

    spring cloud: Hystrix(五):如禁止单个FeignClient使用hystrix 首先application.yml / applicatoin.propreties的配置项:fe ...

  10. spring cloud: Hystrix(四):feign类似于hystrix的断容器功能:fallback

    spring cloud: Hystrix(四):feign使用hystrix @FeignClient支持回退的概念:fallback方法,这里有点类似于:@HystrixCommand(fallb ...

随机推荐

  1. 从零開始学Xamarin.Forms(二) 环境搭建、创建项目

    一.环境搭建 Windows下环境搭建:     1.下载并安装jdk.Android SDK和NDK.当然还须要 VS2013 update 2(VS2010.VS2012均可)以上. a.  最新 ...

  2. oc48--多个对象内存管理练习

    // // main.m // 多个对象内存管理练习 // // ARC是Xcode帮我们生成内存释放的代码,MRC是需要我买自己写retain和release.想研究内存管理只能在MRC,管理对象就 ...

  3. springboot 异常: Requested bean is currently in creation: Is there an unresolvable circular reference?

    2018-07-31 11:56:18.812 WARN 10316 --- [ main] ConfigServletWebServerApplicationContext : Exception ...

  4. hdu 1754(单点更新 ,区间最大值)

    I Hate It Time Limit: 9000/3000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others)Total S ...

  5. python 46 css组合选择器 及优先级 、属性选择器

    一:css组合选择器 特性:每个选择器位可以为任意基本选择器或选择器组合 选择器分为以下几类: 群组选择器,子代(后代)选择器,相邻(兄弟)选择器,交集选择器,多类名选择器 1.群组选择器:    d ...

  6. lnmp 安装FTP服务 并配置FTP用户

    lnmp 默认是不带FTP服务的,需要的童鞋要自行安装.步骤也很简单 一,进入lnmp目录,找到pureftpd.sh 二,直接运行该脚本 ./pureftpd.sh 按任意键开始安装,等待,安装成功 ...

  7. MyBatis动态条件、一对多、整合spring(二)

    输入映射和输出映射 Mapper.xml映射文件定义了操作数据库的sql,每一个sql是一个statement,映射文件是mybatis的核心. parameterType输入类型 1.传递简单类型 ...

  8. Jsp页面报错状态码含义

    原来,全部在HttpServletResponse接口的字段里 状态码 (),表示一个请求已经被接受处理,但还没有完成.  状态码 (),表明HTTP服务器从一个服务器收到了一个无效的响应,当其作为一 ...

  9. rabbit--消息持久化

    消息的可靠性是RabbitMQ的一大特色,那么RabbitMQ是如何保证消息可靠性的呢——消息持久化. 为了保证RabbitMQ在退出或者crash等异常情况下数据没有丢失,需要将queue,exch ...

  10. Laravel5.1学习笔记10 系统架构2 应用程序结构

    应用程序结构 简介 根目录 App 目录 为应用程序设置命名空间 简介 默认的 Laravel 应用程序结构是为了给无论构建大型还是小型应用程序都提供一个良好的开始.当然,你可以依照喜好自由地组织应用 ...