spring cloud 用的是 hystrix,是一个容错组件。
Hystrix实现了 超时机制和断路器模式。
Hystrix是Netflix开源的一个类库,用于隔离远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。主要有以下几点功能:
- 防止雪崩
多个微服务之间进行复杂的通信时,如果有一个服务出现问题,就会引发雪崩效应,导致整个系统瘫痪。Spring Cloud Hystrix提供了一个类似于保险丝的作用,当服务不可用的时候,Hystrix打开断路器,不再进行服务通信,从而保证自身服务的可用性。 - 服务降级
服务降级就是在系统高并发的情况下,可以将一些边缘服务进行降级(服务暂停),将资源优先供给核心服务的处理。 - 为系统提供保护机制: 在依赖的服务出现高延迟或失败时,为系统提供保护和控制。
- 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中运行。
- 跳闸机制:当某服务失败率达到一定的阈值时,Hystrix可以自动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix为每个请求都的依赖都维护了一个小型线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。防止级联失败。
- 快速失败:Fail Fast。同时能快速恢复。侧重点是:(不去真正的请求服务,发生异常再返回),而是直接失败。
- 监控:Hystrix可以实时监控运行指标和配置的变化,提供近实时的监控、报警、运维控制。
- 回退机制:fallback,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。
- 自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。前面有介绍。
需要注意的是:熔断只是作用在消费方(**调用方**)这一端, 所以这里讲的都是消费方的操作.
1. 安装
1.1 maven安装
<!-- 关于单独引入Hystrix依赖,网上有的文章说引入的依赖是spring-cloud-starter-hystrix,其实官方不在推荐使用,推荐使用spring-cloud-starter-netflix-hystrix。 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
1.2 gradle安装
//关于单独引入Hystrix依赖,网上有的文章说引入的依赖是spring-cloud-starter-hystrix,其实官方不在推荐使用,推荐使用spring-cloud-starter-netflix-hystrix。
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
2. Hystrix整合RestTemplate
2.1 基本整合
2.1.1 保证项目内有RestTemplate的Bean
/**
* 因为RestTemplate不需要维护状态或属性等信息, 所以可以做成单例让Spring管理.
* @return
*/
@Bean
@LoadBalanced
public RestTemplate getRestTemplate(){
return new RestTemplate();
}
2.1.2 新增Service接口并实现
import org.springframework.web.bind.annotation.PathVariable;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/7 13:14
* @Project:epec
* @Description:调用订单中心收藏模块接口
* @Modified By:wangfan
* @Version: V1.0
*/
public interface OrderCenterCollectService {
/**
* 调用订单中心收藏模块接口,通过用户ID获取收藏的订单列表
* @param userId
* @return
*/
public String getOrderList(@PathVariable Integer userId);
}
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import learn.wangfan.springcloud.user_center.service.OrderCenterCollectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/7 13:16
* @Project:epec
* @Description:TODO
* @Modified By:wangfan
* @Version: V1.0
*/
@Service
public class OrderCenterCollectServiceImpl implements OrderCenterCollectService {
@Autowired
private RestTemplate restTemplate;
/**
* 调用订单中心收藏模块接口,通过用户ID获取收藏的订单列表
* @param userId
* @return
*/
@Override
@HystrixCommand(defaultFallback = "getOrderListHystrixFallBack")
public String getOrderList(Integer userId) {
//注册中心服务名
String orderCenterName = "ORDER-CENTER";
//拼接HTTP URL请求
String url = "http://"+orderCenterName+"/order/user/collect/list/"+userId;
//发起请求, 接收结果
String result = restTemplate.getForObject(url, String.class);
return result;
}
/**
* getOrderList方法发生服务降级的处理FallBack
* @return
*/
public String getOrderListHystrixFallBack(){
return "ORDER-CENTER服务发生服务异常, 已降级处理.";
}
}
2.1.3 调用方新增Controller接口
import learn.wangfan.springcloud.user_center.service.OrderCenterCollectService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/5 13:17
* @Project:epec
* @Description:TODO
* @Modified By:wangfan
* @Version: V1.0
*/
@RestController
public class CollectController {
//订单中心模块Service
@Autowired private OrderCenterCollectService orderCenterCollectService;
/**
* 获取用户收藏的订单列表版本v5
* @param userId
* @return
*/
@GetMapping("/collect/orders/v5/{userId}")
public Object getCollectOrdersV5(@PathVariable("userId") Integer userId){
String result = orderCenterCollectService.getOrderList(userId);
return result;
}
}
2.1.4 调用方启动类新增注解@EnableCircuitBreaker
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.web.bind.annotation.GetMapping;
@EnableEurekaClient
@SpringBootApplication
@EnableCircuitBreaker
public class UserCenterApplication {
public static void main(String[] args) {
SpringApplication.run(UserCenterApplication.class, args);
}
}
2.1.5 测试
打开user_center项目和 order_center项目. 正常请求url: http://localhost:9000/collect/orders/v5/1 返回结果:
{“code”:”0000”,”msg”:”success”,”rersult”:[{“name”:”name1”,”id”:”100034”},{“name”:”name2”,”id”:”100035”},{“name”:”name3”,”id”:”100036”}]}
关闭order_center项目, 再次请求, 结果:
ORDER-CENTER服务发生服务异常, 已降级处理.
OK, 服务成功降级.
2.2 进阶
3. Hystrix整合Feign
3.1 基本整合
3.1.1 创建Hystrix类
新增OrderCenterCollectClientV3HystrixFallBack类, 实现OrderCenterCollectClientV3接口, 并加上@Component
让Spring管理
import learn.wangfan.springcloud.user_center.feignclients.OrderCenterCollectClientV3;
import org.springframework.stereotype.Component;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/7 12:23
* @Project:epec
* @Description:OrderCenterCollectClientV3的服务降级FallBack
* @Modified By:wangfan
* @Version: V1.0
*/
@Component
public class OrderCenterCollectClientV3HystrixFallBack implements OrderCenterCollectClientV3 {
@Override
public String getOrderList(Integer userId) {
return "ORDER_CENTER服务发升降级";
}
}
Hystrix类其实就是一个普通java类, 但是需要以下几点。
- 必须加上
@Component
注解让Spring管理。 - Hystrix类需要实现
@FeignClient
, 当@``FeignClient
接口中的方法调用失败或超时时,会调用该实现类的方法3.1.2 调用方配置文件开启Hystrix功能
#开始Feign对Hystrix的支持
#Dalston SR1(待定)之后的版本默认关闭hystrix对feign的支持,如果想要使用fallback功能这里必须启用
feign.hystrix.enabled=true
3.1.3 调用方修改@FeignClient修饰的类
```java import learn.wangfan.springcloud.user_center.hystrix.OrderCenterCollectClientV3HystrixFallBack; import org.springframework.cloud.openfeign.FeignClient; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable;
/**
- @Author:壹心科技BCF项目组 wangfan
- @Date:Created in 2020/10/6 01:06
- @Project:epec
- @Description:订单中心收藏模块Feign客户端
- @Modified By:wangfan
- @Version: V1.0 */
/**
- value: 调用服务名称
path: 统一的路径前缀 */ @FeignClient(value = “ORDER-CENTER”, path = “/order”, fallback = OrderCenterCollectClientV3HystrixFallBack.class) public interface OrderCenterCollectClientV3 {
/**
- 通过用户ID获取收藏的订单列表
- @param userId
- @return */ @GetMapping(“/user/collect/list/{userId}”) public String getOrderList(@PathVariable Integer userId);
}
<a name="I7YtS"></a>
### 3.1.4 测试
打开user_center项目和 order_center项目. 正常请求url: [http://localhost:9000/collect/orders/v3/1](http://localhost:9000/collect/orders/v3/1) 返回结果:
> {"code":"0000","msg":"success","rersult":[{"name":"name1","id":"100034"},{"name":"name2","id":"100035"},{"name":"name3","id":"100036"}]}
关闭order_center项目, 再次请求, 结果:
> ORDER_CENTER服务发升降级
OK, 服务成功降级.
<a name="GT8rz"></a>
## 3.2 进阶
<a name="eb21ed61"></a>
### 3.2.1 使用fallbackFactory检查具体错误
<a name="Plvn8"></a>
#### 3.2.1.1 新增类实现`FallbackFactory<T>`
```java
import feign.FeignException;
import feign.hystrix.FallbackFactory;
import learn.wangfan.springcloud.user_center.feignclients.OrderCenterCollectClientV3;
import org.springframework.stereotype.Component;
/**
* @Author:壹心科技BCF项目组 wangfan
* @Date:Created in 2020/10/7 12:37
* @Project:epec
* @Description:实现FallbackFactory<OrderCenterCollectClientV3>接口,按照不同异常处理熔断降级
* @Modified By:wangfan
* @Version: V1.0
*/
@Component
public class OrderCenterCollectClientV3FallBackFactory implements FallbackFactory<OrderCenterCollectClientV3> {
@Override
public OrderCenterCollectClientV3 create(Throwable cause) {
return new OrderCenterCollectClientV3() {
@Override
public String getOrderList(Integer userId) {
//针对不同的异常有不一样的处理
System.out.println("ORDER_CENTER服务发生异常: "+cause);
if (cause instanceof FeignException.InternalServerError){
return "ORDER_CENTER由于远程服务报错, 已降级处理";
}else if (cause instanceof RuntimeException){
return "ORDER_CENTER运行时异常, 已降级处理";
}else {
return "ORDER_CENTER未知异常, 已降级处理";
}
}
};
}
}
3.2.1.2 @FeignClient 新增fallbackFactory属性
@FeignClient(value = "ORDER-CENTER", path = "/order",fallbackFactory = OrderCenterCollectClientV3FallBackFactory.class)
需要注意的是: fallback 属性会覆盖 fallbackFactory属性, 所以如果确定使用fallbackFactory.
3.2.1.3 测试
启动user_center项目, 关闭order_center项目, 浏览器输入url: http://localhost:9000/collect/orders/v3/1 获得结果:
ORDER_CENTER运行时异常, 已降级处理
4. Hystrix信号量隔离与线程隔离
4.1 为什么要隔离
假设这样一个场景:
在一个分布式系统中,服务之间都是相互调用的,如下图所示:
我们容器(Tomcat)配置的线程个数为1000, 用户请求发送到服务消费端customer, 消费端分别请求了4服务提供方: Dependency A
、Dependency H
、 Dependency I
、 Dependency P
。其中服务Dependency I
的并发量非常的大,需要500个线程来执行,但是恰好Dependency I
挂了或者本来就执行很慢,那么这500个线程很可能就堵死在这了。那么剩下的共可用的线程为500个, 随着并发量的增大,剩余服务挂掉的风险就会越来越大,最后导致整个系统的所有服务都不可用,直到系统宕机。这就是服务的雪崩效应
。
为了应对这种情况, Hystrix使用做资源隔离的方式把,每个customer对provider的服务请求都隔离开来,服务之间互不影响。这样就不会出现雪崩效应
。
4.2 线程池隔离
在Hystrix中使用独立的线程池对应每一个服务提供者,来隔离和限制这些服务,于是,某个服务提供者的高延迟或者饱和资源受限只会发生在该服务提供者对用的线程池中。不会影响customer对其他provider的调用线程资源。
比如说,当客户端customer向服务端provider发送请求时,Hystrix给服务Dependency I
分配了10个线程,只要超过了这个并发量就走降级服务,就算服务Dependency I
挂了,最多也就导致服务Dependency I
不可用,容器的10个线程不可用了,但是不会影响系统中的其他服务。
上图是线程池资源隔离示意图,当用户请求服务A和服务I的时候,tomcat的线程(图中蓝色箭头标注)会将请求的任务交给服务A和服务I的内部线程池里面的线程(图中橘色箭头标注)来执行,
tomcat的线程就可以去干别的事情去了,当服务A和服务I自己线程池里面的线程执行完任务之后,就会将调用的结果返回给tomcat的线程,从而实现资源的隔离,
当有大量并发的时候,服务内部的线程池的数量就决定了整个服务的并发度,
例如服务Dependency A
的线程池大小为10个,当同时有12请求时,只会允许10个任务在执行,其他的任务被放在线程池队列中,或者是直接走降级服务,此时,如果服务Dependency A
挂了,就不会造成大量的tomcat线程被服务Dependency A
拖死,服务I依然能够提供服务。整个系统不会受太大的影响。
4.3 信号量隔离
信号量的资源隔离只是起到一个开关的作用,本身只是个计数器。
例如,服务Dependency X
的信号量大小为10,那么同时只允许10个tomcat的线程(此处是tomcat的线程,而不是服务X的独立线程池里面的线程)来访问服务Dependency X
,
其他的请求直接走fallback时报处理,从而达到限流保护的作用。
4.4 二者的比较与选择
线程池隔离
与信号量隔离
的主要区别还是在于线程与普通int类型变量的区别。
线程池隔离 | 信号量隔离 | |
---|---|---|
线程 | 与调用线程非相同线程 | 与调用线程相同(jetty线程) |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(最大线程池大小) | 支持(最大信号量上限 |
Hystrix默认使用的是线程池隔离
。 既然信号量隔离
比线程池隔离
性能好开销小, 为什么还依然默认线程池隔离
呢? 因为线程池支持异步请求可以解放tomcat 的 worker线程阻塞。 还支持异常隔离,如果当前线程池出问题不会影响其他线程池。而信号量归根到底只是一个计数器, 还是运行在tomcat 的 worker线程中的。
有了区别就有选择: 如果程序够健壮能够保证不会出异常,而且请求延时足够小、响应超级快,则可以使用信号量隔离,否则还是建议使用线程池隔离。
4.5 相关配置
配置 | 说明 | 可选 | 默认 |
---|---|---|---|
hystrix.command.default.execution.isolation.strategy | 隔离策略 | Thread 线程隔离 Semaphore 信号量隔离 |
Thread |
hystrix.command.default.execution.isolation.thread.timeoutInMilliseconds | 命令执行超时时间 | 1000ms | |
hystrix.command.default.execution.timeout.enabled | 执行是否启用超时 | true false |
true |
hystrix.command.default.execution.isolation.thread.interruptOnTimeout | 发生超时是是否中断 | true false |
true |
hystrix.command.default.execution.isolation.semaphore.maxConcurrentRequests | 最大并发请求数 该参数当使用ExecutionIsolationStrategy.SEMAPHORE策略时才有效。如果达到最大并发请求数,请求会被拒绝。理论上选择semaphore size的原则和选择thread size一致,但选用semaphore时每次执行的单元要比较小且执行速度快(ms级别),否则的话应该用thread。semaphore应该占整个容器(tomcat)的线程池的一小部分。 |
10 |
5. Hystrix仪表盘
5.1 安装
maven安装:
<!-- Actuator监控 -->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<!-- 关于单独引入Hystrix依赖,网上有的文章说引入的依赖是spring-cloud-starter-hystrix,其实官方不在推荐使用,推荐使用spring-cloud-starter-netflix-hystrix。 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
<!-- //Hystrix仪表盘, 仪表盘是监控hystrix的运行状态的, 所以需要依赖Hystrix以及actuator上报信息 -->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId> spring-cloud-starter-netflix-hystrix-dashboard </artifactId>
</dependency>
gradle安装
//关于单独引入Hystrix依赖,网上有的文章说引入的依赖是spring-cloud-starter-hystrix,其实官方不在推荐使用,推荐使用spring-cloud-starter-netflix-hystrix。
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix'
//Actuator监控
implementation 'org.springframework.boot:spring-boot-starter-actuator'
//Hystrix仪表盘, 仪表盘是监控hystrix的运行状态的, 所以需要依赖Hystrix以及actuator上报信息
implementation 'org.springframework.cloud:spring-cloud-starter-netflix-hystrix-dashboard'
5.2启动类加注解@EnableHystrixDashboard
5.3 测试
启动时控制台会打印上报信息的actuator路径
启动成功后打开浏览器输入URL:http://localhost:{port}/actuator/hystrix.stream
如果报错的话是actuator没有开启端点, 需要配置actuator开启端点:
#开启Actuator所有端点
management.endpoints.web.exposure.include=*
打开仪表盘的默认地址:http://localhost:{port}/hystrix
在输入框输入上报路径地址:http://localhost:{port}/actuator/hystrix.stream, 结果: