Hystrix简单使用
一:简介:
spring cloud 用的是 hystrix,是一个容错组件。
Hystrix实现了 超时机制和断路器模式。
Hystrix是Netflix开源的一个类库,用于隔离远程系统、服务或者第三方库,防止级联失败,从而提升系统的可用性与容错性。主要有以下几点功能:
- 为系统提供保护机制。在依赖的服务出现高延迟或失败时,为系统提供保护和控制。
- 防止雪崩。
- 包裹请求:使用HystrixCommand(或HystrixObservableCommand)包裹对依赖的调用逻辑,每个命令在独立线程中运行。
- 跳闸机制:当某服务失败率达到一定的阈值时,Hystrix可以自动跳闸,停止请求该服务一段时间。
- 资源隔离:Hystrix为每个请求都的依赖都维护了一个小型线程池,如果该线程池已满,发往该依赖的请求就被立即拒绝,而不是排队等候,从而加速失败判定。防止级联失败。
- 快速失败:Fail Fast。同时能快速恢复。侧重点是:(不去真正的请求服务,发生异常再返回),而是直接失败。
- 监控:Hystrix可以实时监控运行指标和配置的变化,提供近实时的监控、报警、运维控制。
- 回退机制:fallback,当请求失败、超时、被拒绝,或当断路器被打开时,执行回退逻辑。回退逻辑我们自定义,提供优雅的服务降级。
- 自我修复:断路器打开一段时间后,会自动进入“半开”状态,可以进行打开,关闭,半开状态的转换。前面有介绍。
二:hystrix独立使用脱离spring cloud
com.netflix.hystrix hystrix-core 1.5.18
测试代码,当出现错误时解决方案,
hystrix简单理解就是对错误以及不符合业务逻辑的项目的一种修复及补丁处理
public class CommonTest extends HystrixCommand{ private String name; public CommonTest(String name) { super(HystrixCommandGroupKey.Factory.asKey("myGroup")); this.name=name; } @Override protected String run() throws Exception { Thread.sleep(1000); return Thread.currentThread().getName(); } /* @Override protected String getFallback() { return "超时了,请等等"; }*/ public static void main(String[] args) throws InterruptedException, ExecutionException { CommonTest t= new CommonTest("zhangsan"); Future future=t.queue(); while (!future.isDone()){ Thread.sleep(700); System.out.println(111); } System.out.println(future.get()); //System.out.println(aa1); } }
三:hystrix结合feign实现服务降级
简单介绍服务降级:就是当访问数据时由于数据量过大或者某些原因,不能提供给前端完整的数据,我们可以提供部分数据给到前端,这样既保证了前端的部分显示,又保证了后端服务继续提供
application.properties中添加如下配置,即feign中开启hystrix
feign.hystrix.enabled=true
在api中添加forback配置
@FeignClient(name = "user-provider",fallback = UserProvider.class) public interface UserApi extends UserHandler { @RequestMapping("/getUser") User getUser(); @RequestMapping("/testMap") Map testMap(); @RequestMapping("/putUser") String putUser(User var1); }
这样如果服务不通或者出现其他故障,将进入UserProvider类当中,所以UserProvider应该实现UserApi,因为这样就可以对每个方法进行处理了
@Component public class UserProvider implements UserApi { /**对服务进行降级*/ @Override public User getUser() { User user = new User(); user.setName("服务不正常,返回默认数据"); return user; } @Override public Map testMap() { return null; } @Override public String putUser(User var1) { return null; } }
测试访问接口:正常访问:
当我在服务方设置睡眠之后:
再次访问:
测试完成,再次测试,如果我在服务端设置两个服务器,其中一个睡眠,一个不睡,然后用ribbon做负载均衡,测试如下:
负载均衡
测试结果:当访问到线程等待的服务器时直接返回降级之后的数据,并没有再次请求别的服务器,如果没有配置hystrix时,会访问其他的机器(感觉好像应该先访问别的机器,等所有的机器都访问不了时才触发降级)
写法二,使用fallbackFactory实现
@FeignClient(name = "user-provider",fallbackFactory = UserProvider.class) public interface UserApi extends UserHandler { @RequestMapping("/getUser") User getUser(); @RequestMapping("/testMap") Map testMap(); @RequestMapping("/putUser") String putUser(User var1); }
编写UserProvider
@Component public class UserProvider implements FallbackFactory{ @Override public UserApi create(Throwable throwable) { return new UserApi() { @Override public User getUser() { Throwable cause = throwable.getCause(); String message = cause.getMessage(); User user=new User(); user.setName(message); return user; } @Override public Map testMap() { return null; } @Override public String putUser(User var1) { return null; } }; } }
这样写的好处是对不同的错误可以进行不同的处理
四:hystrix结合RestTmplate使用
引入pom
org.springframework.boot spring-boot-starter-parent 2.2.6.RELEASE 1.8 Hoxton.SR3 paic.consumer user_consumer 1.0-SNAPSHOT paic.common user_api 1.0-SNAPSHOT org.springframework.boot spring-boot-starter-web org.springframework.cloud spring-cloud-starter-netflix-eureka-client org.springframework.cloud spring-cloud-starter-openfeign org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard org.springframework.cloud spring-cloud-starter-netflix-hystrix org.springframework.cloud spring-cloud-dependencies ${spring-cloud.version} pom import org.springframework.boot spring-boot-maven-plugin
在启动类中增加注解,如果不加,则hystrix不再生效:
@EnableCircuitBreaker
使用方式:为了代码的规范性,hystrix最好写在service模块当中
@LoadBalanced @Bean public RestTemplate getRestTemplate(){ return new RestTemplate(); } @Autowired RestTemplate restTemplate; @GetMapping("/getUser") @HystrixCommand(fallbackMethod = "getUserBackMethod") public User getUser() { ResponseEntityuserResponseEntity= restTemplate.getForEntity("http://USER-PROVIDER/getUser",User.class); User body = userResponseEntity.getBody(); return body; } public User getUserBackMethod(){ User user =new User(); user.setName("fullBack"); return user; }
5:资源隔离
Hystrix 的资源隔离策略有两种,分别为线程池和信号量。那我们为什么需要资源隔离呢?
在一个分布式系统中,服务之间都是相互调用的,例如,我们容器(Tomcat)配置的线程个数为 1000,服务 A-服务 R,其中服务 I 的并发量非常的大,需要 500 个线程来执行,此时,服务 I 又挂了,那么这 500 个线程很可能就夯死了,那么剩下的服务,总共可用的线程为 500 个,随着并发量的增大,剩余服务挂掉的风险就会越来越大,最后导致整个系统的所有服务都不可用,直到系统宕机。
以上就是服务的雪崩效应。Hystrix 就是用来做资源隔离的,比如说,当客户端向服务端发送请求时,给服务 I 分配了 10 个线程,只要超过了这个并发量就走降级服务,就算服务 I 挂了,最多也就导致服务 I 不可用,容器的 10 个线程不可用了,但是不会影响系统中的其他服务。
下面,我们就来具体说下这两种隔离策略。
信号量策略配置
用于隔离本地代码或可快速返回的远程调用可以直接使用信号量隔离,降低线程隔离的上下文切换开销。如 memcached,redis。
线程隔离会带来线程开销,有些场景(比如无网络请求场景)可能会因为用开销换隔离得不偿失,为此 hystrix 提供了信号量隔离。
主要适用于并发需求不大的依赖调用,因为如果并发需求较大,相应的信号量的数量就要设置得够大,因为 Tomcat 线程与处理线程为同一个线程,那么这个依赖调用就会占用过多的 Tomcat 线程资源,有可能会影响到其他服务的接收。
信号量策略配置方法代码如下所示。
super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.SEMAPHORE ))); this.name = name; }
之前在 run 方法中特意输出了线程名称,通过这个名称就可以确定当前是线程隔离还是信号量隔离。
线程隔离策略配置
执行依赖代码的线程与请求线程(比如 Tomcat 线程)分离,请求线程可以自由控制离开的时间,这也是我们通常说的异步编程,Hystrix 是结合 RxJava 来实现的异步编程。
通过为每个包裹了 HystrixCommand 的 API 接口设置独立的、固定大小的线程池(hystrix.threadpool.default.coreSize)来控制并发访问量,当线程饱和的时候可以拒绝服务,防止依赖问题扩散。
系统默认采用线程隔离策略,我们可以通过 andThreadPoolPropertiesDefaults 配置线程池的一些参数,代码如下所示。
public MyHystrixCommand(String name) { super(HystrixCommand.Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("MyGroup")) .andCommandPropertiesDefaults(HystrixCommandProperties.Setter() .withExecutionIsolationStrategy(HystrixCommandProperties.ExecutionIsolationStrategy.THREAD)) .andThreadPoolPropertiesDefaults(HystrixThreadPoolProperties.Setter().withCoreSize(10) .withMaxQueueSize(100).withMaximumSize(100))); this.name = name; }
线程隔离策略的优点如下:
- 一个依赖调用可以给予一个线程池,这个依赖的异常不会影响其他的依赖。
- 使用线程可以完全隔离业务代码,请求线程可以快速返回。
- 可以完全模拟异步调用,方便异步编程。
线程隔离策略的缺点:使用线程池的缺点主要是增加了计算的开销。每一个依赖调用都会涉及到队列,调度,上下文切换,而这些操作都有可能在不同的线程中执行。
线程池隔离乳下图
histrix中为每个服务维护一个线程池
信号量隔离不需要从新开销线程池,只需要维护信号量,且work线程池中的线程就可以通过信号量直接定位是否需要fullback
线程池优点:
1.自定义拒绝策略
2.和work隔离开来
3.每个线程池不影响
所以如果你的服务不会出错,或者访问业务非常快的情况下选择信号量
springcloud中配置线程池隔离(默认就是线程池隔离)
引入pom配置,让我们可以查看一些配置信息
org.springframework.cloud spring-cloud-starter-netflix-hystrix-dashboard org.springframework.boot spring-boot-starter-actuator
这样我们就可以通过健康上报来监控线程池等信息了
配置actuator信息,让其暴露更多的端口,默认只有/health和/info
management.endpoints.web.exposure.include=*
访问信息:
然后看下是否可以访问:
刚开始只有ping......当有接口访问的时候就会上报这些信息
这些信息可以通过页面
进去之后:
如果要使用信息量隔离
添加配置:
hystrix.command.default.execution.isolation.strategy=SEMAPHORE
注意的事情:
在使用feign时,不能在类上加其他的路径,比如