Spring Cloud Hystrix的学习

前言

分布式系统面临的问题

复杂分布式体系结构中的应用程序有许多依赖项,每个依赖项在某些时候都不可避免地会失败。如果主机应用程序没有与这些外部故障隔离,那么它有可能被他们拖垮。

当一切正常时,某一个服务依赖其他的服务

Spring Cloud Hystrix的学习

当其中有一个系统有延迟时,它可能阻塞整个用户请求:

Spring Cloud Hystrix的学习

在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(PS:意味着后续再有请求将无法立即提供服务)

Spring Cloud Hystrix的学习

在微服务架构体系下,服务间的调用错综复杂,交织成一张大网。如果其中某个节点突然无法正常工作,则访问它的众多服务都会被卡住,进而有更多服务被卡住,系统中的线程、CPU、内存等资源有可能被迅速耗尽,最终整个服务体系崩溃。我们管这样的现象叫服务雪崩。

Hytrix

Hystrix 是一个用于处理分布式系统的延迟和容错开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时、异常等,Hystrix 能够保证在一个依赖出问题的情况下,不会导致整体服务失败,避免级联故障,以提高分布式系统的弹性。

断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(类似熔断保险丝),向调用方返回一个符合预期的、可处理的备选响应(FallBack),而不是长时间的等待或者抛出调用方无法处理的异常,这样就保证了服务调用方的线程不会被长时间、不必要地占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩。

Hytrix 能够提供服务降级、服务熔断、服务限流接近实时的监控等方面的功能。

熔断机制

熔断机制是应对雪崩效应的一种微服务链路保护机制。

当筛出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速响应错误信息。当检测到该节点微服务调用响应正常后恢复调用链路。在 SpringCloud 框架里熔断机制通过 Hystrix 实现。Hystrix 会监控微服务间调用的状况,当失败的调用到一定阈值,缺省是 5 秒内 20 次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand。

项目搭建示例:

Spring Cloud Hystrix的学习

在Provider添加Hystrix依赖

<code><dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>/<code>

启动类添加断路器EnableCircuitBreaker注解,开启启用断路器功能

<code>@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class ProviderApplication {

\tpublic static void main(String[] args) {
\t\tSpringApplication.run(ProviderApplication.class, args);
\t}
}/<code>

Common端,添加统一返回的数据封装类

<code>public class ResultEntity<T> {

\tpublic static final String SUCCESS = "SUCCESS";
\tpublic static final String FAILED = "FAILED";
\tpublic static final String NO_MESSAGE = "NO_MESSAGE";
\tpublic static final String NO_DATA = "NO_DATA";
\t/**
\t * 操作成功,不需要返回数据
\t * @return
\t */
\tpublic static ResultEntity<String> successWithoutData() {
\t\treturn new ResultEntity<String>(SUCCESS, NO_MESSAGE, NO_DATA);
\t}
\t/**
\t * 操作成功,需要返回数据
\t * @param data * @return
\t */
\tpublic static <E> ResultEntity<E> successWithData(E data) {
\t\treturn new ResultEntity<>(SUCCESS, NO_MESSAGE, data);
\t}

\t/**
\t * 操作失败,返回错误消息
\t * @param message * @return
\t */
\tpublic static <E> ResultEntity<E> failed(String message) {
\t\treturn new ResultEntity<>(FAILED, message, null);
\t}
\tprivate String result;
\tprivate String message;
\tprivate T data;
\tpublic ResultEntity() {
// TODO Auto-generated constructor stub
\t}
\tpublic ResultEntity(String result, String message, T data) {

\t\tsuper();
\t\tthis.result = result;
\t\tthis.message = message;
\t\tthis.data = data;
\t}
\t@Override
\tpublic String toString() {
\t\treturn "ResultEntity [result=" + result + ", message=" + message + ", data=" + data + "]";
\t}
\tpublic String getResult() {
\t\treturn result;
\t}
\tpublic void setResult(String result) {
\t\tthis.result = result;
\t}
\tpublic String getMessage() {
\t\treturn message;
\t}
\tpublic void setMessage(String message) {
\t\tthis.message = message;
\t}
\tpublic T getData() {
\t\treturn data;
\t}
\tpublic void setData(T data) {
\t\tthis.data = data;
\t}
}/<code>

Provider端

<code>// @HystrixCommand 注解通过 fallbackMethod 属性指定断路情况下要调用的备份方法\t 

@HystrixCommand(fallbackMethod = "getEmpBackup")
\t@RequestMapping("/provider/circuit/breaker/get/emp")
\tpublic ResultEntity<Employee> getEmp(@RequestParam("signal") String signal) {
\t\tif ("exception".equals(signal)) {
\t\t\tthrow new RuntimeException();
\t\t}
\t\tif ("timeout".equals(signal)) {
\t\t\ttry {
\t\t\t\tThread.sleep(5000);
\t\t\t} catch (InterruptedException e) {
\t\t\t\te.printStackTrace();
\t\t\t}
\t\t}
\t\treturn ResultEntity.successWithData(new Employee(666, "sam666", BigDecimal.valueOf(666.66)));
\t}


\tpublic ResultEntity<Employee> getEmpBackup(@RequestParam("signal") String signal) {
\t\treturn ResultEntity.failed("circuit break workded,with signal=" + signal);
\t}/<code>

启动eureka,provider。启动成功后,浏览器输入:

http://localhost:1000/provider/circuit/breaker/get/emp?signal=111,表示服务正常。

Spring Cloud Hystrix的学习

http://localhost:1000/provider/circuit/breaker/get/emp?signal=exception,模拟服务出现异常


Spring Cloud Hystrix的学习

跳转备选服务,getEmpBackup。

http://localhost:1000/provider/circuit/breaker/get/emp?signal=timeout,模拟请求服务超时,


Spring Cloud Hystrix的学习

跳转备选服务,getEmpBackup。

降级机制

服务降级处理是在客户端(Consumer 端)实现完成的,与服务端(Provider 端)没有关系。当某个 Consumer 访问一个 Provider 却迟迟得不到响应时执行预先设定好的一个解决方案,而不是一直等待。

搭建原理示例

Spring Cloud Hystrix的学习

common端添加依赖:

<code><dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>/<code>

配置FallbackFactory

<code>/**
* 请注意自动扫描包的规则
* 比如:feign-consumer 工程需要使用 MyFallBackFactory,
* 那么 MyFallBackFactory 应该在feign-consumer 工程的主启动类所在包或它的子包下
* 简单来说:哪个工程用这个类,哪个工程必须想办法扫描到这个类
*实现Consumer端服务降级功能
* 实现FeignClientFactory接口时要传入@FeignClient注解接口的类型
*在create()方法中返回@FeignClient注解标记类型的对象,当Provider
* 调用失败,会执行这个对象对应的方法
* **/
@Component
public class MyFallBackFactory implements FallbackFactory<EmployeeRemoteService> {

\tpublic EmployeeRemoteService create(final Throwable cause) {
\t\treturn new EmployeeRemoteService() {

//// cause 对象是失败原因对应的异常对象
\t\t\t@RequestMapping("/provider/get/employee/remote")
\t\t\tpublic Employee getEmployeeRemote() {
\t\t\t\treturn null;
\t\t\t}

\t\t\tpublic ResultEntity<Employee> getEmp(String signal) {
\t\t\t\treturn ResultEntity.failed(return ResultEntity.failed("降级机制生效:"+cause.getMessage()););
\t\t\t}
\t\t};
\t}



}/<code>

common的api端添加调用的方法getEmp

<code>/**
* FeignClient注解表示当前接口和一个Provider对应,注解中value属性指定要
* 调用的Provider的微服务名称
* 注解中的fallbackFactory属性指定Provider不可用时提供备用方案的工厂类型
* */
@FeignClient(value = "springcloud-provider",fallbackFactory = MyFallBackFactory.class)
public interface EmployeeRemoteService {

\t@RequestMapping("/provider/get/employee/remote")
\tpublic Employee getEmployeeRemote();

\t@RequestMapping("/provider/circuit/breaker/get/emp")
\tpublic ResultEntity<Employee> getEmp(@RequestParam("signal") String signal);

}/<code>

provider的getEmp方法

<code>@RestController
public class EmployeeController {

\t@RequestMapping("/provider/get/employee/remote")
\tpublic Employee getEmployeeRemote() {
\t\treturn new Employee(12, "Tom", BigDecimal.valueOf(4933.55));
\t}

\t@HystrixCommand(fallbackMethod = "getEmpBackup")
\t@RequestMapping("/provider/circuit/breaker/get/emp")
\tpublic ResultEntity<Employee> getEmp(@RequestParam("signal") String signal) {
\t\tif ("exception".equals(signal)) {
\t\t\tthrow new RuntimeException();
\t\t}
\t\tif ("timeout".equals(signal)) {
\t\t\ttry {
\t\t\t\tThread.sleep(5000);
\t\t\t} catch (InterruptedException e) {
\t\t\t\te.printStackTrace();
\t\t\t}

\t\t}
\t\treturn ResultEntity.successWithData(new Employee(666, "sam666", BigDecimal.valueOf(666.66)));
\t}


\tpublic ResultEntity<Employee> getEmpBackup(@RequestParam("signal") String signal) {
\t\treturn ResultEntity.failed("circuit break workded,with signal=" + signal);
\t}/<code>

feign-consumer端添加配置

<code>feign:
hystrix:
enabled: true/<code>

controller添加调用的方法

<code>@RequestMapping("/feign/consumer/test/fallback")
\tpublic ResultEntity<Employee> testFallBack(@RequestParam("signal")String signal) {
\t\treturn employeeRemoteService.getEmp(signal);
\t}/<code>

启动springcloud-eureka,springcloud-provider,springcloud-feign-consumer

浏览器访问:http://localhost:7000/feign/consumer/test/fallback?signal=111,访问正常


Spring Cloud Hystrix的学习


关闭Provider,再次访问

Spring Cloud Hystrix的学习

降级生效,执行降级方法。再次启动Provider,又可以访问。

监控

新建一个springcloud-dashboard模块。

<code><dependency>
<groupId>org.springframework.cloud</groupId>

<artifactId>spring-cloud-starter-netflix-hystrix-dashboard</artifactId>
</dependency>/<code>

启动类:

<code>// 启用 Hystrix 仪表盘功能
@EnableHystrixDashboard
@SpringBootApplication
public class DashBoardApplication {

\tpublic static void main(String[] args) {
\t\tSpringApplication.run(DashBoardApplication.class, args);
\t}
}/<code>

配置文件:

<code>server:
port: 8000

spring:
application:
name: springcloud-dashboard/<code>

对provider模块进行监控,添加依赖:

<code><dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>/<code>

添加配置:

<code>management:
endpoints:
web:
exposure:
include: hystrix.stream/<code>

启动项目,浏览器输入localhost:8000/hystrix

Spring Cloud Hystrix的学习

查看监控数据

直接查看监控数据本身

http://localhost:1000/actuator/hystrix.stream,

  • 说明 1:http://localhost:1000 访问的是被监控的 provider 工程
  • 说明 2:/actuator/hystrix.stream 是固定格式
  • 说明 3:如果从 provider 启动开始它的方法没有被访问过,那么显示的数据只有“ping:”,要实际访问一个带熔断功能的方法才会有实际数据。

先请求一次才有数据。http://localhost:7000/feign/consumer/test/fallback?signal=111,在访问

http://localhost:1000/actuator/hystrix.stream。

Spring Cloud Hystrix的学习

通过仪表盘工程访问监控数据

第一步:打开仪表盘工程的首页

http://localhost:8000/hystrix

第二步:填入获取监控数据的地址(上面直接查看时使用的地址)

Spring Cloud Hystrix的学习

这时,可以不断刷新之前的熔断的请求,可以看出监控界面数据实时的变化。

http://localhost:1000/provider/circuit/breaker/get/emp?signal=exception

Spring Cloud Hystrix的学习


分享到:


相關文章: