Spring Cloud学习 之 Spring Cloud Hystrix(使用详解)
创建请求命令:
Hystrix命令就是我们之前说的HystrixCommand,它用来封装具体的依赖服务调用逻辑。
我们可以通过继承的方式来实现,比如:
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
public CommandHelloWorld(String name) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
}
@Override
protected String run() {
return "Hello " + name + "!";
}
}
通过上面实现的CommandHelloWorld,我们即可以实现请求的同步执行,也可以实现异步执行。
同步执行:new CommandHelloWorld().execute();
异步执行:new CommandHelloWorld().queue();异步执行的时候,可以通过返回的futureUser调用get方法来获取结果
测试案例:
/**
* 同步测试
* @param args
*/
public static void main(String[] args) {
CommandHelloWorld commandHelloWorld = new CommandHelloWorld("张三");
String execute = commandHelloWorld.execute();
System.out.println(execute);
}
/**
* 测试结果:Hello 张三!
*/
public class CommandHelloWorld extends HystrixCommand<String> {
private final String name;
private final CountDownLatch countDownLatch;
public CommandHelloWorld(String name, CountDownLatch countDownLatch) {
super(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
this.name = name;
this.countDownLatch = countDownLatch;
}
@Override
protected String run() throws Exception {
System.out.println("开始执行run");
System.out.println("run执行完成");
countDownLatch.countDown();
return "Hello " + name + "!";
}
/**
* 异步测试
*
* @param args
*/
public static void main(String[] args) throws Exception {
CountDownLatch countDownLatch = new CountDownLatch(2);
CommandHelloWorld commandHelloWorld = new CommandHelloWorld("张三", countDownLatch);
Future<String> queue = commandHelloWorld.queue();
countDownLatch.countDown();
System.out.println("等待任务执行完成");
countDownLatch.await();
}
/**
* 测试结果
* 等待任务执行完成
* 1
* 开始执行run
* run执行完成
*/
另外,也可以通过@HystrixCommand注解更为优雅的实现Hystrix命令的定义,比如:
public class AnnotationCommand {
@HystrixCommand
public String testAnnotation(String name) {
return "hello,annotation" + name;
}
}
虽然@HystrixCommand注解可以非常优雅的定义Hystri命令的实现,但是如上定义的方法执行同步执行的实现,若要实现异步执行则还需另外定义,比如:
public class AnnotationCommand {
/**
* 同步
*
* @param name
* @return
*/
@HystrixCommand
public String testAnnotation(String name) {
return "hello,annotation" + name;
}
/**
* 异步
*
* @param name
* @return
*/
@HystrixCommand
public Future<String> tesAsyntAnnotation(String name) {
return new AsyncResult<String>() {
@Override
public String invoke() {
return "hello,annotation" + name;
}
};
}
}
observe()和toObservable()虽然都返回了Observable,但是他们略有不同,前者返回的是一个Hot Observable,该命令会在observe()调用的时候立即执行,当Observable每次被订阅的时候会重放它的行为;而后者返回的是一个Cold Observable,toObservable()执行之后,命令不会被立即执行,只有当所有订阅者都订阅它之后才会执行。
虽然HystrixCommand具备了observe()和toObservable()的功能,但是它的实现有一定的局限性,它返回的Observable只能发射一次数据,所以Hystrix还提供了另外一个特殊命令封装HystrixObservableCommand,通过实现的命令可以获取能发射多次的Observable。
定义服务降级:
fallback是Hystrix命令执行失败时使用的后备方法,用来实现服务的降级处理逻辑。在HystrixCommand中可以通过重载getFallback()方法来实现服务降级逻辑,Hystrix会在run()执行过程中出现错误,超时,线程池拒绝,断路器熔断等情况时,执行getFallback()方法内的逻辑,比如我们可以用如下方式实现服务降级逻辑:
public class Fallback extends HystrixCommand {
@Override
protected Object run() throws Exception {
throw new RuntimeException("出错");
}
protected Fallback(HystrixCommandGroupKey group) {
super(group);
}
@Override
protected Object getFallback() {
System.out.println("服务降级");
return "error";
}
public static void main(String[] args) {
Fallback fallback = new Fallback(HystrixCommandGroupKey.Factory.asKey("ExampleGroup"));
Object execute = fallback.execute();
}
}
// 执行结果
java.lang.RuntimeException: 出错
at com.study.cloud.hystrix.fallback.Fallback.run(Fallback.java:13)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:302)
at com.netflix.hystrix.HystrixCommand$2.call(HystrixCommand.java:298)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:46)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:51)
at rx.internal.operators.OnSubscribeDefer.call(OnSubscribeDefer.java:35)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:41)
at rx.internal.operators.OnSubscribeDoOnEach.call(OnSubscribeDoOnEach.java:30)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:48)
at rx.internal.operators.OnSubscribeLift.call(OnSubscribeLift.java:30)
at rx.Observable.unsafeSubscribe(Observable.java:10327)
at rx.internal.operators.OperatorSubscribeOn$SubscribeOnSubscriber.call(OperatorSubscribeOn.java:100)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:56)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction$1.call(HystrixContexSchedulerAction.java:47)
at com.netflix.hystrix.strategy.concurrency.HystrixContexSchedulerAction.call(HystrixContexSchedulerAction.java:69)
at rx.internal.schedulers.ScheduledAction.run(ScheduledAction.java:55)
at java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:511)
at java.util.concurrent.FutureTask.run(FutureTask.java:266)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at java.lang.Thread.run(Thread.java:748)
服务降级
在HystrixObservableCommand实现的Hystrix命令中,我们可以通过重载resumeWithFallback方法来实现服务降级逻辑。该方法会返回一个Observable对象,当命令执行失败的时候,Hystrix会将Observable中的结果通过给所有的订阅者。
若要通过注解实现服务降级,只需要使用@HystrixCommand中的fallbackMethod参数来指定具体的服务降级实现方法。在使用注解来定义服务降级逻辑时,我们需要将具体的Hystrix命令与fallback实现函数定义在同一个类中,并且fallbackMthod的值必须与实现fallback方法的名字相同。且必须定义在一个类中,所以对于fallback的访问修饰符没有特定的要求,定义为private,protected,public均可。
在实际使用时,我们需要为大多数执行过程中可能会失败的Hystrix命令实现服务降级逻辑,但是也有一些情况可以不去实现降级逻辑,如下所示:
执行写操作的命令:当Hystrix命令是用来执行写操作而不是返回一些信息的时候,通常情况下这类操作的返回类型是void或是为空的时候,我们通常只需要调用通知者即可
执行批处理或离线计算的命令:当Hystrix命令是用来执行批处理程序生成一份报告或是进行任何类型的离线计算时,那么通常这些操作只需要将错误传播个给调用者然后让调用者稍后重试而不是发送给调用者一个静默的降级处理响应。
不论Hystrix命令是否实现了服务降级,命令状态和断路器状态都会更新,并且我们可以由此了解到命令执行的失败情况。
异常处理:
异常传播:
在HystrixComman实现的run()方法中抛出异常时,除了HystrixBadRequestException之外,其他异常均会被Hystrix认为命令执行失败并触发服务降级的处理逻辑,所以当需要在命令执行中抛出不触发服务降级的异常时来使用它。
而在使用注册配置实现Hystrix命令时,它还支持忽略指定异常类型功能,只需要通过设置@HystrixCommand
注解的ignoreException参数,比如:
public class Demo01 {
@HystrixCommand(ignoreExceptions = ArithmeticException.class)
public String test() {
// do something
return "hello";
}
}
如上面代码的定义,当方法抛出了类型为BadRequestException的异常时,Hystrix会将他包装在HystrixBadRequestException中抛出,这样就不会触发后续的fallback逻辑。
异常获取:
当Hystrix命令因为异常进入服务降级逻辑之后,往往需要对不同异常做针对性的处理,那么我们如何来获取当前抛出的异常呢?
在以传统继承方式实现的Hystrix命令中,我们可以用getFallback()方法通过getExecutionException()方法来获取具体的异常,通过判断来进入不同的处理逻辑。
除了传统的实现方式之外,注解配置也同样可以实现异常的获取。它的实现也非常简单,只需要在fallback实现方法的参数中增加对Throwable e对象的定义,这样在方法内部就可以获取触发服务降级的具体异常内容了。比如:
public class Demo01 {
@HystrixCommand(fallbackMethod = "fallback", ignoreExceptions = ArithmeticException.class)
public String test() {
// do something
return "hello";
}
public void fallback(Throwable throwable) {
System.out.println(throwable.getMessage());
}
}
命令名称,分组以及线程池划分:
以继承方式实现的Hystrix命令使用类名作为默认的命令,我们也可以在构造函数中通过Setter静态类来设置,比如:
public class Demo02 extends HystrixCommand {
@Override
protected Object run() throws Exception {
return null;
}
protected Demo02() {
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("groupCode"))
.andCommandKey(HystrixCommandKey.Factory.asKey("commandName"))
// .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("threadPoolName"))
)
;
}
}
从上面Setter的使用中可以看到,我们并没有直接设置命令名称,而是先调用了withGroupKey来设置命令组名,然后才通过andCommanKey来设置命令。这是因为在Setter的定义中,只有withGroupKey静态函数可以创建Setter实例,所以GroupKey是每个Setter必须的参数,而CommandKey则是一个可选参数。
通过设置命令组,Hystrix根据组来组织和统计命令的告警,仪表盘等信息。那么为什么一定要设置命令组呢?因为除了根据组能实现统计之外,Hystrix会让相同组名的命令使用同一个线程池,所以我们需要在创建Hystrix命令时为其指定命令组名来实现默认线程池划分。
如果线程池分配仅仅依靠命令组来划分,那么它就显得不够灵活了,所以Hystrix还提供了HystrixThreadKey来对线程池进行设置,通过它我们可以实现更细粒度的线程池划分。
如果在没有特别指定HystrixThreadPoolKey的情况下,依然会使用命令组的方式来划分线程池。通常情况下,尽量通过HystrixThreadPoolKey的方式来指定线程池的划分,而不是通过组名的默认方式实现划分,因为多个不同的命令可能从业务逻辑上来看属于同一个组,但是往往从实现本身来看需要跟其他命令进行隔离。
上面已经介绍了如何为通过继承实现的HystrxCommand设置命令名称,分组,以及线程池划分。那么当我们使用 @HystrixCommand注解的时候,又该如何设置呢?只需要设置@HystrixCommand注解的commandKey,groupKey以及threadPoolKey属性即可。
Spring Cloud学习 之 Spring Cloud Hystrix(使用详解)的更多相关文章
- Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)
Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...
- Spring Cloud 学习 之 Spring Cloud Eureka(搭建)
Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录 搭建服务注册中心: 注册服务提供者: 高可用注册中心: 搭建服务注册中心: ...
- Spring Cloud:使用Ribbon实现负载均衡详解(下)
在上一篇文章(Spring Cloud:使用Ribbon实现负载均衡详解(上))中,我对 Ribbon 做了一个介绍,Ribbon 可以实现直接通过服务名称对服务进行访问.这一篇文章我详细分析一下如何 ...
- spring基于通用Dao的多数据源配置详解【ds1】
spring基于通用Dao的多数据源配置详解 有时候在一个项目中会连接多个数据库,需要在spring中配置多个数据源,最近就遇到了这个问题,由于我的项目之前是基于通用Dao的,配置的时候问题不断,这种 ...
- Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍
Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍 spring集成 mybatis Spring4.x零配置框架搭建 两年前一直在做后 ...
- spring AOP 之四:@AspectJ切入点标识符语法详解
@AspectJ相关文章 <spring AOP 之二:@AspectJ注解的3种配置> <spring AOP 之三:使用@AspectJ定义切入点> <spring ...
- ASP.NET MVC 5 学习教程:生成的代码详解
原文 ASP.NET MVC 5 学习教程:生成的代码详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...
- IP地址和子网划分学习笔记之《IP地址详解》
2018-05-03 18:47:37 在学习IP地址和子网划分前,必须对进制计数有一定了解,尤其是二进制和十进制之间的相互转换,对于我们掌握IP地址和子网的划分非常有帮助,可参看如下目录详文. ...
- OpenCV学习C++接口 Mat像素遍历详解
OpenCV学习C++接口 Mat像素遍历详解
- 零拷贝详解 Java NIO学习笔记四(零拷贝详解)
转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...
随机推荐
- MyBatis-Plus使用小结
官网: https://mybatis.plus/ https://gitee.com/baomidou/mybatis-plus https://github.com/baomidou/mybati ...
- Python导出数据到Excel表格-NotImplementedError: formatting_info=True not yet implemented
在使用Python写入数据到Excel表格中时出现报错信息记录:“NotImplementedError: formatting_info=True not yet implemented” 报错分析 ...
- L7过拟合欠拟合及其解决方案
1.涉及语句 import d2lzh1981 as d2l 数据1 : d2lzh1981 链接:https://pan.baidu.com/s/1LyaZ84Q4M75GLOO-ZPvPoA 提取 ...
- JMeter在Mac下的安装
其实不论操作系统是Windows.Unix(如Mac OS).Linux(如Ubuntu)等,JMeter所需要的基础环境配置都是类似的,本文介绍JMeter for MAC的安装与环境配置. JMe ...
- C#开发BIMFACE系列34 服务端API之模型对比5:获取模型构件对比差异
系列目录 [已更新最新开发文章,点击查看详细] BIMFACE平台提供了服务端“获取修改构件属性差异”API,其返回的结果也是一个列表,仅针对修改的构件(不包含新增.删除的构件),是指对于一个 ...
- 5. history
https://developer.mozilla.org/zh-CN/docs/Web/API/History_API Browser History APIs
- HBase Filter 过滤器概述
abc 过滤器介绍 HBase过滤器是一套为完成一些较高级的需求所提供的API接口. 过滤器也被称为下推判断器(push-down predicates),支持把数据过滤标准从客户端下推到服务器,带有 ...
- 用SQL查询分析实现类似金蝶K3的收发存明细表
使用SQL查询分析实现类收发存的报表,原始需求在 另外一篇文章 的第四部分.下图是实现需求. 一.准备 删除临时表 [buy]判断是否存在临时表,存在则删除[/buy] if OBJECT_ID('t ...
- Python操作三大主流数据库
Python 标准数据库接口为 Python DB-API,Python DB-API为开发人员提供了数据库应用编程接口. Python 数据库接口支持非常多的数据库,你可以选择适合你项目的数据库: ...
- php静态变量的销毁
什么都不说,先上代码: public function _childrenids($data,$cate_id,$clear=false) { static $arr = array(); if ($ ...