您现在的位置是:首页 > 正文

HystrixRPC保护的原理,HystrixCommand命令的执行方法

2024-02-01 01:14:09阅读 1

HystrixCommand命令的执行方法

前面讲到,独立使用HystrixCommand命令主要有以下两个步骤:

(1)继承HystrixCommand类,将正常的业务逻辑实现在继承的run方法中,将回退的业务逻辑实现在继承的getFallback方法中。

(2)使用HystrixCommand类提供的执行启动方法启动命令的执行。

HystrixCommand提供了4个执行启动的方法:execute()、queue()、observe()和toObservable()。

 execute()方法

HystrixCommand的execute()方法以同步堵塞方式执行run()。一旦开始执行该命令,当前线程就会阻塞,直到该命令返回结果,然后才能继续执行下面的逻辑。

HystrixCommand的execute()方法的使用示例如下:

package com.crazymaker.demo.hystrix;
...
@Slf4j
public class HystryxCommandExcecuteDemo
{
 public static final int COUNT = 5;
 /**
 *测试同步执行
 */
 @Test
 public void testExecute() throws Exception
 {
 /**
 *使用统一配置类
 */
 HystrixCommand.Setter setter = SetterDemo.buildSetter(
 "group-1",
 "testCommand",
 "testThreadPool");
 /**
 *循环 5 次
 */
 for (int i = 0; i < COUNT; i++)
 {
 String result =
 new HttpGetterCommand(HELLO_TEST_URL, setter).execute();
 log.info("result={}", result);
 }
 Thread.sleep(Integer.MAX_VALUE);
 }
}

运行测试用例前需要启动demo-provider实例,确保其REST接口/api/demo/hello/v1可以正常访问。执行上面的程序,输出的主要结果如下:

08:20:05.488 [hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - 第1次请求-> begin...
08:20:08.698 [hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - 第1次请求-> end!
08:20:08.708 [main] INFO c.c.d.h.CommandTester - 第1次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}
08:20:08.710 [hystrix-testThreadPool-2] INFO c.c.d.h.HttpGetterCommand - 第2次请求-> begin...
08:20:10.741 [hystrix-testThreadPool-2] INFO c.c.d.h.HttpGetterCommand - 第2次请求-> end!
08:20:10.744 [main] INFO c.c.d.h.CommandTester - 第2次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}
08:20:10.751 [hystrix-testThreadPool-3] INFO c.c.d.h.HttpGetterCommand - 第3次请求-> begin...
08:20:12.766 [hystrix-testThreadPool-3] INFO c.c.d.h.HttpGetterCommand - 第3次请求-> end!
08:20:12.767 [main] INFO c.c.d.h.CommandTester - 第3次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}
//省略后面的重复请求输出

从结果中可以看出,Hystrix会从线程池中取一个线程来执行HttpGetterCommand命令的run()方法,命令执行过程中,main线程一直在等待其返回值。

queue()方法

HystrixCommand的queue()方法以异步非阻塞方式执行run()方法,该方法直接返回一个Future对象。可通过Future.get()拿到run()的返回结果,但Future.get()是阻塞执行的。

HystrixCommand的queue()方法的使用示例程序如下:

package com.crazymaker.demo.hystrix;
...
@Slf4j
public class HystryxCommandExcecuteDemo
{
 @Test
 public void testQueue() throws Exception {
 /**
 *使用统一配置
 */
 HystrixCommand.Setter setter = getSetter(
 "group-1",
 "testCommand",
 "testThreadPool");
 List<Future<String>> flist = new LinkedList<>();
 /**
 *同时发起5个异步的请求
 */
 for (int i = 0; i < COUNT; i++) {
 Future<String> future = new HttpGetterCommand(TEST_URL, setter).queue();
 flist.add(future);
 }
 /**
 *统一获取异步请求的结果
 */
 Iterator<Future<String>> it = flist.iterator();
 int count = 1;
 while (it.hasNext()) {
 Future<String> future = it.next();
 String result = future.get(10, TimeUnit.SECONDS);
 log.info("第{}次请求的结果:{}", count++, result);
 }
 Thread.sleep(Integer.MAX_VALUE);
 }
}
运行这个示例程序前需要启动demo-provider实例,确保它的REST接口/api/demo/hello/v1可以正常访问。执行这个示例程序,主要的输出结果如下:
08:30:54.618 [hystrix-testThreadPool-2] INFO c.c.d.h.HttpGetterCommand - 第3次请求-> begin...
08:30:54.618 [hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - 第4次请求-> begin...
08:30:54.618 [hystrix-testThreadPool-4] INFO c.c.d.h.HttpGetterCommand - 第5次请求-> begin...
08:30:54.618 [hystrix-testThreadPool-3] INFO c.c.d.h.HttpGetterCommand - 第2次请求-> begin...
08:30:54.618 [hystrix-testThreadPool-5] INFO c.c.d.h.HttpGetterCommand - 第1次请求-> begin...
08:30:58.358 [hystrix-testThreadPool-2] INFO c.c.d.h.HttpGetterCommand - 第3次请求-> end!
08:30:58.358 [hystrix-testThreadPool-3] INFO c.c.d.h.HttpGetterCommand - 第2次请求-> end!
08:30:58.358 [hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - 第4次请求-> end!
08:30:58.358 [hystrix-testThreadPool-4] INFO c.c.d.h.HttpGetterCommand - 第5次请求-> end!
08:30:58.358 [hystrix-testThreadPool-5] INFO c.c.d.h.HttpGetterCommand - 第1次请求-> end!
08:30:58.364 [main] INFO c.c.d.h.CommandTester - 第1次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}
08:30:58.365 [main] INFO c.c.d.h.CommandTester - 第2次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}
08:30:58.365 [main] INFO c.c.d.h.CommandTester - 第3次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}
08:30:58.365 [main] INFO c.c.d.h.CommandTester - 第4次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}
08:30:58.365 [main] INFO c.c.d.h.CommandTester - 第5次请求的结果:{"status":200,"msg":"操作成功","data":{"hello":"world"}}

实际上,前面介绍的HystrixCommand的execute()方法是在内部使用queue().get()的方式完成同步调用的。

observe()方法

HystrixCommand的observe()方法会返回一个响应式编程Observable主题,可以为该主题对象注册上Subscriber观察者回调实例,或者注册上Action1不完全回调实例来响应式处理命令的执行结果。

HystrixCommand的observe()方法的使用示例程序如下:
package com.crazymaker.demo.hystrix;
...
@Slf4j
public class HystryxCommandExcecuteDemo
{
 @Test
 public void testObserve() throws Exception
 {
 /**
 *使用统一配置类
 */
 HystrixCommand.Setter setter = SetterDemo.buildSetter(
 "group-1",
 "testCommand",
 "testThreadPool");
 Observable<String> observe = new HttpGetterCommand(HELLO_TEST_URL, setter).observe();
 Thread.sleep(1000);
 log.info("订阅尚未开始!");
 //订阅3次
 observe.subscribe(result -> log.info("onNext result={}", result),
 error -> log.error("onError error={}", error));
 observe.subscribe(result -> log.info("onNext result ={}", result),
error -> log.error("onError error={}", error));
 observe.subscribe(
 result -> log.info("onNext result={}", result),
 error -> log.error("onError error ={}", error),
 () -> log.info("onCompleted called"));
 Thread.sleep(Integer.MAX_VALUE);
 }
}

运行这个示例程序前需要启动demo-provider实例,确保其REST接口/api/demo/hello/v1可以正常访问。执行这个示例程序,主要的输出结果如下:

[hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - req1 begin...
[main] INFO c.c.d.h.HystryxCommandExcecuteDemo - 订阅尚未开始!
[hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - req1 end: {"respCode":0,"respMsg":"操作成功","datas":{"hello":"wor
[hystrix-testThreadPool-1] INFO c.c.d.h.HystryxCommandExcecuteDemo - onNext result=req1:{"respCode":0,"respMsg":"操作成功","dat
[hystrix-testThreadPool-1] INFO c.c.d.h.HystryxCommandExcecuteDemo - onNext result =req1:{"respCode":0,"respMsg":"操作成功","da
[hystrix-testThreadPool-1] INFO c.c.d.h.HystryxCommandExcecuteDemo - onNext result=req1:{"respCode":0,"respMsg":"操作成功","dat
[hystrix-testThreadPool-1] INFO c.c.d.h.HystryxCommandExcecuteDemo - onCompleted called

通过执行结果可以看出,如果HystrixCommand的run()方法执行成功,就会触发订阅者的onNext()和onCompleted()回调方法,如果执行异常,就会触发订阅者的onError()回调方法。

调用HystrixCommand的observe()方法会返回一个热主题(Hot Observable)。什么是热主题呢?就是无论主题是否存在观察者订阅,都会自动触发执行它的run()方法。另外还有一点,observe()方法所返回的主题可以重复订阅。

 toObservable()方法

HystrixCommand的toObservable()方法会返回一个响应式编程Observable主题。同样可以为该主题对象注册上Subscriber观察者回调实例,或者注册上Action1不完全回调实例,来响应式处理命令的执行结果。不过,与observe()返回的主题不同,Observable主题返回的是冷主题,并且只能被订阅一次。

HystrixCommand的toObservable()方法的使用示例程序如下:
package com.crazymaker.demo.hystrix;
...
@Slf4j
public class HystryxCommandExcecuteDemo
{
 @Test
 public void testToObservable() throws Exception
 {
 /**
 *使用统一配置类
 */
 HystrixCommand.Setter setter = SetterDemo.buildSetter(
 "group-1",
 "testCommand",
 "testThreadPool");
 for (int i = 0; i < COUNT; i++)
 {
 Thread.sleep(2);
 new HttpGetterCommand(HELLO_TEST_URL, setter)
 .toObservable()
 .subscribe(result -> log.info("result={}", result),
 error -> log.error("error={}", error)
 );
 }
 Thread.sleep(Integer.MAX_VALUE);
 }
}

运行这个示例前需要启动demo-provider实例,确保它的REST接口/api/demo/hello/v1可以正常访问。执行这个示例程序,主要的输出结果如下:

[hystrix-testThreadPool-5] INFO c.c.d.h.HttpGetterCommand - req3 begin...
[hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - req2 begin...
[hystrix-testThreadPool-3] INFO c.c.d.h.HttpGetterCommand - req4 begin...
[hystrix-testThreadPool-2] INFO c.c.d.h.HttpGetterCommand - req1 begin...[hystrix-testThreadPool-4] INFO c.c.d.h.HttpGetterCommand - req5 begin...
[hystrix-testThreadPool-4] INFO c.c.d.h.HttpGetterCommand - req5 end: {"respCode":0,...}
[hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - req2 end: {"respCode":0, ...}
[hystrix-testThreadPool-3] INFO c.c.d.h.HttpGetterCommand - req4 end: {"respCode":0, ...}
[hystrix-testThreadPool-2] INFO c.c.d.h.HttpGetterCommand - req1 end: {"respCode":0, ...}
[hystrix-testThreadPool-5] INFO c.c.d.h.HttpGetterCommand - req3 end: {"respCode":0, ...}
[hystrix-testThreadPool-1] INFO c.c.d.h.HystryxCommandExcecuteDemo - result=req2:{ ...}
[hystrix-testThreadPool-3] INFO c.c.d.h.HystryxCommandExcecuteDemo - result=req4:{ ...}
[hystrix-testThreadPool-5] INFO c.c.d.h.HystryxCommandExcecuteDemo - result=req3:{ ...}
[hystrix-testThreadPool-4] INFO c.c.d.h.HystryxCommandExcecuteDemo - result=req5:{ ...}
[hystrix-testThreadPool-2] INFO c.c.d.h.HystryxCommandExcecuteDemo - result=req1:{ ...}

什么是冷主题(Cold Observable)?就是在获取主题的时候不会立即触发执行,只有在观察者订阅时才会执行内部的HystrixCommand命令的run()方法。

toObservable()方法和observe()方法之间的区别如下:

(1)observe()和toObservable()虽然都返回了Observable主题,但是observe()返回的是热主题,toObservable()返回的是冷主题。

(2)observe()返回的主题可以被多次订阅,而toObservable()返回的主题只能被单次订阅。

在使用@HystrixCommand注解时,observe()方法对应的执行模式为EAGER,toObservable()方法对应的执行模式为LAZY,具体如下:

//此注解使用observe()方法来获取主题
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.EAGER)
//此注解使用toObservable()方法来获取冷主题
@HystrixCommand(observableExecutionMode = ObservableExecutionMode.LAZY)

由于本文侧重结合Spring Cloud介绍Hystrix核心原理,并没有涉及@HystrixCommand注解的单独使用,因此不对@HystrixCommand注解进行详细介绍。

 HystrixCommand的执行方法之间的关系

实际上,Hystrix内部总是以Observable的形式作为响应式的调用,不同执行命令方法只是进行了相应Observable转换。Hystrix的核心类HystrixCommand尽管只返回单个结果,但是确实是基于RxJava的Observable主题类来实现的。

前面介绍到,获取HystrixCommand命令的结果可以使用execute()、queue()、observe()和toObservable()这4个方法,它们之间的关系如图5-2所示。

HystrixRPC保护的原理,HystrixCommand命令的执行方法

图5-2 4个方法之间的关系

execute()、queue()、observe()和toObservable()这4个方法之间的调用关系如下:

(1)toObservable()返回一个冷主题,订阅者可以订阅结果。

(2)observe()首先调用toObservable()获得一个冷主题,再创建一个ReplaySubject重复主题去订阅该冷主题,然后将重复主题转化为热主题。因此,调用observe()会自动触发执行run()/construct()方法。

(3)queue()调用了toObservable().toBlocking().toFuture()。

详细来说,queue()首先通过toObservable()来获得一个冷主题,然后通过toBlocking()将该冷主题转换成BlockingObservable阻塞主题,该主题可以把数据以阻塞的方式发出来,最后通过toFuture方法把BlockingObservable阻塞主题转换成一个Future异步回调实例,并且返回该Future实例。但是,queue()自身并不会阻塞,消费者可以自己决定如何处理Future的异步回调操作。

(4)execute()调用了queue().get(),阻塞消费者的线程,同步获取Future异步回调实例的结果。

除了定义HystrixCommand这个具备同步获取结果的命令处理器之外,Hystrix还定义了另一个只具备响应式编程能力的命令处理器HystrixObservableCommand,该命令没有实现execute()和queue()两个方法,仅仅实现了observe()和toObservable()两个方法,如图5-3所示。

HystrixRPC保护的原理,HystrixCommand命令的执行方法

图5-3 HystrixObservableCommand纯响应式命令处理器的执行方法

网站文章

  • SpringBoot配置

    SpringBoot配置

    配置文件分类Spring boot是基于约定的,所以很多配置都有默认值,但如果想使用自己的配置替换默认配置的话,就可以使用application.properties或者 application.ym...

    2024-02-01 01:14:00
  • 【设计模式】桥接模式

    【设计模式】桥接模式

    设计模式大总结 桥梁模式是对象的结构模式。又称为柄体(Handle and Body)模式或接口(Interface)模式。桥梁模式的用意是“将抽象化(Abstraction)与实现化(Implementation)脱耦,使得二者可以独立地变化”。一。简解桥接模式顾名思义,是在两个模块间搭的一个桥,这两个模块就是抽象化和实现 抽象由抽象类或者接口声明的,作为一类类的抽象 实现则是继承抽象类或者

    2024-02-01 01:13:32
  • Android中根据手机的分辨率dp和px之间的转换

    public class UnitUtil { /** * 根据手机的分辨率从 dp 的单位 转成为 px(像素) */ public static int dp2px(Context context, float dpValue) { final float scale = context.getResources().getDisplay...

    2024-02-01 01:13:24
  • 理解KMP算法

    理解KMP算法

    KMP算法用于字符串模式匹配,目标串T=[T1.....Tn],模式串P=[P1....Pm],这里n>=m,i代表T的索引指针,j代表P的索引指针,传统字符串匹配算法,在Ti!=Pj的时候,i指针需要回退到i-j的位置,同时j回退到0,也就是模式串P开始的位置,这样传统算法的匹配过程的复杂度就是O(m*n)。其实在Ti!=Pj的时候,i指针不需要回退,[Ti-j+1...Ti-1]和[P1...

    2024-02-01 01:13:16
  • 微电子学与计算机期刊2019,微电子与通信工程学院研究生两篇论文被人工智能顶级会议AAAI 2019接收...

    微电子学与计算机期刊2019,微电子与通信工程学院研究生两篇论文被人工智能顶级会议AAAI 2019接收...

    近日,微电子与通信工程学院研究生有两篇文章被国际人工智能领域顶级学术会议AAAI 2019(AAAI Conference on Artificial Intelligence 2019)录用。AAA...

    2024-02-01 01:12:50
  • linux get current thread count and system threads limit

    get current thread count grep -s '^Threads' /proc/[0-9]*/status | awk '{ sum += $2; } END { print sum; }'get the system thread limitcat /proc/sys/kernel/threads-max转载于:https://www.cnblogs.com/l...

    2024-02-01 01:12:37
  • Linux实战操作

    Linux实战操作

    LinuxLinux目录一、pandas是什么?二、使用步骤1.引入库2.读入数据总结Linux目录登录系统后,在当前命令窗口下输入命令:查看linux目录ls / 或 ll /bin->user/...

    2024-02-01 01:12:30
  • vue3.0 store抽离,模块导入

    store/index.js import Vue from &#39;vue&#39; import Vuex from &#39;vuex&#39; import getters from &#3...

    2024-02-01 01:12:02
  • 适合初学者!超详细的vscode的C++自定义头文件的配置! 热门推荐

    适合初学者!超详细的vscode的C++自定义头文件的配置! 热门推荐

    vscode的环境下,C++自定义头文件的解决方法!

    2024-02-01 01:11:55
  • std::find和std::string::find

    总之,std::find,最好拿来查找任意元素。std::string::find,最好拿来查找子字符串。

    2024-02-01 01:11:48