(4)能够对RPC调用提供接近实时的监控和警报。
监控信息包括请求成功、请求失败、请求超时和线程拒绝。如果对特定服务RPC调用的错误百分比超过阈值,后续的RPC调用就会自动失败,一段时间内停止对该服务的所有请求。
前面已经介绍Spring Cloud在调用处理器中是使用HystrixCommand命令封装RPC调用,从而实现RPC保护。
HystrixCommand简介
================
Hystrix使用命令模式并结合RxJava的响应式编程和滑动窗口技术实现了对外部服务RPC调用的保护。
Hystrix实现了HystrixCommand和HystrixObservableCommand两个命令类,用于封装需要保护的RPC调用。由于其中的HystrixObservableCommand命令不具备同步执行的能力,只具备异步执行能力,而HystrixCommand命令却都具备,并且Spring Cloud中重点使用HystrixCommand命令,因此本章将以HystrixCommand命令为重点介绍Hystrix的原理和使用。
HystrixCommand的使用
=================
如果不是在Spring Cloud的开发环境中使用HystrixCommand命令,就需要增加其Maven的依赖坐标,设置如下:
com.netflix.hystrix
hystrix-core
独立使用HystrixCommand命令主要有以下两个步骤:
(1)继承HystrixCommand类,将正常的业务逻辑实现在继承的run方法中,将回退的业务逻辑实现在继承的getFallback方法中。
(2)使用HystrixCommand类提供的启动方法启动命令的执行。
HystrixCommand命令的run方法是异步调用(或者同步调用)时被调度时执行的方法,getFallback方法是当run执行异常(或超时等)时的回退方法。
使用HystrixCommand命令时,需要通过它的启动方法(如execute)来启动其执行,这个过程有点像使用Thread时通过start方法启动run方法的执行。
HystrixCommand命令的完整执行过程比较复杂,简化版本的HystrixCommand命令的执行过程如图5-1所示。
图5-1 简化版本的HystrixCommand命令的执行过程
下面通过继承HystrixCommand创建一个简单的HTTP请求命令,并且对HTTP请求过程中执行的总次数、失败的总次数进行统计,具体的代码如下:
package com.crazymaker.demo.hystrix;
//省略import
@Slf4j
public class HttpGetterCommand extends HystrixCommand
{
private String url;
//run方法是否执行
private boolean hasRun = false;
//执行的次序
private int index;
//执行的总次数,线程安全
private static AtomicInteger total = new AtomicInteger(0);
//失败的总次数,线程安全
private static AtomicInteger failed = new AtomicInteger(0);
public HttpGetterCommand(String url, Setter setter)
{
super(setter);
this.url = url;
}
@Override
protected String run() throws Exception
{
hasRun = true;
index = total.incrementAndGet();
log.info(“req{} begin…”, index);
String responseData = HttpRequestUtil.simpleGet(url);
log.info(" req{} end: {}", index, responseData);
return “req” + index + “:” + responseData;
}
@Override
protected String getFallback()
{
//是否直接失败
boolean isFastFall = !hasRun;
if (isFastFall)
{
index = total.incrementAndGet();
}
if (super.isCircuitBreakerOpen())
{
HystrixCommandMetrics.HealthCounts hc =
super.getMetrics().getHealthCounts();
log.info(“window totalRequests:{},errorPercentage:{}”,
hc.getTotalRequests(), //滑动窗口总的请求数
hc.getErrorPercentage()); //滑动窗口出错比例
}
//熔断器是否打开
boolean isCircuitBreakerOpen = isCircuitBreakerOpen();
log.info(“req{} fallback: 熔断{},直接失败 {},失败次数{}”,
index,
isCircuitBreakerOpen,
isFastFall,
failed.incrementAndGet());
return “req” + index + “:调用失败”;
}
}
以上自定义的HTTP请求命令HttpGetterCommand继承了HystrixCommand,并且实现了该基类的run和getFallback两个方法。在构造函数中,使用HystrixCommand.Setter配置实例对该基类的实例进行了初始化。
HttpGetterCommand的测试用例代码如下:
package com.crazymaker.demo.hystrix;
…
@Slf4j
public class HystryxCommandExcecuteDemo
{
/***测试HttpGetterCommand */
@Test
public void testHttpGetterCommand() throws Exception
{
/**
*构造配置实例
*/
HystrixCommand.Setter setter = HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“group-1”))
.andCommandKey(HystrixCommandKey.Factory.asKey(“command-1”))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“threadPool-1”));
/**测试HttpGetterCommand */
String result =new HttpGetterCommand(HELLO_TEST_URL, setter)
.execute();
log.info(“result={}”, result);
}
}
用例中首先构造了一个配置实例setter,配置了非常基础的命令组Key(GroupKey)、命令Key(CommandKey)、线程池Key(ThreadPoolKey)3个配置项,然后创建了HttpGetterCommand实例并使用execute()执行该命令,执行的结果大致如下:
[hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - req1 begin…
[hystrix-testThreadPool-1] INFO c.c.d.h.HttpGetterCommand - req1 fallback: 熔断false,直接失败false,失败次数 1
[main] INFO c.c.d.h.HystryxCommandExcecuteDemo - result=req1:调用失败
这里的HttpGetterCommand实例所请求的地址是一个常量,其值如下:
/**
*演示用地址: demo-provider的REST接口 /api/demo/hello/v1
*根据实际的地址调整
*/
public static final String HELLO_TEST_URL =
“http://crazydemo.com:7700/demo-provider/api/demo/hello/v1”;
为了演示启动请求失败的过程,这里特意没有启动demo-provider服务,所以从上面的执行结果中可以看到,由于HTTP请求失败,因此getFallback()回退方法被成功地执行了。
HystrixCommand的配置内容和方式
======================
HystrixCommand命令的配置方式之一是使用HystrixCommand.Setter配置实例进行配置,简单的配置实例如下:
HystrixCommand.Setter setter = HystrixCommand.Setter
.withGroupKey(HystrixCommandGroupKey.Factory.asKey(“group-1”))
.andCommandKey(HystrixCommandKey.Factory.asKey(“command-1”))
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey(“threadPool-1”));
其中涉及以下3个配置项:
(1)CommandKey:该命令的名称。
(2)GroupKey:该命令属于哪一个组,以帮助我们更好地组织命令。
(3)ThreadPoolKey:该命令所属线程池的名称,相同的线程池名称会共享同一线程池,若不进行配置,则默认使用GroupKey作为线程池名称。
除此之外,还可以通过HystrixCommand.Setter配置实例,整体设置一些其他的属性集合,如:
(1)CommandProperties:与命令执行相关的一些属性集,包括降级设置、熔断器的配置、隔离策略以及一些监控指标配置项等。
(2)ThreadPoolProperties:与线程池相关的一些属性集,包括线程池大小、排队队列的大小等。由于本书的很多用例要用到HystrixCommand.Setter配置实例,因此专门写了一个方法获取配置实例,它的源码如下:
package com.crazymaker.demo.hystrix;
…
@Slf4j
public class SetterDemo
{
public static HystrixCommand.Setter buildSetter(
结尾
这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。
由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!
strixCommand.Setter buildSetter(
结尾
[外链图片转存中…(img-aKenMHAs-1718731338298)]
这不止是一份面试清单,更是一种”被期望的责任“,因为有无数个待面试者,希望从这篇文章中,找出通往期望公司的”钥匙“,所以上面每道选题都是结合我自身的经验于千万个面试题中经过艰辛的两周,一个题一个题筛选出来再次对好答案和格式做出来的,面试的答案也是再三斟酌,深怕误人子弟是小,影响他人仕途才是大过,也希望您能把这篇文章分享给更多的朋友,让他帮助更多的人,帮助他人,快乐自己,最后,感谢您的阅读。
由于细节内容实在太多啦,在这里我花了两周的时间把这些答案整理成一份文档了,在这里只把部分知识点截图出来粗略的介绍,每个小节点里面都有更细化的内容!