一、分布式系统面临的问题
- 在微服务架构体系下,服务间的调用错综复杂,交织成一张大网。如果其中某个节 点突然无法正常工作,则访问它的众多服务都会被卡住,进而有更多服务被卡住,系统 中的线程、CPU、内存等资源有可能被迅速耗尽,最终整个服务体系崩溃。
-
二、Hystrix介绍
Hystrix 是一个用于处理分布式系统的延迟和容错的开源库,在分布式系统里,许多 依赖不可避免的会调用失败,比如超时、异常等,Hystrix 能够保证在一个依赖出问题的 情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。
- “断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故 障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应 (FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服 务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延, 乃至雪崩。
Hytrix 能够提供服务降级、服务熔断、服务限流、接近实时的监控等方面的功能。
三、服务熔断机制
熔断机制是应对雪崩效应的一种微服务链路保护机制。 当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该 节点微服务的调用,快速响应错误信息。当检测到该节点微服务调用响应正常后恢复调用链 路。在 SpringCloud 框架里熔断机制通过 Hystrix 实现。Hystrix 会监控微服务间调用的状况, 当失败的调用到一定阈值,缺省是 5 秒内 20 次调用失败就会启动熔断机制。熔断机制的注 解是@HystrixCommand。
三、熔断-代码部分
熔断指的是provider方的熔断,如果provider方出现的问题,熔断后别的就不在调用这个方法。
1. 在provider工程中添加依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2. 在provider主启动类上添加 @EnableCircuitBreaker 注解
/**
* @date: 2021/2/28 15:33
* @author: 易学习
* 下面两个注解功能大致相同,但是这个版本不需要添加
* @EnableDiscoveryClient: 启用发现服务功能,不局限于Eureka注册中心
* @EnableEurekaClient: 启用Eureka客户端功能,必须是Eureka注册中心
*
* @EnableCircuitBreaker: 启用断路器功能【服务熔断】
*/
@EnableCircuitBreaker
@SpringBootApplication
public class ApplicationProvider {
public static void main(String[] args) {
SpringApplication.run(ApplicationProvider.class);
}
}
3. 在common 工程中添加一个 ResultEntity ,统一作为AJAX请求和远程服务调用返回值
package com.atguigu.spring.cloud.util;
/**
* 整个项目统一使用这个类型作为Ajax请求或远程方法调用返回响应的数据格式
* @author
*
* @param <T>
*/
public class ResultEntity<T> {
public static final String SUCCESS = "SUCCESS";
public static final String FAILED = "FAILED";
public static final String NO_MESSAGE = "NO_MESSAGE";
public static final String NO_DATA = "NO_DATA";
/**
* 操作成功,不需要返回数据
* @return
*/
public static ResultEntity<String> successWithoutData() {
return new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
}
/**
* 操作成功,需要返回数据
* @param data
* @return
*/
public static <E> ResultEntity<E> successWithData(E data) {
return new ResultEntity<>(SUCCESS, NO_MESSAGE, data);
}
/**
* 操作失败,返回错误消息
* @param message
* @return
*/
public static <E> ResultEntity<E> failed(String message) {
return new ResultEntity<>(FAILED, message, null);
}
private String result;
private String message;
private T data;
public ResultEntity() {
}
public ResultEntity(String result, String message, T data) {
super();
this.result = result;
this.message = message;
this.data = data;
}
@Override
public String toString() {
return "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
}
public String getResult() {
return result;
}
public void setResult(String result) {
this.result = result;
}
public String getMessage() {
return message;
}
public void setMessage(String message) {
this.message = message;
}
public T getData() {
return data;
}
public void setData(T data) {
this.data = data;
}
}
4. provider工程中的controller方法
/**
* @HystrixCommand 注解通过 fallbackMethod属性指定断路情况下要调用的备用方法
* @param name
* @return
*/
@HystrixCommand(fallbackMethod= "getUserBackUp")
@RequestMapping("/provider/get/user")
public ResultEntity<User> getUser(@RequestParam String name){
// 如果这个方法出现 错误/超时 就会去 备用方法 【超时默认为1秒】
return ResultEntity.successWithData(new User(1,"aaa","男"));
}
/**
* 备选方案
* @param name
* @return
*/
public ResultEntity<User> getUserBackUp(@RequestParam String name){
return ResultEntity.failed("熔断机制触发!");
}
5.注意:
- 这只是熔断部分,没有远程调用,所以不用在common的service中写同名接口
- 浏览器访问的时候直接访问provider的controller【测试】
四、服务降级机制
- 服务降级处理时在客户端(consumer端)实现完成的,和provider没有关系。
- 当某个Consumer访问一个provider却迟迟得不到响应时预先设定好一个解决方案,而不是一直等待。
1.common工程添加 Hystrix依赖
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2. common工程中需要定义一个工厂类
实现 FallbackFactory 接口【泛型为Common中的远程调用Service】
/**
* @date: 2021/3/5 21:36
* @author: 易学习
* 1.实现Consumer端服务降级的功能
* 2.实现FallbackFactory接口时要传入@FeignClient接口类型
* 3.在create()方法中返回一个@FeignClient注解标注的接口类型的对象,当Provider调用失败后,会执行这个对象的对应方法
*/
@Component
public class MyFallBackFactory implements FallbackFactory<UserRemoteService> {
@Override
public UserRemoteService create(Throwable throwable) {
return new UserRemoteService() {
@Override
public ResultEntity<User> getUser(String name) {
return ResultEntity.failed("consumer端降级!!");
}
};
}
}
3. 修改common的远程 调用接口
common工程中原来的远程调用接口 UserRemoteService 的@FeignClients要添加 FallbackFactory属性为自己写的工厂.class
fallbackFactory属性指定provider不可用时提供备用方案的工厂对象
4.feign-consumer端启动hystrix
【在配置文件中启动】
feign:
hystrix:
enabled: true