ZuulException REJECTED_SEMAPHORE_EXECUTION 是一个最近在性能测试中常常遇到的异常。查询资料发现是由于zuul默认每一个路由直接用信号量作隔离,而且默认值是100,也就是当一个路由请求的信号量高于100那么就拒绝服务了,返回500。spring
既然默认值过小,那么就在gateway的配置提升各个路由的信号量再实验。docker
routes: linkflow: path: /api1/** serviceId: lf stripPrefix: false semaphore: maxSemaphores: 2000 oauth: path: /api2/** serviceId: lf stripPrefix: false semaphore: maxSemaphores: 1000
两个路由的信号量分开提升到2000和1000。咱们再用gatling测试一下。数据库
setUp(scn.inject(rampUsers(200) over (3 seconds)).protocols(httpConf))
这是咱们的模型,3s内启动200个用户,顺序访问5个API。因此会有1000个request。机器配置只有2核16G,而且是docker化的数据库。因此总体性能不高。api
看结果仍然有57个KO,可是比以前1000个Request有900个KO的比例好不少了。性能
Edgware
版本的spring cloud提供了另外一种基于线程池的隔离机制。实现起来也很是简单,测试
zuul: ribbon-isolation-strategy: THREAD thread-pool: use-separate-thread-pools: true thread-pool-key-prefix: zuulgw hystrix: threadpool: default: coreSize: 50 maximumSize: 10000 allowMaximumSizeToDivergeFromCoreSize: true maxQueueSize: -1 execution: isolation: thread: timeoutInMilliseconds: 60000
use-separate-thread-pools
的意思是每一个路由都有本身的线程池,而不是共享一个。thread-pool-key-prefix
会指定一个线程池前缀方便调试。hystrix
的部分主要设置线程池的大小,这里设置了10000,其实并非越大越好。线程池越大削峰填谷的效果越显著,也就是时间换空间。系统的总体负载会上升,致使响应时间愈来愈长,那么当响应时间超过某个限度,其实系统也算是不可用了。后面能够看到数据。spa
此次没有500的状况了,1000个Request都正常返回了。线程
从几张图对比下两种隔离的效果,上图是信号量隔离,下图是线程隔离。3d
直观上能发现使用线程隔离的分布更好看一些,600ms内的响应会更多一些。调试
两张图展现的是同一时刻的Request和Response的数量。
先看信号量隔离的场景,Response per second是逐步提高的,可是达到一个量级后,gateway开始拒绝服务。猜想是超过了信号量的限制或是超时?
线程隔离的这张就比较有意思了,能够看到Request per second上升的速度要比上面的快,说明系统是试图接收更多的请求而后分发给线程池。再看在某个时间点Response per second反而开始降低,由于线程不断的建立消耗了大量的系统资源,响应变慢。以后由于请求少了,负载下降,Response又开始抬升。因此线程池也并不是越大越好,须要不断调试寻找一个平衡点。
线程池提供了比信号量更好的隔离机制,而且从实际测试发现高吞吐场景下能够完成更多的请求。可是信号量隔离的开销更小,对于自己就是10ms之内的系统,显然信号量更合适。