您当前的位置:首页 > 电脑百科 > 程序开发 > 编程百科

Hystrix依赖隔离之线程池隔离

时间:2020-06-23 11:18:46  来源:  作者:

为什么要做线程隔离

比如我们现在有3个业务调用分别是查询订单、查询商品、查询用户,且这三个业务请求都是依赖第三方服务-订单服务、商品服务、用户服务。三个服务均是通过RPC调用。当查询订单服务,假如线程阻塞了,这个时候后续有大量的查询订单请求过来,那么容器中的线程数量则会持续增加直致CPU资源耗尽到100%,整个服务对外不可用,集群环境下就是雪崩。

Hystrix是如何通过线程池实现线程隔离的

Hystrix通过命令模式,将每个类型的业务请求封装成对应的命令请求,比如查询订单->订单Command,查询商品->商品Command,查询用户->用户Command。每个类型的Command对应一个线程池。创建好的线程池是被放入到ConcurrentHashMap中

1.依赖隔离概述

依赖隔离是Hystrix的核心目的。依赖隔离其实就是资源隔离,把对依赖使用的资源隔离起来,统一控制和调度。那为什么需要把资源隔离起来呢?主要有以下几点:

1.合理分配资源,把给资源分配的控制权交给用户,某一个依赖的故障不会影响到其他的依赖调用,访问资源也不受影响。

2.可以方便的指定调用策略,比如超时异常,熔断处理。

3.对依赖限制资源也是对下游依赖起到一个保护作用,避免大量的并发请求在依赖服务有问题的时候造成依赖服务瘫痪或者更糟的雪崩效应。

4.对依赖调用进行封装有利于对调用的监控和分析,类似于hystrix-dashboard的使用。

Hystrix提供了两种依赖隔离方式:线程池隔离 和 信号量隔离。如下图,线程池隔离,Hystrix可以为每一个依赖建立一个线程池,使之和其他依赖的使用资源隔离,同时限制他们的并发访问和阻塞扩张。每个依赖可以根据权重分配资源(这里主要是线程),每一部分的依赖出现了问题,也不会影响其他依赖的使用资源。

Hystrix依赖隔离之线程池隔离

 

2.线程池隔离

如果简单的使用异步线程来实现依赖调用会有如下问题:1、线程的创建和销毁;2、线程上下文空间的切换,用户态和内核态的切换带来的性能损耗。

使用线程池的方式可以解决第一种问题,但是第二个问题计算开销是不能避免的。Netflix在使用过程中详细评估了使用异步线程和同步线程带来的性能差异,结果表明在99%的情况下,异步线程带来的几毫秒延迟的完全可以接受的。

Hystrix依赖隔离之线程池隔离

 

3.线程池隔离的优缺点

优点:

  • 一个依赖可以给予一个线程池,这个依赖的异常不会影响其他的依赖。
  • 使用线程可以完全隔离第三方代码,请求线程可以快速返回。
  • 当一个失败的依赖再次变成可用时,线程池将清理,并立即恢复可用,而不是一个长时间的恢复。
  • 可以完全模拟异步调用,方便异步编程。
  • 使用线程池,可以有效的进行实时监控、统计和封装。

缺点:

  • 使用线程池的缺点主要是增加了计算的开销。每一个依赖调用都会涉及到队列,调度,上下文切换,而这些操作都有可能在不同的线程中执行。

4.Command Name&Command Group

Hystrix使用Command模式对依赖调用进行封装。当我们写一个调用继承HystrixCommand的时候,可以指定一个名称Command Name。如果不指定Hystrix将会使用getClass().getSimpleName()来默认获取。如果要指定,可以使用如下代码,使用HystrixCommandKey.Factory帮助类在构造函数中指定。

public HelloWorldCommand(String name)
    {
        //定义命令组 和 方法调用超时时间
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorldCommand")));
        this.name = name;
    }

而Command Group可以把一组Command归为一组。如上例代码,可以使用
HystrixCommandGroupKey.Factory.asKey来指定Command Group。一般情况下,逻辑上是同一类型的会放在同一个Command Group中。比如,获取用户相关信息的依赖可以放在一起。

虽然Hystrix可以为每个依赖建立一个线程池,但是如果依赖成千上万,建立那么多线程池肯定是不可能的。所以默认情况下,Hystrix会为每一个Command Group建立一个线程池。

5.Command Thread Pool

Hystrix可以指定创建或关联上一个线程池,每一个线程池都有一个Key。这个线程池就是线程隔离的关键,所有的监控、缓存、调用等等都来自于这个线程池。可以通过如下代码指定线程池:

public HelloWorldCommand(String name)
    {
        //定义命令组 和 方法调用超时时间
        super(Setter.withGroupKey(HystrixCommandGroupKey.Factory.asKey("HelloWorldGroup"))
                .andCommandKey(HystrixCommandKey.Factory.asKey("HelloWorldCommand"))
                .andThreadPoolKey(HystrixThreadPoolKey.Factory.asKey("HelloWorldPool")));
        this.name = name;
    }

上文说到,默认情况下,每一个Command Group会自动创建一个线程池。那什么时候我们需要单独指定线程池呢?因为线程池主要的目的是隔离,所以当有一些依赖在一个Command Group中,但是又有隔离的必要的时候,比如一个依赖的超时会用满所有的线程池线程,而不应该影响其他的依赖。

6.基本实现原理

Command模式:Hystrix中大量使用rxJAVA来实现Command模式。所有自定义的Command,不管继承于HystrixObservableCommand还是HystrixCommand,最终都继承于AbstractCommand。Thread Pool,Command Group,Command Key都在AbstractCommand这里实现。

线程池的创建和管理:Hystrix的线程池在
HystrixConcurrencyStrategy初始化,线程池是由ThreadPoolExecutor实现的。每个线程池默认初始化10个线程。Hystrix有个静态类Factory,创建的线程池会被存储在Factory中的ConcurrentHashMap中。ConcurrentHashMap的Key则是上文说到的CommandGroupKey或者指定的ThreadPoolKey。每次命令执行的时候,都会根据ThreadPoolKey去找到对应的线程池。线程池拥有一个继承于rxjava中Scheduler的HystrixContextScheduler,用于在执行命令的时候,把命令在这个线程池上调度执行。

命令的执行:以execute()方法为例,Hystrix通过toObservable()来构造命令,构造过程中,定义了整个命令执行过程中的stage(未开始、执行中、完成执行、执行异常等等)的回调和处理方法。在
executeCommandWithSpecifiedIsolation()方法中,使用exjava的subscribeOn方法,传入上文提到的HystrixContextScheduler对象,通过HystrixContextScheduler的ThreadPoolScheduler把命令submit到ThreadPoolExecutor中去执行。

7.最佳实践

对于那些本来延迟就比较小的请求(例如访问本地缓存成功率很高的请求)来说,线程池带来的开销是非常高的,这时,你可以考虑采用其他方法,例如非阻塞信号量(不支持超时),来实现依赖服务的隔离,使用信号量的开销很小。但绝大多数情况下,Netflix 更偏向于使用线程池来隔离依赖服务,因为其带来的额外开销可以接受,并且能支持包括超时在内的所有功能。



Tags:线程池隔离   点击:()  评论:()
声明:本站部分内容及图片来自互联网,转载是出于传递更多信息之目的,内容观点仅代表作者本人,如有任何标注错误或版权侵犯请与我们联系(Email:2595517585@qq.com),我们将及时更正、删除,谢谢。
▌相关推荐
为什么要做线程隔离比如我们现在有3个业务调用分别是查询订单、查询商品、查询用户,且这三个业务请求都是依赖第三方服务-订单服务、商品服务、用户服务。三个服务均是通过RP...【详细内容】
2020-06-23  Tags: 线程池隔离  点击:(59)  评论:(0)  加入收藏
▌简易百科推荐
本文分为三个等级自顶向下地分析了glibc中内存分配与回收的过程。本文不过度关注细节,因此只是分别从arena层次、bin层次、chunk层次进行图解,而不涉及有关指针的具体操作。前...【详细内容】
2021-12-28  linux技术栈    Tags:glibc   点击:(3)  评论:(0)  加入收藏
摘 要 (OF作品展示)OF之前介绍了用python实现数据可视化、数据分析及一些小项目,但基本都是后端的知识。想要做一个好看的可视化大屏,我们还要学一些前端的知识(vue),网上有很多比...【详细内容】
2021-12-27  项目与数据管理    Tags:Vue   点击:(2)  评论:(0)  加入收藏
程序是如何被执行的  程序是如何被执行的?许多开发者可能也没法回答这个问题,大多数人更注重的是如何编写程序,却不会太注意编写好的程序是如何被运行,这并不是一个好...【详细内容】
2021-12-23  IT学习日记    Tags:程序   点击:(9)  评论:(0)  加入收藏
阅读收获✔️1. 了解单点登录实现原理✔️2. 掌握快速使用xxl-sso接入单点登录功能一、早期的多系统登录解决方案 单系统登录解决方案的核心是cookie,cookie携带会话id在浏览器...【详细内容】
2021-12-23  程序yuan    Tags:单点登录(   点击:(8)  评论:(0)  加入收藏
下载Eclipse RCP IDE如果你电脑上还没有安装Eclipse,那么请到这里下载对应版本的软件进行安装。具体的安装步骤就不在这赘述了。创建第一个标准Eclipse RCP应用(总共分为六步)1...【详细内容】
2021-12-22  阿福ChrisYuan    Tags:RCP应用   点击:(7)  评论:(0)  加入收藏
今天想简单聊一聊 Token 的 Value Capture,就是币的价值问题。首先说明啊,这个话题包含的内容非常之光,Token 的经济学设计也可以包含诸多问题,所以几乎不可能把这个问题说的清...【详细内容】
2021-12-21  唐少华TSH    Tags:Token   点击:(10)  评论:(0)  加入收藏
实现效果:假如有10条数据,分组展示,默认在当前页面展示4个,点击换一批,从第5个开始继续展示,到最后一组,再重新返回到第一组 data() { return { qList: [], //处理后...【详细内容】
2021-12-17  Mason程    Tags:VUE   点击:(14)  评论:(0)  加入收藏
什么是性能调优?(what) 为什么需要性能调优?(why) 什么时候需要性能调优?(when) 什么地方需要性能调优?(where) 什么时候来进行性能调优?(who) 怎么样进行性能调优?(How) 硬件配...【详细内容】
2021-12-16  软件测试小p    Tags:性能调优   点击:(20)  评论:(0)  加入收藏
Tasker 是一款适用于 Android 设备的高级自动化应用,它可以通过脚本让重复性的操作自动运行,提高效率。 不知道从哪里听说的抖音 app 会导致 OLED 屏幕烧屏。于是就现学现卖,自...【详细内容】
2021-12-15  ITBang    Tags:抖音防烧屏   点击:(25)  评论:(0)  加入收藏
11 月 23 日,Rust Moderation Team(审核团队)在 GitHub 上发布了辞职公告,即刻生效。根据公告,审核团队集体辞职是为了抗议 Rust 核心团队(Core team)在执行社区行为准则和标准上...【详细内容】
2021-12-15  InfoQ    Tags:Rust   点击:(25)  评论:(0)  加入收藏
相关文章
    无相关信息
最新更新
栏目热门
栏目头条