zoukankan      html  css  js  c++  java
  • 隔离技术线程池(ThreadPool)和信号量(semaphore)

    一、首先要明白Semaphore和线程池各自是干什么?

      信号量Semaphore是一个并发工具类,用来控制可同时并发的线程数,其内部维护了一组虚拟许可,通过构造器指定许可的数量,每次线程执行操作时先通过acquire方法获得许可,执行完毕再通过release方法释放许可。如果无可用许可,那么acquire方法将一直阻塞,直到其它线程释放许可。

      线程池用来控制实际工作的线程数量,通过线程复用的方式来减小内存开销。线程池可同时工作的线程数量是一定的,超过该数量的线程需进入线程队列等待,直到有可用的工作线程来执行任务。

      使用Seamphore,你创建了多少线程,实际就会有多少线程进行执行,只是可同时执行的线程数量会受到限制。但使用线程池,你创建的线程只是作为任务提交给线程池执行,实际工作的线程由线程池创建,并且实际工作的线程数量由线程池自己管理。

      简单来说,线程池实际工作的线程是work线程,不是你自己创建的,是由线程池创建的,并由线程池自动控制实际并发的work线程数量。而Seamphore相当于一个信号灯,作用是对线程做限流,Seamphore可以对你自己创建的的线程做限流(也可以对线程池的work线程做限流),Seamphore的限流必须通过手动acquire和release来实现。

      区别就是两点:

    1、实际工作的线程是谁创建的?

    使用线程池,实际工作线程由线程池创建;使用Seamphore,实际工作的线程由你自己创建。

    2、限流是否自动实现?

    线程池自动,Seamphore手动。

    二 Hystrix中的实现

    先给个总结对比:

    隔离方式 是否支持超时 是否支持熔断 隔离原理 是否是异步调用 资源消耗
    线程池隔离 支持,可直接返回 支持,当线程池到达maxSize后,再请求会触发fallback接口进行熔断 每个服务单独用线程池 可以是异步,也可以是同步。看调用的方法 大,大量线程的上下文切换,容易造成机器负载高
    信号量隔离 不支持,如果阻塞,只能通过调用协议(如:socket超时才能返回) 支持,当信号量达到maxConcurrentRequests后。再请求会触发fallback 通过信号量的计数器 同步调用,不支持异步

    小,只是个计数器

      信号量模式

      在该模式下,接收请求和执行下游依赖在同一个线程内完成,不存在线程上下文切换所带来的性能开销,所以大部分场景应该选择信号量模式,但是在下面这种情况下,信号量模式并非是一个好的选择。

      比如一个接口中依赖了3个下游:serviceA、serviceB、serviceC,且这3个服务返回的数据互相不依赖,这种情况下如果针对A、B、C的熔断降级使用信号量模式,那么接口耗时就等于请求A、B、C服务耗时的总和,无疑这不是好的方案。

      另外,为了限制对下游依赖的并发调用量,可以配置Hystrix的execution.isolation.semaphore.maxConcurrentRequests,当并发请求数达到阈值时,请求线程可以快速失败,执行降级

      实现也很简单,一个简单的计数器,当请求进入熔断器时,执行tryAcquire(),计数器加1,结果大于阈值的话,就返回false,发生信号量拒绝事件,执行降级逻辑。当请求离开熔断器时,执行release(),计数器减1。

      线程池模式

      在该模式下,用户请求会被提交到各自的线程池中执行,把执行每个下游服务的线程分离,从而达到资源隔离的作用。当线程池来不及处理并且请求队列塞满时,新进来的请求将快速失败,可以避免依赖问题扩散。

      在信号量模式提到的问题,对所依赖的多个下游服务,通过线程池的异步执行,可以有效的提高接口性能。

    优势

    • 减少所依赖服务发生故障时的影响面,比如ServiceA服务发生异常,导致请求大量超时,对应的线程池被打满,这时并不影响ServiceB、ServiceC的调用。
    • 如果接口性能有变动,可以方便的动态调整线程池的参数或者是超时时间,前提是Hystrix参数实现了动态调整。

    缺点

    • 请求在线程池中执行,肯定会带来任务调度、排队和上下文切换带来的开销。
    • 因为涉及到跨线程,那么就存在ThreadLocal数据的传递问题,比如在主线程初始化的ThreadLocal变量,在线程池线程中无法获取

    三 Zuul中的实现

      Zuul默认是使用信号量隔离,并且信号量的大小是100,请求的并发线程超过100就会报错

      可以调大该信号量的最大值来提高性能,配置如下:

    zuul:
      semaphore:
        max-semaphores: 5000

      也可以改为使用线程隔离,调大hystrix线程池线程大小,该线程池默认10个线程,配置如下:

    zuul:
      ribbonIsolationStrategy: THREAD
    
    hystrix:
      threadpool:
        default:
          coreSize: 100
          maximumSize: 400
          allowMaximumSizeToDivergeFromCoreSize: true
          maxQueueSize: -1
    maximumSize:最大线程数量

    allowMaximumSizeToDivergeFromCoreSize:是否让maximumSize生效,false的话则只有coreSize会生效
    maxQueueSize:线程池的队列大小,-1代表使用SynchronousQueue队列
  • 相关阅读:
    Delphi 与 C/C++ 数据类型对照表
    JAVA中堆和栈的区别
    关于Column '*' not found 解决方案 Hibernate使用SQL查询返回实体类型,即返回某个类,或实体类
    Oracle笔记
    oracle时间运算
    struts2中iterator标签的相关使用
    url传中文,转码
    表格的css,细线表格
    使用struts 2 获取服务器数据 ongl表达式 标签
    struts 2 Commons FileUpload文件上传
  • 原文地址:https://www.cnblogs.com/chihirotan/p/11368520.html
Copyright © 2011-2022 走看看