Hystrix
一 Hystrix介绍
1.1 Hystrix是什么
在分布式环境中,许多服务依赖项中的一些必然会失败,或者我们的服务被突发性的高并发访问导致出现问题。Hystrix是一个库,通过添加延迟容忍和容错逻辑,帮助你控制这些分布式服务之间的交互。Hystrix通过隔离服务之间的访问点、停止级联失败和提供回退选项来实现这一点,所有这些都可以提高系统的整体弹性。
https://github.com/Netflix/Hystrix/wiki
1.2 Hystrix为了什么
Hystrix被设计的目标是:
- 对通过第三方客户端库访问的依赖项(通常是通过网络)的延迟和故障进行保护和控制。
- 在复杂的分布式系统中阻止级联故障。
- 快速失败,快速恢复。
- 回退,尽可能优雅地降级。
- 启用近实时监控、警报和操作控制。
1.3 Hystrix解决了什么问题
复杂分布式体系结构中的应用程序有许多依赖项,每个依赖项在某些时候都不可避免地会失败。如果主机应用程序没有与这些外部故障隔离,那么它有可能被他们拖垮。
例如,对于一个依赖于30个服务的应用程序,每个服务都有99.99%的正常运行时间,你可以期望如下:
99.99*30 = 99.7% 可用
也就是说一亿个请求的0.3% = 300000 会失败
如果一切正常,那么每个月有2个小时服务是不可用的
现实通常是更糟糕
当一切正常时,请求看起来是这样的:
当其中有一个系统有延迟时,它可能阻塞整个用户请求:
在高流量的情况下,一个后端依赖项的延迟可能导致所有服务器上的所有资源在数秒内饱和(PS:意味着后续再有请求将无法立即提供服务)
1.4 Hystrix设计原则是什么
- 防止任何单个依赖项耗尽所有容器(如Tomcat)用户线程。
- 甩掉包袱,快速失败而不是排队。
- 在任何可行的地方提供回退,以保护用户不受失败的影响。
- 使用隔离技术(如隔离板、泳道和断路器模式)来限制任何一个依赖项的影响。
- 通过近实时的度量、监视和警报来优化发现时间。
- 通过配置的低延迟传播来优化恢复时间。
- 支持对Hystrix的大多数方面的动态属性更改,允许使用低延迟反馈循环进行实时操作修改。
- 避免在整个依赖客户端执行中出现故障,而不仅仅是在网络流量中。
1.5 Hystrix是如何实现它的目标的
- 用一个HystrixCommand 或者 HystrixObservableCommand (这是命令模式的一个例子)包装所有的对外部系统(或者依赖)的调用,典型地它们在一个单独的线程中执行
- 调用超时时间比你自己定义的阈值要长。有一个默认值,对于大多数的依赖项你是可以自定义超时时间的。
- 为每个依赖项维护一个小的线程池(或信号量);如果线程池满了,那么该依赖性将会立即拒绝请求,而不是排队。
- 调用的结果有这么几种:成功、失败(客户端抛出异常)、超时、拒绝。
- 在一段时间内,如果服务的错误百分比超过了一个阈值,就会触发一个断路器来停止对特定服务的所有请求,无论是手动的还是自动的。
- 当请求失败、被拒绝、超时或短路时,执行回退逻辑(返回一些托底数据)。
- 近实时监控指标和配置变化。
当你使用Hystrix来包装每个依赖项时,上图中所示的架构会发生变化,如下图所示:
每个依赖项相互隔离,当延迟发生时,它会被限制在资源中,并包含回退逻辑,该逻辑决定在依赖项中发生任何类型的故障时应作出何种响应:
如下图,当我们访问的服务B出现问题的时候,不能影响其他的访问
二 Hystrix使用
在SpringCloud中使用Hystrix是非常简单的事情,SpringCloud对Hystrix进行了包装,让我们可以方便的使用,只需要通过注解来指定当我们服务出现问题时候需要执行的返回托底数据的方法即可
2.1 POM 文件
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!--从注册中心中获取服务列表-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-eureka-client</artifactId>
</dependency>
<!--hystrix 依赖-->
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-netflix-hystrix</artifactId>
</dependency>
2.2 application.yml
server:
port: 9000
eureka: #注册中心的地址
client:
service-url:
defaultZone: http://zhangsan:abc@localhost:10000/eureka #curl风格
spring:
application:
name: 12consumer-eureka-hystrix
2.3 controller
import com.netflix.appinfo.InstanceInfo;
import com.netflix.discovery.EurekaClient;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixProperty;
import com.qianfeng.microservice.consumer.pojo.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import org.springframework.web.client.RestTemplate;
/**
* Created by jackiechan on 19-1-8/下午12:00
*
* @Author jackiechan
*/
@RestController
@RequestMapping("/userconsumer")
public class UserController {
@Autowired
private RestTemplate template;
@Autowired
private EurekaClient eurekaClient; //这个对象spring会自动创建不需要我们额外创建
@GetMapping("/info/{id}")
//开启熔断降级
@HystrixCommand(fallbackMethod = "abc") //给当前方法设置失败后的操作,失败后到底做什么fallbackMethod指定一个方法名,当出现问题的时候会去执行这个方法,要求这个方法的参数和返回值必须和当前方法一致
public User getUserById(@PathVariable Long id) throws Exception {
System.out.println("出错的线程" + Thread.currentThread().getName());
InstanceInfo instanceInfo = eurekaClient.getNextServerFromEureka("04PROVIDER-EUREKA", false);//cong euerka上获取指定名字的服务的信息
String url = instanceInfo.getHomePageUrl();//获取服务的地址 也就是 类似于 http://localhost:8000
System.out.println("服务提供者的地址:======>"+url);
User user = template.getForObject(url+"/user/info/" + id, User.class);//请求指定地址并将返回的json字符串转换为User对象
return user;
}
@PostMapping("/save")
public String addUser(@RequestBody User user) {
String result = template.postForObject("http://localhost:8000/user/save/", user, String.class);//post方式请求这个地址,并将user作为参数传递过去(json格式),并将返回结果转换为String类型
return result;
}
public User abc(Long id){
User user = new User();
user.setId(-100);
user.setUsername("刚才失败传递的id是:==>" + id);
return user;
}
}
2.4 主程序
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.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* Created by jackiechan on 19-1-8/下午12:04
*
* @Author jackiechan
*/
@SpringBootApplication
@EnableEurekaClient
@EnableCircuitBreaker//开启熔断机制
public class ConsumerStarter {
public static void main (String[] args){
SpringApplication.run(ConsumerStarter.class,args