文章目录

友情提醒
- 文末获取【真会玩】- SpringCloud Netflix 实战系列
Postman在线脚本
、Github仓库实战源码
- 【真会玩】- SpringCloud Netflix 实战系列有概念有实战,考验动手能力,如果实战中有操作没有达到与本文同等效果的请先仔细检查个人操作与文中相关内容是否一致,如检查无误请私信或评论区留言~
Hystrix
在分布式环境中,存在许多服务依赖关系,但是这些服务中必然存在交互失败的问题
- Hystrix通过
超时机制
和断路器模式
,帮我们控制这些分布式服务之间的交互- Hystrix通过
隔离服务之间的访问点( 远程系统、服务或者第三方库)
、停止跨服务的级联故障
以及提供回退选项
来实现这一点,提高了系统的整体弹性(可用性与容错性)。- Hystrix提供了
监控
,可以实时监控运行指标
和配置
的变化,提供近实时的监控、报警、运维控制。- Hystrix提供了
系统保护机制
,在依赖的服务出现高延迟或失败时,为系统提供保护和控制。- Hystrix提供了
跳闸机制
,当某服务失败率达到一定的阈值时,Hystrix可以自动跳闸,停止请求该服务一段时间。- Hystrix提供了
回退机制(Fallback)
,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。- Hystrix实现了
资源隔离
,为每个请求都的依赖都维护了一个小型线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。防止级联失败。- Hystrix实现了
快速失败(Fail Fast)
,不去真正的请求服务,发生异常再返回,而是直接失败
,同时也能快速恢复- Hystrix实现了
包裹请求
,使用HystrixCommand / HystrixObservableCommand
包裹对依赖的调用逻辑,每个命令在独立线程中运行。- Hystrix实现了
自我修复
,断路器打开一段时间后自动进入半开
状态,可以进行打开/关闭
,半开状态的转换。下面实战的时候会有相关介绍。
降级
向服务方发起请求,判断连接超时
- 将这次请求记录到服务
- 尝试向其他服务器发起请求,还是没有请求成功
- catch异常
- 可以返回重试页面,提供重试入口
- 返回提示信息
隔离
线程隔离(限流)
每发起一个Http请求都会开一个独立线程去处理业务,涉及到了线程消耗的问题,为了避免造成线程任务积压
- 当线程数达到线程池线程数上限的时候直接抛出异常,后面来的任务全部不处理,只处理之前还没处理完的请求,这叫隔离/限流
熔断
我们在向服务方发起请求失败了,给连续失败次数计数
- 达到阈值的时候抛出异常进入异常处理逻辑
依赖集成
在
Feign-Consume
服务请求方添加如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
举个例子🌰
直接使用
new
HystrixTest
类,继承HystrixCommand
抽象类,实现run
、getFallback
方法
public class HystrixTest extends HystrixCommand {
public static void main(String[] args) {
Future<String> future = new HystrixTest(HystrixCommandGroupKey.Factory.asKey("doProcess")).queue();
String reslut = "";
try {
reslut = future.get();
} catch (ExecutionException e) {
e.printStackTrace();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("reslut:" + reslut);
}
@Override
protected Object run() throws Exception {
System.out.println("开始执行...");
//制造异常场景,都知道1/0在java中会抛出ArithmeticException(算术异常)对吧
int i = 1 / 0;
return "执行成功...";
}
/**
* 备用逻辑
*
* @return java.lang.Object
* @author Rhys.Ni
* @date 2022/9/26
*/
@Override
protected Object getFallback() {
return "异常了,走到了getFallback逻辑";
}
//有很多种构造函数,我们只需要一种就可以了
public HystrixTest(HystrixCommandGroupKey group) {
super(group);
}
}
运行从结果可以看出程序进了
run
方法并执行算法抛出了异常,那么异常了程序后续该怎么执行呢?
当程序抛出异常时则会进入备用逻辑
getFallback
方法中,成功执行了备用方法里面的逻辑
Feign整合Hystrix
Fallback
在
Feign-Consumer
服务application.yml
配置文件中添加以下配置
- 默认是关闭的
feign:
hystrix:
enabled: true
new
FeignProviderBack
类,实现FeignConsumerApi
接口,将接口内所有方法都重新实现一遍,这种形式的降级策略就是针对于每一个独立请求的降级
FeignConsumerApi
、ServiceApi
相关代码请参考【真会玩】- SpringCloud Netflix 实战笔记 -【OpenFeign代替Feign】
@Component
public class FeignProviderBack implements FeignConsumerApi {
@Override
public String pingFeignProvider() {
return "降级了,返回了兜底数据";
}
}
在
@FeignClient
注解中添加属性fallback = FeignProviderBack.class
@FeignClient(name = "FeignProvider",fallback = FeignProviderBack.class)
public interface FeignConsumerApi extends ServiceApi {
}
在
ServiceApi
中有一点需要注意
- 不能在类上加
@RequestMapping
注解,否则启动的时候会重复
创建两次相同的方法
并且抛异常启动失败
因此,我们需要去掉
ServiceApi
上的@RequestMapping
注解
我们调用一下
http://localhost:9080/testOpenFeign
接口
- 可以看到确实走进了
FeignProviderBack
中的降级逻辑
FallbackFactory
在
@FeignClient
注解中替换属性fallback = FeignProviderBack.class
为fallbackFactory = FeignProviderBackFactory.class
@FeignClient(name = "FeignProvider",fallbackFactory = FeignProviderBackFactory.class)
public interface FeignConsumerApi extends ServiceApi {
}
new
FeignProviderBackFactory
类,实现FallbackFactory
接口
- 这边可以针对具体业务的Api使用,这里我针对
FeignConsumerApi
来使用
public class FeignProviderBackFactory implements FallbackFactory<FeignConsumerApi> {
@Override
public FeignConsumerApi create(Throwable throwable) {
return new FeignConsumerApi() {
@Override
public String pingFeignProvider() {
return "FallbackFactory 实现降级了,返回了兜底数据";
}
};
}
}
重启
Feign-Consumer
服务,再次调用http://localhost:9080/testOpenFeign
接口
- 可以看到也是生效了的
除此以外我们还可以根据异常类型进行判断执行不同的处理逻辑
@Component
public class FeignProviderBackFactory implements FallbackFactory<FeignConsumerApi> {
@Override
public FeignConsumerApi create(Throwable throwable) {
return new FeignConsumerApi() {
@Override
public String pingFeignProvider() {
if (throwable instanceof RuntimeException) {
return "请求时异常:" + throwable;
} else {
return "FallbackFactory 实现降级了,返回了兜底数据";
}
}
};
}
}
RestTemplate整合Hystrix
- 在启动类
FeignConsumerApplication
加上@EnableCircuitBreaker
注解支持Hystrix
- 有的兄弟可能会问那刚刚Feign集成Hystrix的时候为什么没加这个注解也可以实现?
- 因为Feign默认支持Hystrix,只需要在配置文件中控制配置开关即可
- 在启动类声明
RestTemplate
为单例Bean
@EnableFeignClients
@EnableCircuitBreaker
@SpringBootApplication
public class FeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApplication.class, args);
}
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
new
TestRestService
类
- @HystrixCommand(defaultFallback = “testFallBack”)
- testFallBack为方法名,所以要新增方法
testFallBack
@Service
public class TestRestService {
@Resource
private RestTemplate restTemplate;
@HystrixCommand(defaultFallback = "testFallBack")
public String testOpenFeignWithRest() {
String url = "http://FeignProvider/pingFeignProvider";
ResponseEntity<String> responseEntity = restTemplate.getForEntity(url, String.class);
if (responseEntity.getStatusCode() == HttpStatus.OK) {
String result = responseEntity.getBody();
return result;
} else {
return "Bad Request";
}
}
private String testFallBack() {
return "@HystrixCommand 实现了降级,返回了兜底数据";
}
}
FeignConsumerController
中新增接口/testOpenFeignWithRest
- 注入
TestRestService
服务
@RestController
public class FeignConsumerController {
@Resource
private FeignConsumerApi feignConsumerApi;
@Resource
private TestRestService restService;
@GetMapping("/testOpenFeign")
public String testOpenFeign() {
return feignConsumerApi.pingFeignProvider();
}
@GetMapping("/testOpenFeignWithRest")
public Object testOpenFeignWithRest() {
return restService.testOpenFeignWithRest();
}
}
重启服务调用
/testOpenFeignWithRest
接口
http://localhost:9080/testOpenFeignWithRest
可以看到咱们走到
TestRestService
中逻辑成功降级了
线程隔离&信号量隔离
默认情况下hystrix使用线程池控制请求隔离
线程池隔离:用 Hystrix 自己的线程去执行调用
信号量隔离:直接让 tomcat 线程去调用依赖服务
- 信号量隔离,只是一道关卡,信号量有多少,就允许多少个 tomcat 线程通过它,然后去执行
- 信号量隔离主要维护的是Tomcat的线程,不需要内部线程池,更加轻量级。
监控线程池隔离
监控线程池隔离开启dashboard
在
Feign-Consumer
服务调用方添加如下依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
在
Feign-Consumer
启动类添加@EnableHystrixDashboard
注解
@EnableFeignClients
@EnableCircuitBreaker
@EnableHystrixDashboard
@SpringBootApplication
public class FeignConsumerApplication {
public static void main(String[] args) {
SpringApplication.run(FeignConsumerApplication.class, args);
}
@Bean
RestTemplate restTemplate() {
return new RestTemplate();
}
}
在
Feign-Consumer
配置文件application.yml
中添加以下配置开启所有端点
#开启所有Actuator Web访问端口
management:
endpoints:
web:
exposure:
include: "*"
#配置主机地址白名单
hystrix:
dashboard:
proxy-stream-allow-list: "localhost"
- 重启
Feign-Consumer
服务,打开图形监控页面
localhost:9080/hystrix
输入监控上报接口地址
http://localhost:9080/actuator/hystrix.stream
进入页面后得调用其他接口让数据进行统计,让页面
动起来
Pool Size
:线程池大小,有多少个线程
监控信号量隔离
想要在
dashboard
中监控信号量隔离状态,需要在配置文件中修改隔离策略配置
- 默认
Thread
策略,Thread
|Semaphore
Thread
通过线程数量
来限制并发请求数,可以提供额外的保护
,但有一定的延迟
。一般用于网络调用SEMAPHORE
通过Semaphore Count
来限制并发请求数,适用于无网络
的高并发
请求
隔离策略配置
hystrix:
#隔离策略配置
command:
default:
execution:
isolation:
strategy: Semaphore
重启再次刷新页面,调用其他接口增加信息上报数据流动
因为我们这次切换成了
SEMAPHORE
信号量隔离,可以看到Thread Pools
栏里是没有相关信息数据的
其他配置
hystrix:
dashboard:
#配置主机地址白名单
proxy-stream-allow-list: "localhost"
#隔离策略配置
command:
default:
execution:
isolation:
strategy: Semaphore
thread:
#命令执行超时时间,默认1000ms
timeoutInMilliseconds: 1000
#发生超时是是否中断
interruptOnTimeout: true
semaphore:
#最大并发请求数
#默认10,该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效
#如果达到最大并发请求数,请求会被拒绝
#理论上选择semaphore size的原则和选择thread size一致
#但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread
maxConcurrentRequests: 10
#执行是否启用超时,默认启用true
timeout:
enabled: true
源码&脚本获取地址
- Postman脚本:
https://www.getpostman.com/collections/8a0954746646f0dee9a6
- 脚本可直接复制到Postman使用
Link
方式导入- 实战源码仓库:
https://github.com/RhysNi/SpringCloudFamilyMeals.git