Hystrix
1.分布式系统面临的问题
复杂分布式体系结构中的应用程序有数十个依赖关系,每个依赖关系在某些时候将不可避免的失败
2.服务雪崩
-
多个微服务之间调用的时候,假设微服务A调用微服务B和微服务C,微服务B和微服务C又调用其他的微服务,这就是所谓的“扇出”,如果扇出的链路上某个微服务的调用响应时间过长或者不可用,对微服务A的调用就会占用越来越多的系统资源,进而引起系统崩溃,所谓的“雪崩效应”
-
对于高流量的应用来说,单一的后端依赖可能会导致所有服务器上的所有资源都在几秒钟内饱和。比失败更糟糕的是,这些应用程序,还可能导致服务之间的延迟增加,备份队列,线程和其他系统资源紧张,导致整个系统发生更多级联故障,这些都表示需要对故障和延迟进行距离和管理,以便单个依赖关系的失败,不能取消整个应用程序或系统。
3.Hystrix的概念和作用
3.1Hystrix的概念
-
Hystrix是一个用于处理分布式系统的延迟和熔断的开源库,在分布式系统里,许多依赖不可避免的会调用失败,比如超时,异常等,Hystrix能够保证在一个依赖出问题的时候,不会导致整个服务失败,避免级联故障,以提高分布式系统的弹性。
-
“断路器”本身是一种开关装置,当某个服务单元发生故障之后,通过断路器的故障监控(雷士熔断保险丝),向调用方返回一个服务预期的,可处理的备选相应(FallBack)而不是长时间的等待或者抛出调用方法无法处理的异常,这样就可以保证了服务调用方法的线程不会被长时间占用,不必要的占用,从而避免了故障在分布式系统中的蔓延,乃至雪崩
3.2Hystrix的作用
-
服务熔断
-
熔断机制是对应雪崩效用的一中微服务链路保护机制
-
当扇出链路的某个微服务不可用或者响应时间太长时,会进行服务的降级,进而熔断该节点微服务的调用,快速返回错误的响应信息。当检测到该节点微服务调用响应正常回复调用链路。在SpringCloud框架里熔断机制通过Hystrix实现。Hystrix会监控微服务间调用的状况,当时白的调用到一定阈值,缺省是5秒内20次调用失败就会启动熔断机制。熔断机制的注解是@HystrixCommand
-
-
服务降级
-
接近实时的监控
-
服务限流
4.Hystrix实战
4.1服务熔断
前提:基于负载均衡及Ribbon这一边文章中的Ribbon实战项目的基础上添加内容
-
创建一个新的modules,项目名称为复制provider-dept-hystrix-8004,然后将provider-dept--8001的代码全部复制进去
-
在原来依赖的基础上,添加hystrix依赖
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- 修改DeptController中的代码
package com.zixin.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.zixin.springcloud.pojo.Dept;
import com.zixin.springcloud.service.DeptService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
@RestController
@RequestMapping("/provider/dept")
public class DeptController {
@Autowired
private DeptService deptService;
@GetMapping("/findDeptById/{deptno}")
//发生异常的时候调用备用方法
@HystrixCommand(fallbackMethod = "getDeptHystrix")
public Dept getDept(@PathVariable Long deptno){
Dept dept = deptService.findDeptById(deptno);
if(dept == null){
throw new RuntimeException("id=" + deptno + ",不存在该用户或信息无法找到");
}
return dept;
}
//备选方法
public Dept getDeptHystrix(@PathVariable Long deptno){
Dept dept = new Dept();
dept.setDeptno(deptno.toString());
dept.setDname("id=" + deptno + ",没有对应信息,null--@Hystrix");
dept.setDb_source("no this database in MySQL");
return dept;
}
}
-
启动类重命名为ProviderDeptHystrixApplication,并在该启动类上添加
@EnableCircuitBreaker
注解,用于开启熔断器 -
运行provider-dept-hystrix-8004、eureka-server-7001以及consumer-dept-8001项目,访问
http://localhost:7001/
,可以看到provider-dept-hystrix-8004服务已经注册上去了
然后在访问
http://localhost:9001/consumer/dept/findDeptById/1
,可以获取到对应的数据
当访问数据库中没有的数据时,如http://localhost:9001/consumer/dept/findDeptById/5
,页面中就会显示对应的提示信息,至此服务熔断功能得以实现
4.2服务降级
前提:基于负载均衡及Ribbon和Feign-使用接口方式调用服务这两篇文章中的Ribbon实战项目的基础上添加内容
- 在springcloud-api项目的pom.xml中添加feign的依赖
<!--feign-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-feign</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
- 在springcloud-api项目中com.zixin.springcloud.service包下创建impl文件夹,并在该文件夹下创建FeignDeptFallBackFactoryServiceImpl类,实现FallbackFactory接口
package com.zixin.springcloud.service.impl;
import com.zixin.springcloud.pojo.Dept;
import com.zixin.springcloud.service.FeignDeptService;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
//服务降级
@Component
public class FeignDeptFallBackFactoryServiceImpl implements FallbackFactory<FeignDeptService> {
@Override
public FeignDeptService create(Throwable throwable) {
return new FeignDeptServiceFactory() {
@Override
public int addDept(Dept dept) {
return 0;
}
@Override
public Dept findDeptById(Long deptno) {
Dept dept = new Dept();
dept.setDeptno(deptno.toString());
dept.setDname("id="+deptno+"没有对应的信息,客户端提供了降级的信息,这个服务已经被关闭了");
dept.setDb_source("没有数据~");
return dept;
}
@Override
public List<Dept> getDeptList() {
return null;
}
};
}
}
- 接着在
@FeignClient
注解里面添加fallbackFactory
属性,由于配置出现异常时调用的类,全部代码如下:
package com.zixin.springcloud.service;
import com.zixin.springcloud.pojo.Dept;
import com.zixin.springcloud.service.impl.FeignDeptFallBackFactoryServiceImpl;
import com.zixin.springcloud.service.impl.FeignDeptServiceFactory;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Component
@FeignClient(value = "PROVIDER-DEPT/provider/dept", fallbackFactory = FeignDeptFallBackFactoryServiceImpl.class)
public interface FeignDeptService {
//添加部门
@PostMapping("/addDept")
int addDept(Dept dept);
//根据id查询部门信息
@GetMapping("/findDeptById/{deptno}")
Dept findDeptById(@PathVariable Long deptno);
//查询所有部门信息
@GetMapping("/findDeptList")
List<Dept> getDeptList();
}
- 在consumer-dept-feign项目的配置文件中添加下面的配置,开启降级服务
#开启服务降级
feign.hystrix.enabled=true
- 运行provider-dept-8001、eureka-server-7001以及consumer-dept-feign项目,访问
http://localhost:9002/consumer/dept/findDeptById?deptno=2
,出现下面的错误
原因:接收参数时使用@PathVariable
注解
解决方法:把原来都使用到@PathVariable
注解的地方都换成@RequestParam
- 运行provider-dept-8001、eureka-server-7001以及consumer-dept-feign项目,访问
http://localhost:9002/consumer/dept/findDeptById?deptno=2
,出现下面的错误
原因:当使用feign传参数的时候,需要加上@RequestParam注解,否则对方服务无法识别参数
解决方法:使用feign传参数的时候,加上@RequestParam注解,比如我这里的FeignDeptService 类中的findDeptById方法
package com.zixin.springcloud.service;
import com.zixin.springcloud.pojo.Dept;
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import java.util.List;
@Component
@FeignClient(value = "PROVIDER-DEPT/provider/dept")
public interface FeignDeptService {
//添加部门
@PostMapping("/addDept")
int addDept(Dept dept);
//根据id查询部门信息
@GetMapping("/findDeptById")//路径中的参数也去掉
Dept findDeptById(@RequestParam("deptno") Long deptno);//这里要换成@RequestParam注解
//查询所有部门信息
@GetMapping("/findDeptList")
List<Dept> getDeptList();
}
- 再次运行provider-dept-8001、eureka-server-7001以及consumer-dept-feign项目,访问
http://localhost:9002/consumer/dept/findDeptById?deptno=2
,得到下面结果
当把provider-dept-8001停掉,访问http://localhost:9002/consumer/dept/findDeptById?deptno=2
,就会出现提示信息如下
服务熔断与服务降级的区别
-
服务熔断:服务端,某个服务超时或者异常时引起熔断
-
服务降级:客户端,从整体网站请求负载考虑,即当某个服务熔断或者关闭之后,服务将不再被调用,此时在客户端,我们可以准备一个FallbackFactory,返回一个默认的值(缺省值),整个服务水平下降了,但还是可以用,比直接挂掉好
4.3Dashboard流监控
前提:基于负载均衡及Ribbon和Feign-使用接口方式调用服务这两篇文章中的Ribbon实战项目的基础上添加内容
- 新创建一个Modules,名称为consumer-hystrix-dashboard,然后引入hystrix和hystrix-dashboard依赖
<dependencies>
<!--hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
- 在配置文件application.properties中添加该项目的服务端口号
#服务端口号
server.port=9003
- 在src/main/java目录下创建com.zixin.springcloud包,并在该包下创建启动类ConsumerHystrixDashboard
package com.zixin.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
import org.springframework.web.bind.annotation.PathVariable;
//开启hystrix监控
@EnableHystrixDashboard
@SpringBootApplication
public class ConsumerHystrixDashboard {
public static void main(String[] args) {
SpringApplication.run(ConsumerHystrixDashboard.class, args);
}
}
-
运行consumer-hystrix-dashboard项目,访问
http://localhost:9003/
,出现下面的界面,说明项目运行成功
-
访问
http://localhost:9003/hystrix
,可以看到如下的监控页面信息
-
在provider-dept-hystrix-8004项目中的启动类中添加一个Bean
package com.zixin.springcloud;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
import org.mybatis.spring.annotation.MapperScan;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.boot.web.servlet.ServletRegistrationBean;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
//开启熔断器
@EnableCircuitBreaker
@EnableEurekaClient
@MapperScan(basePackages = "com.zixin.springcloud.mapper")
@SpringBootApplication
public class ProviderDeptHystrixApplication {
private static final Logger logger = LoggerFactory.getLogger(ProviderDeptHystrixApplication.class);
public static void main(String[] args) {
logger.debug("-------ProviderDeptHystrixApplication正在启动-----");
SpringApplication.run(ProviderDeptHystrixApplication.class, args);
}
//增加一个Servlet
@Bean
public ServletRegistrationBean servletRegistrationBean(){
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/hystrix.stream");
return registrationBean;
}
}
说明:这里的/hystrix.stream映射路径对应hystrix监控页面的默认路径,如下面红色框起来的那部分
-
运行consumer-hystrix-dashboard、eureka-server-7001以及provider-dept-hystrix-8004项目,访问
http://localhost:7001/
,可以看到provider-dept-hystrix-8004 服务已经注册到eureka注册中心,如下所示
-
访问
http://localhost:8004/provider/dept//findDeptById?deptno=1
,也可以获取到数据,如下所示
-
访问
http://localhost:9003/hystrix
,也可以看到hystrix的监控页面,如下所示
-
访问
http://localhost:8004/hystrix.stream
,可以得到如下的数据
-
在hystrix监控页面中输入对应的监控地址,监控毫秒数以及监控服务名称
-
点击Monitor Stream按钮,跳转到如下页面
说明:
-
七种颜色对应关系
-
一个圈
实心圆:共有两种含义,它通过颜色的变化代表了实例的健康程度,它的健康程度从绿色<黄色<橙色<红色递减,该实心圆除了颜色的变化之外,它的大小也会根据实例的请求流量发生变化,流量越大,该实心圆就越大,所以通过该实心圆的展示,就可以在大量的实例中快速发现故障实例和高压力实例
- 一条线:用来记录2分钟内流量的相对变化,可以通过它来观察流量的上升和下降趋势
比如访问多次http://localhost:8004/provider/dept//findDeptById?deptno=1
请求,那个圈也跟着变大,那条线就会有一定的弧度
- 全图说明