前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >hystrix基础

hystrix基础

作者头像
丁D
发布2022-08-12 14:57:27
2320
发布2022-08-12 14:57:27
举报
文章被收录于专栏:老铁丁D

hystrix是什么

在分布式系统中,一个系统通常会有多个依赖项目,那么不可避免的是依赖项目可能会失败,如果主项目没有跟依赖项进行隔离,那么就会有越来越多的线程hang住在调用依赖项的地方等待超时,这样会消耗大量的服务器资源,从而导致主项目被依赖项目拖死。

alt
alt

Hystrix设计原则

  1. 提供快速失败,降级功能;
  2. 资源隔离防止单个依赖项占用所有tomcat资源(线程)
  3. Hystrix两种封装,4种调用方式
  4. 线程池隔离,和信号量隔离

资源隔离

使用Hystrix进行资源隔离,Hystrix提供了一种抽象叫做command,就是说,我们将对同一个依赖项目的调用请求,全部隔离到一个资源池内,,对这个服务的调用,全部使用这个资源池里面的线程,这就是资源隔离。

假设线程池里面只有10个线程,即使这个时候请求有1000个,那也只是使用10个线程去请求,其他的会放在等待队列,等待队列满了就直接调用快速失败方法。这样就不会出现由于接口的调用延迟,将tomcat内的线程全部耗尽。

Hystrix 两种策略,线程池隔离,信号量隔离 Hystrix最基本的隔离技术是,线程池隔离(默认),在实际应用中也是90%使用这个。

指定了HystrixCommand.run()的资源隔离策略,THREAD或者SEMAPHORE,一种是基于线程池,一种是信号量

线程池机制,每个command运行在一个线程中,限流是通过线程池的大小来控制的

信号量机制,command是运行在调用线程中,但是通过信号量的容量来进行限流

线程池其实最大的好处就是对于网络访问请求,如果有超时的话,可以避免调用线程阻塞住

而使用信号量的场景,通常是针对超大并发量的场景下,每个服务实例每秒都几百的QPS,那么此时你用线程池的话,线程一般不会太多,可能撑不住那么高的并发,如果要撑住,可能要耗费大量的线程资源,那么就是用信号量,来进行限流保护

一般用信号量常见于那种基于纯内存的一些业务逻辑服务,而不涉及到任何网络访问请求

线程池机制的缺点:每一个commod都要有一个独立的线程执行,这就涉及要了 线程的排队,调度,上下文切换,这就有性能的损耗,但是对于系统的稳定性,这一点点的性能损耗完全是可以接受的,

代码语言:javascript
复制
// to use thread isolation 
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.THREAD) 
// to use semaphore isolation 
HystrixCommandProperties.Setter().withExecutionIsolationStrategy(ExecutionIsolationStrategy.SEMAPHORE) 
<dependency> 
<groupId>com.netflix.hystrix</groupId> 
<artifactId>hystrix-core</artifactId> 
<version>1.5.12</version> 
</dependency> 
//HystrixCommandGroupKey 一般一个依赖服务使用同一个线程池orderGroup 
public class CommandOrderInfo extends HystrixCommand<OrderInfo> { 
private final String orderId; 
public CommandHelloWorld(String orderId) { 
super(HystrixCommandGroupKey.Factory.asKey("orderGroup")); 
this.orderId= orderId; 
} 
@Override 
protected String run() { 
//发送http请求调用订单系统获取订单,返回json,转成orderInfo对象返回 
//调用省略。。。。 
return orderInfo 
} 
} 
//调用 同步 
new CommandOrderInfo("MO123456").execute() 
//调用 异步(很少用) 
new CommandHelloWorld("World").queue() 
对command调用queue(),仅仅将command放入线程池的一个等待队列,就立即返回,拿到一个Future对象,后面可以做一些其他的事情,然后过一段时间对future调用get()方法获取数据(先判断是否执行完成) 

command名称和command组
代码语言:javascript
复制
每个command都需要一个command key默认是(类名),每个command代表的是一个接口 
command可以归属一个command组代表一个服务,,这个服务所有的,这样就可以聚合一些监控和报警信息. 
最佳实践就是 一个command组代表一个服务,,这个服务所有的command都设置同一个command组,这就可以统计这个服务的一些监控信息和报警信息。 
每个command可以设置一个线程池,也可以多个command共享一个线程池里面的线程。。。 
线程池和command组没有关系 但是默认情况下,一个command组就会创建一个线程池,组内所有的command共享这个线程池的线程。 
你的每个command,都可以设置一个自己的名称(默认类名),同时可以设置一个自己的组 
private static final Setter cachedSetter =setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")).andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")); 
public CommandHelloWorld(String name) { 
super(cachedSetter); 
this.name = name; 
} 

command group,是一个非常重要的概念,默认情况下,因为就是通过command group来定义一个线程池的,而且还会通过command group来聚合一些监控和报警信息

同一个command group中的请求,都会进入同一个线程池中

command线程池

threadpool key代表了一个HystrixThreadPool,用来进行统一监控,统计,缓存

默认的threadpool key就是command group名称 每个command都会跟它的threadpool key对应的thread pool绑定在一起

如果不想直接用command group,也可以手动设置thread pool name

代码语言:javascript
复制
public CommandHelloWorld(String name) { 
super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("ExampleGroup")) 
.andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorld")) 
.andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool"))); 
this.name = name; 
} 

command key,代表了一类command,一般来说,代表了底层的依赖服务的一个接口

command group,代表了某一个底层的依赖服务,合理,一个依赖服务可能会暴露出来多个接口,每个接口就是一个command key

command group,在逻辑上去组织起来一堆command key的调用,统计信息,成功次数,timeout超时次数,失败次数,可以看到某一个服务整体的一些访问情况

command group,一般来说,推荐是根据一个服务去划分出一个线程池,command key默认都是属于同一个线程池的

command group,对应了一个服务,但是这个服务暴露出来的几个接口,访问量很不一样,差异非常之大

你可能就希望在这个服务command group内部,包含的对应多个接口的command key,做一些细粒度的资源隔离,也就是对同一个服务的不同接口,都使用不同的线程池

command key -> command group

command key -> 自己的threadpool key

逻辑上来说,多个command key属于一个command group,在做统计的时候,会放在一起统计

每个command key有自己的线程池,每个接口有自己的线程池,去做资源隔离和限流

但是对于thread pool资源隔离来说,可能是希望能够拆分的更加一致一些,比如在一个功能模块内,对不同的请求可以使用不同的thread pool

command group一般来说,可以是对应一个服务,多个command key对应这个服务的多个接口,多个接口的调用共享同一个线程池

如果说你的command key,要用自己的线程池,可以定义自己的threadpool key,就ok了

coreSize
代码语言:javascript
复制
//设置线程池的大小,默认是10,一般来说,用这个默认的10个线程大小就够了 
HystrixThreadPoolProperties.Setter().withCoreSize(int value) 

5、queueSizeRejectionThreshold

控制queue满后reject的threshold,因为maxQueueSize不允许热修改,因此提供这个参数可以热修改,控制队列的最大大小

HystrixCommand在提交到线程池之前,其实会先进入一个队列中,这个队列满了之后,才会reject

代码语言:javascript
复制
//默认值是5 
HystrixThreadPoolProperties.Setter().withQueueSizeRejectionThreshold(int value) 

execution.isolation.semaphore.maxConcurrentRequests

设置使用SEMAPHORE隔离策略的时候,允许访问的最大并发量,超过这个最大并发量,请求直接被reject

这个并发量的设置,跟线程池大小的设置,应该是类似的,但是基于信号量的话,性能会好很多,而且hystrix框架本身的开销会小很多

默认值是10,设置的小一些,否则因为信号量是基于调用线程去执行command的,而且不能从timeout中抽离,因此一旦设置的太大,而且有延时发生,可能瞬间导致tomcat本身的线程资源本占满

代码语言:javascript
复制
HystrixCommandProperties.Setter().withExecutionIsolationSemaphoreMaxConcurrentRequests(int value) 

官网

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2019-08-06 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • hystrix是什么
  • Hystrix设计原则
  • 资源隔离
    • command名称和command组
      • command线程池
        • coreSize
          • execution.isolation.semaphore.maxConcurrentRequests
          领券
          问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档