Hystrix流程分析及断路器工作原理

上一篇转载的文章主要讲了Hystrix的应用场景、基础组件概念以及从源码的角度阐述了基本应用。本篇文章主要借鉴官方文档介绍Hystrix的工作流程及断路器的原理,最后说一下与SpringCloud的简单集成。

点击图片进入我的博客
点击进入我的博客

How it Works

先上一个官方的流程图:

在这里插入图片描述
这个图大概描述了Hystrix的工作流程:
按照图中绿色标识的步骤:
1、创建一个HystrixCommand或HystrixObservableCommand对象;
2、通过以下四种方法执行命令(前两种方法仅适用于简单HystrixCommand对象且不可用HystrixObservableCommand):

  • execute():阻塞的,返回从依赖项收到的单个响应(或在出现错误时抛出异常)
  • queue(): 非阻塞的,返回一个可以从依赖项中获取单个响应的方法的Future对象。
  • observe(): 订阅Observable表示来自依赖项的响应,并返回Observable复制该源的响应Observable
  • toObservable(): 返回一个Observable,如果订阅了,会执行Hystrix命令并发出其响应

3、如果为此请求设置了缓存,首先判断缓存是否是可用的(即是否可以从缓存得到本次请求的响应),如果可以得到则直接返回,得不到再走下一步。
4、判断断路器是否打开,如果打开了说明之前的请求失败了(或者说之前满足了断路器的打开条件,具体满足的开闭条件下一模块再说),则直接调用重写的fallback()函数;如果断路器未打开再到下一步。
5、判断与该command关联的线程池和队列(或信号量)是否已满,如果已经满了,则Hystrix不会执行该Command,直接调用fallback()返回;否则到下一步。
6、此时通过HystrixObservableCommand.construct()或HystrixCommand.run()执行目的方法调用对依赖项的请求,如果执行失败或超时则直接执行fallback()。
7、健康状况统计
Hystrix在执行过程中会记录断路器的成功,失败,拒绝和超时状态,Hystrix维护一个记录这些数据的计数器,计数器的数据决定断路器的开闭状态。
8、当命令失败时Hystrix都会尝试回退,一般情况下,如果实现了HystrixCommand.getFallback()会返回单个回退值,或者实现HystrixObservableCommand.resumeWithFallback()会发出一个或多个回退值的Observable。如果没有实现fallback方法或在执行fallback方法时抛出了异常,Hystrix仍然会返回一个Observable,但不会返回任何内容,并立即终止并发出onError通知。通过此onError通知,导致命令失败的异常被传回给调用者。
9、成功返回

断路器

还是先上一个官方给出的逻辑决策图,包括计数器如何决定断路器的开闭。
在这里插入图片描述

说明:
1、如果请求数达到了设置的请求阈值或者请求失败的比例超过了设置的比例,则断路器将从close状态转到open状态,这时所有的请求都会被阻止。
2、sleep一段时间后,下一个请求将被放过,这时断路器处于半开半闭状态,目的是为了验证一下后边的路是否通畅,如果请求失败,则断路器回到open状态;如果成功了则断路器切换到closed状态并且返回响应的结果。

图中下方描述了计数器维护的数据存储结构及工作原理:大概意思是它维护10个桶(bucket),每个桶中记录第i秒请求状态(success、failure、timeout、rejection)的数量,当新的一秒请求记录来的时候,计数器会丢掉时间最靠前的桶。

SpringCloud简单集成

这个比较简单,注册中心选用Eureka(Consul的可以自己去测),直接上代码

model-1:eureka-server

Application.java

@EnableEurekaServer
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}

application.properties

spring.application.name=eureka-server
server.port=1001

eureka.instance.hostname=localhost
eureka.client.register-with-eureka=false
eureka.client.fetch-registry=false

logging.file=${spring.application.name}.log
model-2:eureka-consumer-ribbon-hystrix

Application.java

@EnableCircuitBreaker
@EnableDiscoveryClient
@SpringBootApplication
public class Application {

	@Bean
	@LoadBalanced
	public RestTemplate restTemplate() {
		return new RestTemplate();
	}

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}

DcController.java

@RestController
public class DcController {

    @Autowired
    ConsumerService consumerService;

    @GetMapping("/consumer")
    public String dc() {
        return consumerService.consumer();
    }

    @Service
    class ConsumerService {

        @Autowired
        RestTemplate restTemplate;

        @HystrixCommand(fallbackMethod = "fallback")
        public String consumer() {
            return restTemplate.getForObject("http://eureka-client/dc", String.class);
        }

        public String fallback() {
            return "fallbck";
        }

    }

}

application.properties

spring.application.name=eureka-consumer-ribbon-hystrix
server.port=2101

eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/

logging.file=${spring.application.name}.log
model-3:eureka-client

Application.java

@EnableDiscoveryClient
@SpringBootApplication
public class Application {

	public static void main(String[] args) {
		new SpringApplicationBuilder(Application.class).web(true).run(args);
	}
}

DcController.java

@RestController
public class DcController {

    @Autowired
    DiscoveryClient discoveryClient;

    @GetMapping("/dc")
    public String dc() {
        String services = "Services: " + discoveryClient.getServices();
        System.out.println(services);
        return services;
    }

}

application.properties

spring.application.name=eureka-client
server.port=2001

eureka.client.serviceUrl.defaultZone=http://localhost:1001/eureka/
#eureka.client.serviceUrl.defaultZone=http://peer1:1001/eureka/,http://peer2:1002/eureka/

logging.file=${spring.application.name}.log

Hystrix官方文档:Netflix-Hystrix官方WIKI
更多SpringCloud基础教程访问:程序员DD老司机的SpringCloud教程

本文已在版权印备案,如需转载请访问版权印06630244

### 断路器模式的工作原理 断路器模式的核心在于保护系统的稳定性,避免因单一服务故障而导致整个系统崩溃。其工作机制类似于电力系统中的断路器,在检测到过载或其他异常情况时自动切断电流流动,从而保护设备安全。 在微服务架构中,断路器模式通过监控服务调用的状态(如成功率、失败率、超时次数等),动态调整是否允许对该服务发起新的请求。一旦发现目标服务频繁发生错误或性能下降,则暂时阻止对其的访问,并立即返回预定义的结果(通常是降级处理)。这样不仅可以减少对下游服务的压力,还能让上游服务迅速响应客户端请求而不必等待长时间无果的操作完成[^1]。 具来说,Hystrix作为Netflix开源的一款库实现了这一理念。它会在后台持续收集关于各个命令键(commandKey)对应操作的数据指标——包括但不限于成功与否、耗时时长以及是否有资源争抢等情况;随后依据这些统计数据决定何时开启熔断状态。例如,当一段时间内的错误率达到一定阈值时,就会激活断路器进入打开(open)状态,此后所有针对此特定功能的新尝试都将被拦截并直接执行备选方案(即所谓的fallback logic),直到经过一段冷却期后再试探性恢复正常连接验证目的端口健康状况良好为止[^2]。 此外,Hystrix还采用了线程隔离策略来增强防护效果。这意味着每一个依赖项都被分配到了独立的线程运行,即使其中任何一个出现了阻塞也不会波及其他部分正常运转的能力[^3]。而且考虑到实际开发场景下的复杂性,hystrix并不能完全控制住某些外部因素比如网络层面上未妥善设置好的参数可能导致预期之外的行为出现所以特别强调了对于InterruptedException的支持必要性和合理规划HTTP请求时限的重要性[^5]。 总结而言,断路器不仅能够有效隔绝不稳定模块带来的连锁反应风险,还可以借助内置算法实现智能化管理流程优化用户验的同时保障整业务连续性不受干扰[^4]。 ```python import com.netflix.hystrix.HystrixCommand; import com.netflix.hystrix.HystrixCommandGroupKey; public class ExampleCommand extends HystrixCommand<String> { private final String name; public ExampleCommand(String name) { super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")); this.name = name; } @Override protected String run() throws Exception { // 实际的服务调用逻辑放在这 return "hello " + name; } @Override protected String getFallback(){ // 当run方法抛出异常时会被调用 return "fallback response"; } } ``` ####
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值