Hystrix
1. Hystrix概述
Netflix的创造了一个调用的库Hystrix实现了断路器图案。在微服务架构中,通常有多层服务调用。
当其中有一个系统有延迟, 它会阻塞整个用户请求
在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(即服务雪崩)
2. 服务熔断
熔断机制是对雪崩效应的一种微服务链路保护机制
熔断机制的注解是@HystrixCommand
1. 添加依赖
我们是在服务端就行服务熔断的, 因此在provider中进行操作
<!--Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
2. 编写controller
package com.wang.springcloud.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.wang.springcloud.pojo.Dept;
import com.wang.springcloud.service.DeptService;
import io.swagger.annotations.ApiModel;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cloud.client.ServiceInstance;
import org.springframework.cloud.client.discovery.DiscoveryClient;
import org.springframework.web.bind.annotation.*;
import java.util.List;
//提供RestFul服务!
@RestController
@ApiModel("Provider Controller")
public class DeptController {
@Autowired
private DeptService deptService;
//注册DiscoveryClient, 注意此时要导入的包是SpringCloud的
//获取一些配置的信息, 得到具体的微服务
@Autowired
private DiscoveryClient client;
@ApiOperation("通过部门编号获得一个部门的信息")
@GetMapping("/dept/get/{id}")
//只要失败, 调用对应的方法
@HystrixCommand(fallbackMethod = "hystrixGet")
public Dept get(@PathVariable("id") @ApiParam("部门的id") Long id){
Dept dept = deptService.queryById(id);
//如果id不存在, 会返回null, 这里抛出异常
if (dept == null) {
throw new RuntimeException("id => " + id + " , 不存在该用户, 或者信息无法找到!");
}
return dept;
}
//备选方法 ==> 当查询的id不存在, 创建对应id的对象, name字段放入提示信息, 失败时返回我们创建的对象!
public Dept hystrixGet(@PathVariable("id") @ApiParam("部门的id") Long id){
//这里可以用链式编程, 是因为我们在pojo的lombok中开启了链式编程的支持
return new Dept()
.setDeptno(id)
.setDname("id => " + id + " , 没有对应的信息, null ---- @Hystrix")
.setDb_source("This database is not exist in Mysql");
}
//注册进来的微服务, 获得一些信息(得到配置文件中的info的信息)
@ApiOperation("微服务的信息")
@GetMapping("/dept/discovery")
public Object discovery() {
//获取微服务列表的清单
List<String> services = client.getServices();
System.out.println("discovery => services: " + services);
//得到一个具体的微服务, 通过具体的微服务ID, applicationName(即为在配置文件中配置的该SpringBoot的名字!)
List<ServiceInstance> instances = client.getInstances("SPRINGCLOUD-PROVIDER-DEPT");
for (ServiceInstance instance : instances) {
System.out.println(
instance.getHost() + " " +
instance.getPort() + " " +
instance.getUri() + " " +
instance.getServiceId()
);
}
//返回这个client就可以了
return this.client;
}
}
注意
-
使用 @HystrixCommand 注解, 并用 fallbackMethod 属性指定服务熔断后调用的方法(此处即抛出异常 ==> 不熔断的话显示为500)
-
熔断调用的方法中, 并用写Mapping的注解, 和调用该方法的服务用同一个url
-
可以将错误信息返回到对象中
3. 在主启动类中声明
package com.wang.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
//启动类
@SpringBootApplication
//在服务启动后自动将该服务注册到Eureka中
@EnableEurekaClient
//服务发现, 这样就可以监控了
@EnableDiscoveryClient
//添加对熔断的支持(启用断路器)
@EnableCircuitBreaker
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class, args);
}
}
注意
- 此处服务熔断要添加的是熔断器的支持 @EnableCircuitBreaker
4. Eureka中的小技巧(没什么用)
可以显示服务的IP地址(最后一行设置为true)
#Eureka配置, 配置该服务注册到哪里(与Server中的url地址一致)
eureka:
client:
service-url:
#向集群发布, 只需要向所有的Eureka发布url就可以了
defaultZone: http://eureka7001.com:7001/eureka/,http://eureka7002.com:7002/eureka/,http://eureka7003.com:7003/eureka/
instance:
instance-id: springcloud-provider-dept-hystrix-8001 #修改Eureka上的默认描述信息
prefer-ip-address: true
效果如下
3. 服务降级
服务降级是发生在客户端上的, 而且依附于feign, 我们在使用了feign的consumer上设置
1. 编写降级的工厂类
我们在API中写降级的工厂类, 从而可以被其他模块调用到
package com.wang.springcloud.service;
import com.wang.springcloud.pojo.Dept;
import feign.hystrix.FallbackFactory;
import org.springframework.stereotype.Component;
import java.util.List;
//降级
@Component
public class DeptClientFallbackFactory implements FallbackFactory {
@Override
public DeptClientService create(Throwable throwable) {
return new DeptClientService() {
@Override
public Dept queryById(Long id) {
return new Dept()
.setDeptno(id)
.setDname("id => " + id + " 没有对应的信息, 客户端提供了降级的信息, 这个服务已经被关闭")
.setDb_source("没有数据");
}
@Override
public List<Dept> queryAll() {
return null;
}
@Override
public Boolean addDept(Dept dept) {
return null;
}
};
}
}
注意
- 要注册到SpringBoot中
- 想要实现服务降级, 要实现FallbackFactory接口, 并重写方法
- 重写方法的类型可以换成我们需要的类型(原来是Object, 支持所有的类型)
- 如果我们使用的类型为接口, 要在返回值中实现该接口
2. 开启服务降级的支持
开启服务降级, 只需要在对应的客户端的配置文件中启用即可
#开启降级feign.hystrix
feign:
hystrix:
enabled: true
4. 服务熔断与服务降级的对比
服务熔断
- 服务端
- 某个服务超时或异常, 引起服务熔断, 相当于保险丝
服务降级
- 客户端
- 从网站整体的请求负载考虑, 当某个服务熔断或者关闭之后, 服务将不再被调用, 此时在客户端我们可以准备一个 FallbackFactory, 返回一个默认的值(缺省值), 整体的服务水平下降了, 比直接挂掉强
相同点
-
目的很一致,都是从可用性可靠性着想,为防止系统的整体缓慢甚至崩溃,采用的技术手段;
-
最终表现类似,对于两者来说,最终让用户体验到的是某些功能暂时不可达或不可用;
-
粒度一般都是服务级别,当然,业界也有不少更细粒度的做法,比如做到数据持久层(允许查询,不允许增删改);
-
自治性要求很高,熔断模式一般都是服务基于策略的自动触发,降级虽说可人工干预,但在微服务架构下,完全靠人显然不可能,开关预置、配置中心都是必要手段;
不同点
-
触发原因不太一样,服务熔断一般是某个服务(下游服务)故障引起,而服务降级一般是从整体负荷考虑;
-
管理目标的层次不太一样,熔断其实是一个框架级的处理,每个微服务都需要(无层级之分),而降级一般需要对业务有层级之分(比如降级一般是从最外围服务开始)
5. Hystrix Dashboard流量监控
Hystrix提供了一个可视化的流量监控
1. Dashboard的编写
1. 导入依赖
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>SpringCloud</artifactId>
<groupId>com.wang</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>SpringCloud-consumer-hystrix-dashboard</artifactId>
<!--实体类以及Web-->
<dependencies>
<dependency>
<groupId>com.wang</groupId>
<artifactId>SpringCloud-API</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--热部署工具-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-devtools</artifactId>
</dependency>
<!--swagger-->
<dependency>
<groupId>io.springfox</groupId>
<artifactId>springfox-boot-starter</artifactId>
</dependency>
<!--Ribbon-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-ribbon</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--Eureka-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-eureka</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--Hystrix-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
<!--Hystrix Dashboard-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-hystrix-dashboard</artifactId>
<version>1.4.6.RELEASE</version>
</dependency>
</dependencies>
</project>
注意
- 此监控是在客户端的, 因此与被监控的客户端的依赖基本相同, 区别是要多配置一个 hystrix-dashboard
2. 编写配置文件
server:
port: 9002
注意
- 这里配置好端口号就好了, 注意配置的端口是空闲的端口
3. 配置主启动类
package com.wang.springcloud;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.hystrix.dashboard.EnableHystrixDashboard;
@SpringBootApplication
//开启Hystrix Dashboard
@EnableHystrixDashboard
public class DeptConsumerDashboard_9002 {
public static void main(String[] args) {
SpringApplication.run(DeptConsumerDashboard_9002.class, args);
}
}
注意
- 在主启动类使用注解 @EnableHystrixDashboard 来开启Hystrix Dashboard
2. 被监控的微服务的编写
1. 前提
注意, 微服务被Hystrix Dashboard监控需要满足以下两点
- 添加了Hystrix依赖
- 添加了actuator依赖
2. 在主启动类配置Servlet
由于Hystrix Dashboard要求微服务注册一个URL地址, 我们在SpringBoot中使用 ServletRegistrationBean 类型来注册一个Servlet的Bean
package com.wang.springcloud;
import com.netflix.hystrix.contrib.metrics.eventstream.HystrixMetricsStreamServlet;
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.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
//启动类
@SpringBootApplication
//在服务启动后自动将该服务注册到Eureka中
@EnableEurekaClient
//服务发现, 这样就可以监控了
@EnableDiscoveryClient
//添加对熔断的支持(启用断路器)
@EnableCircuitBreaker
public class DeptProviderHystrix_8001 {
public static void main(String[] args) {
SpringApplication.run(DeptProviderHystrix_8001.class, args);
}
//增加一个Servlet的Bean
@Bean
public ServletRegistrationBean hystrixMetricsStreamServlet() {
ServletRegistrationBean registrationBean = new ServletRegistrationBean(new HystrixMetricsStreamServlet());
registrationBean.addUrlMappings("/hystrix/actuator/hystrix.stream");
return registrationBean;
}
}
注意
- 这里写的URLMapping就是我们在DashBoard中监控的地址的后缀, 前面为localhost: 端口号
- 这里是固定的写法!
3. Hystrix Dashboard
启动Eureka, Provider和DashBoard, 访问dashboard下的 /hystrix, 输入微服务的URL即可查看