Spring Cloud-hystrix(六)
作用
防止 多个服务相互交互时某个服务运行缓慢导致调用方线程挂起,高并发情况下 导致挂起线太多 引起调用方的服务不可用
能够在服务发生故障或者通过断路器监控向调用方返回一个错误 而不是长时间的等待
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(六)的更多相关文章
- Spring Cloud(六):Hystrix 监控数据聚合 Turbine【Finchley 版】
Spring Cloud(六):Hystrix 监控数据聚合 Turbine[Finchley 版] 发表于 2018-04-17 | 更新于 2018-05-07 | 上一篇我们介绍了使用 H ...
- Spring Cloud 微服务笔记(六)Spring Cloud Hystrix
Spring Cloud Hystrix Hystrix是一个延迟和容错库,旨在隔离远程系统.服务和第三方库,阻止链接故障,在复杂的分布式系统中实现恢复能力. 一.快速入门 1)依赖: <dep ...
- Spring Cloud Hystrix理解与实践(一):搭建简单监控集群
前言 在分布式架构中,所谓的断路器模式是指当某个服务发生故障之后,通过断路器的故障监控,向调用方返回一个错误响应,这样就不会使得线程因调用故障服务被长时间占用不释放,避免故障的继续蔓延.Spring ...
- 一起来学Spring Cloud | 第六章:服务网关 ( Zuul)
本章节,我们讲解springcloud重要组件:微服务网关Zuul.如果有同学从第一章看到本章的,会发现我们已经讲解了大部分微服务常用的基本组件. 已经讲解过的: 一起来学Spring Cloud | ...
- 笔记:Spring Cloud Hystrix 异常处理、缓存和请求合并
异常处理 在 HystrixCommand 实现的run方法中抛出异常,除了 HystrixBadRequestException之外,其他异常均会被Hystrix 认为命令执行失败并触发服务降级处理 ...
- 笔记:Spring Cloud Hystrix 服务容错保护
由于每个单元都在不同的进程中运行,依赖通过远程调用的方式执行,这样就有可能因为网络原因或是依赖服务自身问题出现调用故障或延迟,而这些问题会直接导致调用方的对外服务也出现延迟,若此时调用方的请求不断增加 ...
- 第五章 服务容错保护:Spring Cloud Hystrix
在微服务架构中,我们将系统拆分为很多个服务,各个服务之间通过注册与订阅的方式相互依赖,由于各个服务都是在各自的进程中运行,就有可能由于网络原因或者服务自身的问题导致调用故障或延迟,随着服务的积压,可能 ...
- 试水Spring Cloud Hystrix
Spring Cloud Hystrix是一个容错库,它实现了断路器模式,使得当服务发生异常时,会自动切断连接,并将请求引导至预设的回调方法. 服务端 在Spring Tool Suite的文件菜单中 ...
- spring cloud: Hystrix(五):如禁止单个FeignClient使用hystrix
spring cloud: Hystrix(五):如禁止单个FeignClient使用hystrix 首先application.yml / applicatoin.propreties的配置项:fe ...
- spring cloud: Hystrix(四):feign类似于hystrix的断容器功能:fallback
spring cloud: Hystrix(四):feign使用hystrix @FeignClient支持回退的概念:fallback方法,这里有点类似于:@HystrixCommand(fallb ...
随机推荐
- phoenixframe自己主动化測试平台对div弹出框(如弹出的div登陆框)的处理
package org.phoenix.cases; import java.util.LinkedList; import org.phoenix.action.WebElementActionPr ...
- How to do IF NOT EXISTS in SQLite
http://stackoverflow.com/questions/531035/how-to-do-if-not-exists-in-sqlite How about this? INSERT O ...
- ssdb底层实现——ssdb底层是leveldb,leveldb根本上是skiplist(例如为存储多个list items,必然有多个item key,而非暴力string cat),用它来做redis的list和set等,势必在数据结构和算法层面上有诸多不适
我已经在用ssdb的hash结构,存储了很多数据了,但是我现在的用法正确吗? 我使用hash结构合理吗? 1. ssdb数据库说是类似redis,而且他们都有hash结构,但是他们的命名有点不同,ss ...
- 简述RTMPDump与编译移植
RTMPDump主页 ,RTMPDump库主要包含三部分: 1.一个基本的客户端程序 2.两个服务器程序(rtmpsrv.rtmpsuck) 3.一个支持rtmp协议的库—librtmp 下载RTMP ...
- bzoj 1503郁闷的出纳员(splay)
1503: [NOI2004]郁闷的出纳员 Time Limit: 5 Sec Memory Limit: 64 MBSubmit: 11759 Solved: 4163[Submit][Stat ...
- [Apple开发者帐户帮助]二、管理你的团队(2)更改团队成员角色
如果您已加入Apple开发者计划,您将在App Store Connect中管理团队成员.有关详细信息,请转到App Store Connect帮助中的添加和编辑用户. 如果您已加入Apple Dev ...
- Github标星4W+,热榜第一,如何用Python实现所有算法
文章发布于公号[数智物语] (ID:decision_engine),关注公号不错过每一篇干货. 来源 | 大数据文摘(BigDataDigest) 编译 | 周素云.蒋宝尚 学会了 Python 基 ...
- GStreamer系列 - 基本介绍
什么是Gstreamer? Gstreamer是一个支持Windows,Linux,Android, iOS的跨平台的多媒体框架,应用程序可以通过管道(Pipeline)的方式,将多媒体处理的各个步骤 ...
- BZOJ 1303
思路: 水题 竟然不会做 尴尬 比b大的数=1 比b小的数=-1 找到b 统计一下左边比b大x的数有多少 扫右边的时候就查左边的表 就可以了 //By SiriusRen #include < ...
- BZOJ 4753 二分+树形DP
思路: 先二分答案 f[x][j]表示在x的子树里选j个点 f[x][j+k]=max(f[x][j+k],f[x][j]+f[v[i]][k]); 初始化 x!=0 -> f[x][1]=p[ ...