1.依赖隔离概述

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

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

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

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

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

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

2.线程池隔离

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

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

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 更偏向于使用线程池来隔离依赖服务,因为其带来的额外开销可以接受,并且能支持包括超时在内的所有功能。

Hystrix入门与分析(二):依赖隔离之线程池隔离的更多相关文章

  1. hystrix线程池隔离的原理与验证

    引子 幸福很简单: 今天项目半年规划被通过,终于可以早点下班.先坐公交,全程开着灯,买了了几天的书竟然有时间看了.半小时后,公交到站,换乘大巴车.车还等着上人的功夫,有昏暗的灯光,可以继续看会儿书.过 ...

  2. SQLite入门与分析(二)---设计与概念(续)

    SQLite入门与分析(二)---设计与概念(续)   写在前面:本节讨论事务,事务是DBMS最核心的技术之一.在计算机科学史上,有三位科学家因在数据库领域的成就而获ACM图灵奖,而其中之一Jim G ...

  3. 基于hystrix的线程池隔离

    hystrix进行资源隔离,其实是提供了一个抽象,叫做command,就是说,你如果要把对某一个依赖服务的所有调用请求,全部隔离在同一份资源池内 对这个依赖服务的所有调用请求,全部走这个资源池内的资源 ...

  4. 隔离技术线程池(ThreadPool)和信号量(semaphore)

    一.首先要明白Semaphore和线程池各自是干什么? 信号量Semaphore是一个并发工具类,用来控制可同时并发的线程数,其内部维护了一组虚拟许可,通过构造器指定许可的数量,每次线程执行操作时先通 ...

  5. Java提高班(二)深入理解线程池ThreadPool

    本文你将获得以下信息: 线程池源码解读 线程池执行流程分析 带返回值的线程池实现 延迟线程池实现 为了方便读者理解,本文会由浅入深,先从线程池的使用开始再延伸到源码解读和源码分析等高级内容,读者可根据 ...

  6. Solr4.8.0源码分析(3)之index的线程池管理

    Solr4.8.0源码分析(3)之index的线程池管理 Solr建索引时候是有最大的线程数限制的,它由solrconfig.xml的<maxIndexingThreads>8</m ...

  7. Hystrix入门与分析(一):初识Hystrix

    在以前的文章中,我们介绍过使用Gauva实现限流的功能,现在我们来了解一下如何在服务框架中实现熔断和降级的方法. 简介Hystrix 大型系统架构的演进基本上都是这样一个方向:从单体应用到分布式架构. ...

  8. Java并发(二十一):线程池实现原理

    一.总览 线程池类ThreadPoolExecutor的相关类需要先了解: (图片来自:https://javadoop.com/post/java-thread-pool#%E6%80%BB%E8% ...

  9. spring boot:使用多个线程池实现实现任务的线程池隔离(spring boot 2.3.2)

    一,为什么要使用多个线程池? 使用多个线程池,把相同的任务放到同一个线程池中,可以起到隔离的作用,避免有线程出错时影响到其他线程池,例如只有一个线程池时,有两种任务,下单,处理图片,如果线程池被处理图 ...

随机推荐

  1. asp.net mvc ViewData 和 ViewBag区别,TempData

    ViewData 和 ViewBag都是页面级别的生命周期,TempData--Passing data between the current and next HTTP requests Temp ...

  2. flask之基于DBUtils实现数据库连接池、本地线程、上下文

    本篇导航: 数据库连接池 本地线程 上下文管理 面向对象部分知识点解析 1.子类继承父类__init__的三种方式 class Dog(Animal): #子类 派生类 def __init__(se ...

  3. 阿里云CentOS 7无外网IP的ECS访问外网(配置网关服务器)

    说明: 1.必须要有一台机器具有外网IP的ECS. 2.如果不想配置具有外网IP的ECS时,可以购买NAT网关,但需要钱,贵.下面会说明NAT网关的配置. 3.最后吐槽一下阿里云VPC网关导致不能按照 ...

  4. set non-stop on

    # Enable the async interface. set target-async 1 # If using the CLI, pagination breaks non-stop. set ...

  5. C# MemoryCache GCHandle

    MemoryCache在项目中用了很久,感觉比较简单,以前也看过里面的源代码,主要借用MemoryCacheStore来完成数据的存储,里面是线程安全的,MemoryCacheStore借用Hasht ...

  6. [Python设计模式] 第24章 加薪审批——职责链模式

    github地址:https://github.com/cheesezh/python_design_patterns 题目 用程序模拟以下情景 员工向经理发起加薪申请,经理无权决定,需要向总监汇报, ...

  7. 每天一个linux命令(11):nl命令

    1.命令简介 nl (Number of Lines) 将指定的文件添加行号标注后写到标准输出.如果不指定文件或指定文件为"-" ,程序将从标准输入读取数据. 2.用法 nl [选 ...

  8. RobotFrameWork系列免费课程-开课了~

    1. 背景介绍 有一段时间没有发表过文章了,一方面除了自己确实变得懒惰外,另一方面也确实有其它事情,无法抽出闲余时间来坚持写下去. 之前在博客园中,发表了关于<公开课一:Robot FrameW ...

  9. 【理论面试篇】收集整理来自网络上的一些常见的 经典前端、H5面试题 Web前端开发面试题

    ##2017.10.30收集 面试技巧 5.1 面试形式 1)        一般而言,小公司做笔试题:大公司面谈项目经验:做地图的一定考算法 2)        面试官喜欢什么样的人 ü  技术好. ...

  10. 利用cwRsync客户端将Windows下文件同步到Linux

    这里不描述Linux服务端安装配置rsync服务的过程,有需要可以在网络上查找相关教程. 1.安装cwRsync客户端下载地址:http://itefix.no/cwrsync/下载文件cwRsync ...