Hystrix基本认识
:::info
在springcloud中有一个熔断器组件Hystrix这个组件的其中一个作用就是处理服务器的故障,防止出现雪崩的情况,我们通过使用这个组件可以实现,当服务器挂掉的时候被调用无法被响应,当超过了我们的熔断器等待时间时,就会失败并且**触发服务降级,熔断器返回兜底数据,从而防止故障的扩散,**当一定时间内达到一定的失败阈值时候,熔断器将启动并开始拒绝后续的请求,直接进入熔断状态。基于熔断降级,熔断器还可以实现限流的操作。
:::
Hystrix功能
资源隔离
- 线程池隔离
- 使用一个线程池来存储当前请求,线程池对请求作处理,设置任务返回处理超时时间,堆积的请求先入线程池队列。这种方式要为每个依赖服务申请线程池,有一定的资源消耗,好处是可以应对突发流量(流量洪峰来临时,处理不完可将数据存储到线程池队里慢慢处理)
- 信号量隔离
- 使用一个原子计数器(或信号量)记录当前有多少个线程在运行,请求来先判断计数器的数值,若超过设置的最大线程个数则丢弃该类型的新请求,若不超过则执行计数操作请求来计数器+1,请求返回计数器-1。这种方式是严格的控制线程且立即返回模式,无法应对突发流量(流量洪峰来临时,处理的线程超过数量,其他的请求会直接返回,不继续去请求依赖的服务)
线程池 | 线程池 | 信号量 |
---|---|---|
线程 | 与调用线程非相同线程 | 与调用线程相同(jetty线程) |
开销 | 排队、调度、上下文开销等 | 无线程切换,开销低 |
异步 | 支持 | 不支持 |
并发支持 | 支持(最大线程池大小) | 支持(最大信号量上限) |
服务熔断
:::info
熔断机制是对服务链路的保护机制,如果链路上的某个服务不可访问,调用超时,发生异常等,服务会触发降级返回托底数据,然后熔断服务的调用,失败率达到某个阀值服务标记为短路状态,当检查到该节点能正常使用时服务会快速恢复
:::
降级机制
:::info
超时降级、资源不足时(线程或信号量)降级,降级后可以配合降级接口返回托底数据
简单理解就是服务降级就是当服务因为网络故障,服务器故障,读取超时等原因造成服务不可达的情况下返回一些预先准备好的数据给客户端
托底数据可以返回一些友好信息给用户
:::
缓存
:::info
提供了请求缓存、请求合并实现,在高并发的场景之下,Hystrix请求缓存可以方便地开启和使用请求缓存来优化系统,达到减轻高并发时请求线程的消耗、降低请求响应时间的效果
:::
Hystrix的执行流程
正常情况下,断路器处于关闭状态,如果调用持续出错或者超时达到设定阈值,电路被打开进入熔断状态,这时请求这个服务会触发快速失败,立马返回兜底数据不要让线程死等,后续一段时间内的所有调用都会被拒绝,一段时间以后/withCircuitBreakerSleepWindowInMilliseconds=5S,保护器会尝试进入半熔断状态,允许少量请求进来尝试,如果调用仍然失败,则回到熔断状态,如果调用成功,则回到电路闭合状态
Hystrix简单使用
导入依赖
<!--springweb依赖-->
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<!-- 表示注册到eureka客户端-->
<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>
服务接口
package com.noting.controller;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import com.noting.domain.User;
import lombok.extern.slf4j.Slf4j;
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.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.client.RestTemplate;
@Slf4j
@RestController
@RequestMapping("/order")
public class OrderController {
@Autowired
// RestTemplate 用于发送 HTTP 请求的类,通过调用其 getForObject() 方法来发起 GET 请求。
private RestTemplate restTemplate;
/*@GetMapping("/{id}")
public User getUser(@PathVariable("id") Long id){
// 这里由于使用user使用了集群,并且restTemplate设置了负载均衡,这里调用的地址写为eureka的客户端的服务名
User user = restTemplate.getForObject("http://user-server/user/" + id, User.class);
return user;
}*/
/**
* 当你给你的resttemplate设置了负载均衡之后,你的调用地址就应该改为服务名称,
* 底层会通过服务名称拿到此服务名下面的所有地址默认轮询进行调用
* @param id
* @return
*/
@GetMapping("/{id}")
// 此注解标识的方法Hystrix会去监控是否需要熔断
@HystrixCommand(fallbackMethod = "getUserFallbackMethod")
public User getUser(@PathVariable("id") Long id){
/*
* @FeignClient("stores")
* public interface StoreClient {
* @RequestMapping(method = RequestMethod.GET, value = "/stores")
* List<Store> getStores();
*
* @RequestMapping(method = RequestMethod.GET, value = "/stores")
* Page<Store> getStores(Pageable pageable);
*
* @RequestMapping(method = RequestMethod.POST, value = "/stores/{storeId}", consumes = "application/json")
* Store update(@PathVariable("storeId") Long storeId, Store store);
* }
*/
int i = 1/0;
log.info("进入了getUser方法....");
User user = restTemplate.getForObject("http://user-server/user/" + id, User.class);
log.info("我是User对象:" + user.toString());
return user;
}
public User getUserFallbackMethod(@PathVariable("id") Long id){
log.info("进入了getUserFallbackMethod方法....");
return new User(-1L, "触发熔断了!", "遭求了叁,顶不住了");
}
}
启动类:加注解@EnableCircuitBreaker
package com.noting;
import com.netflix.loadbalancer.RandomRule;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.cloud.netflix.eureka.EnableEurekaClient;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
// 这个注解表示此项目是一个eureka-client服务,但是其实只要导入了client依赖就表示一个client,所以这个注解可以不加
@EnableEurekaClient
// 这个注解表示开启熔断器
@EnableCircuitBreaker
public class OrderServerApp {
public static void main(String[] args) {
SpringApplication.run(OrderServerApp.class, args);
}
// 此注解的意思是将方法中响应的对象交给spring容器管理,此注解必须在@Configuration注解的类中使用
// 方法名称就是容器中Bean的名称
@Bean
// 复载均衡的的注解,让RestTemplate具有负载均衡的能力
@LoadBalanced
public RestTemplate restTemplate(){
return new RestTemplate();
}
// 配置负载均衡为随机算法
@Bean
public RandomRule randomRule(){
return new RandomRule();
}
}