项目中使用微服务架构一两年的时间,在这期间使用到了SpringCloud与Netflex-Hystrix断路器,实际使用过程中踩了不少的坑,在此记录
Tomcat线程模型
- HTTP请求在进入服务端后,会首先进入tomcat的Accept队列等待后续处理
- tomcat内的Acceptor线程不断的从Accept队列中取出连接,并将请求转移到Worker线程来做后续业务处理,如果此时worker线程不足,则Acceptor线程将阻塞
- worker线程执行原生webfilter过滤器逻辑
- worker线程执行Spring的DispatcherServlet逻辑
可见,服务端最大并发能力由worker线程数决定,Tomcat默认的最小worker线程数为10,最大为200,而Acceptor队列最大容量在window下是8192,linux下是10000
引入Hystrix后的线程模型
springcloud-hystrix断路器的介入是在请求进入DispatcherServlet以后,有线程池模式和信号量模式两种,默认为线程池模式
- worker线程在DispatcherServlet内部会获取hystrix线程来执行后续的逻辑,如果没有足够的线程来处理,则会进入hystrix队列等待
- 在hystrix线程处理或队列等待的过程中,worker线程将被阻塞,直到hystrix完成处理
tomcat分配的worker处理线程看其名字http-nio-8080-exec-1像是NIO,实际上tomcat线程只有在HTTP请求头的读写部分为nio,其他部分均为bio,在请求进入hystrix后,worker线程无法实现复用
hystrix线程池有以下几项配置,分别是
hystrix:
threadpool:
default:
coreSize: 10 #Hystrix核心线程数,默认10
maximumSize: 20 #Hystrix最大线程数,默认10,只有在设置allowMaximumSizeToDivergeFromCoreSize=true后才能生效
allowMaximumSizeToDivergeFromCoreSize: true #是否允许线程数动态调整,默认false
keepAliveTimeMinutes: 1 #非核心线程空闲多久后自动释放(单位分钟)
maxQueueSize: 10 #请求等待队列排队请求最大容量,默认-1为无容量,即不允许排队等待而直接降级/抛出异常
queueSizeRejectionThreshold: 5 #请求等待队列中的排队请求数达到此值后,请求将被降级/抛出异常,默认5,建议同时调整maxQueueSize值,若maxQueueSize=-1,则此属性不可用
通过以上流程我们能够发现一些特点
- 之所以要分maxQueueSize和queueSizeRejectionThreshold这两个参数,而不是直接使用maxQueueSize,是因为队列一旦创建就不可以改变大小,无法实现动态调整,所以才有了第二个参数,没有其他意义
- 在引入hystrix后,服务端的最大并发能力仍然受到tomcat的worker线程数限制,即hystrix总可用线程数+hystrix队列最大容量不可能超过tomcat的worker线程数
- 原生webfilter的执行是在进入hystrix之前的worker线程,而后续任务的执行在hystrix,故若程序在webfilter中写入了ThreadLocal数据,在后续逻辑中无法读取,会造成程序BUG
- hystrix线程在工作的过程中,对应的worker线程将被阻塞,造成了线程的浪费,这个问题在当前模式下无法解决
Hystrix线程池与Tomcat线程池工作方式的不同
Tomcat线程池是web容器自身实现的,非java的默认线程池,有min-spare与max的配置,分别代表最小线程数和最大线程数,在请求进入后,线程池当前线程数如果没有达到min-spare,会创建新的线程,而如果并发请求数超过了min-spare,未达到max,则会继续动态创建线程(在空闲后会收缩),只有在并发请求达到max后,后续请求才会在队列中持续等待
默认Hystrix执行线程池为java的ThreadPoolExecutor,它不像Tomcat一样尽可能的创建线程,而是在请求并发数超过coreSize后,优先进入hystrix队列等待,只有在队列满了之后才会创建新的线程
Hystrix的信号量模式
上面说的都是hystrix默认的线程池模式的情况,而在hystrix信号量模式下,hystrix不会创建自己的线程池和队列,而是采用信号量的方式直接对tomcat线程进行隔离,故可以节省一半的线程数,并且不会出现线程池模式的webfilter的ThreadLocal问题,但是他的问题在于
- 无法使用hystrix队列,所以达到并发限制后请求将直接被降级/抛出异常
- 无法提供hystrix超时控制,一旦任务阻塞将无法实现断路器的熔断能力
以上是在使用hystrix时踩的其中一个小坑,下一篇文章将提供hystrix线程池工作方式问题的解决方案,点此查看