创建请求命令:

​ 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(使用详解)的更多相关文章

  1. Spring Cloud 学习 之 Spring Cloud Eureka(源码分析)

    Spring Cloud 学习 之 Spring Cloud Eureka(源码分析) Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 ...

  2. Spring Cloud 学习 之 Spring Cloud Eureka(搭建)

    Spring Boot版本:2.1.4.RELEASE Spring Cloud版本:Greenwich.SR1 文章目录 搭建服务注册中心: 注册服务提供者: 高可用注册中心: 搭建服务注册中心: ...

  3. Spring Cloud:使用Ribbon实现负载均衡详解(下)

    在上一篇文章(Spring Cloud:使用Ribbon实现负载均衡详解(上))中,我对 Ribbon 做了一个介绍,Ribbon 可以实现直接通过服务名称对服务进行访问.这一篇文章我详细分析一下如何 ...

  4. spring基于通用Dao的多数据源配置详解【ds1】

    spring基于通用Dao的多数据源配置详解 有时候在一个项目中会连接多个数据库,需要在spring中配置多个数据源,最近就遇到了这个问题,由于我的项目之前是基于通用Dao的,配置的时候问题不断,这种 ...

  5. Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍

    Spring4.X + spring MVC + Mybatis3 零配置应用开发框架搭建详解(1) - 基本介绍 spring集成 mybatis Spring4.x零配置框架搭建 两年前一直在做后 ...

  6. spring AOP 之四:@AspectJ切入点标识符语法详解

    @AspectJ相关文章 <spring AOP 之二:@AspectJ注解的3种配置> <spring AOP 之三:使用@AspectJ定义切入点> <spring ...

  7. ASP.NET MVC 5 学习教程:生成的代码详解

    原文 ASP.NET MVC 5 学习教程:生成的代码详解 起飞网 ASP.NET MVC 5 学习教程目录: 添加控制器 添加视图 修改视图和布局页 控制器传递数据给视图 添加模型 创建连接字符串 ...

  8. IP地址和子网划分学习笔记之《IP地址详解》

    2018-05-03 18:47:37   在学习IP地址和子网划分前,必须对进制计数有一定了解,尤其是二进制和十进制之间的相互转换,对于我们掌握IP地址和子网的划分非常有帮助,可参看如下目录详文. ...

  9. OpenCV学习C++接口 Mat像素遍历详解

    OpenCV学习C++接口 Mat像素遍历详解

  10. 零拷贝详解 Java NIO学习笔记四(零拷贝详解)

    转 https://blog.csdn.net/u013096088/article/details/79122671 Java NIO学习笔记四(零拷贝详解) 2018年01月21日 20:20:5 ...

随机推荐

  1. 绕过XSS过滤姿势总结

    0x01 弹窗关键字检测绕过 基本WAF都针对常用的弹窗函数做了拦截,如alert().prompt().confirm(),另外还有代码执行函数eval(),想要绕过去也比较简单,我们以alert( ...

  2. Python 编程环境搭建(Windows 系统中)

    由于大家普遍使用 Windows 系统,所以本文只介绍 Windows 系统中 Python 环境的安装. 在 Windows 中安装 Python 与安装普通软件没什么差别,下载所需版本的安装包后, ...

  3. 基础_TCP/IP

    概念明确: 1:TCP/IP代表传输控制协议/网际协议,指的是一系列协议  为什么会叫TCP/IP.因为用的多, 2:HTTP 是属于应用层的协议 3:OSI七层模型和TCP/IP 平等,只是TCP/ ...

  4. 基于thinkphp3.2.3开发的CMS内容管理系统 - ThinkPHP框架

    基于thinkphp3.2.3开发的CMS内容管理系统 thinkphp版本:3.2.3 功能: --分类栏目管理 --文章管理 --用户管理 --友情链接管理 --系统设置 目前占时这些功能,更多功 ...

  5. Soul Android app 悬浮view以及帖子中view的联动刷新逆向分析

    Soul app是我司的竞品,对它的语音音乐播放同步联动的逻辑很感兴趣,于是就开启了一波逆向分析. 下面看代码,以及技术分析,直接步入正轨,哈哈. 我们根据https://github.com/xin ...

  6. C++调用TensorFlow

    在使用C++调用TensorFlow接口时出现的问题,网上没有资料,问了老师才知道的. Exception ignored in: <module 'threading' from 'E:\\t ...

  7. Java的自动装箱

    JDK5的新特性自动装箱:把基本类型转换为包装类类型自动拆箱:把包装类类型转换为基本类型 注意一个小问题: 在使用时,Integer x = null;代码就会出现NullPointerExcepti ...

  8. 想学习CTF的一定要看这篇,让你学习效率提升80%

    在学习CTF过程中你是否遇到这样的情况: 下定决心想要学习CTF,不知道从哪里开始? 找了一堆CTF相关的知识学习,但是知识点太凌乱,没有统一明确的学习路径. 又或者理论学习完,没有相应的实操环境? ...

  9. pytorch Model Linear实现线性回归CUDA版本

    实验代码 import torch import torch.nn as nn #y = wx + b class MyModel(nn.Module): def __init__(self): su ...

  10. ubuntu(Linux)下,查看CPU性能集合

    CPU: 详细:cat /proc/cpuinfo型号:grep "model name" /proc/cpuinfo |awk -F ':' '{print $NF}'数量:ls ...