dubbo集群中的provider角色,有两个线程池,一个是IO线程池,一个是业务线程池(默认200) 当业务线程并发比较高,或者业务处理变慢,业务线程池很容易满额,抛出“RejectedExecutionException: Thread pool is EXHAUSTED! ”异常。固然,前提是咱们每给Provider的线程池配置等待Queue(队列)。 既然provider端已经抛出异常,表示本身受不了,可是consumer无动于衷,发出的请求还在等待,直到超时。这样极其容易致使整个系统的“雪崩”,由于它违背了fail-fast原则。 咱们但愿一旦Provider因为线程池被打满而没法收到请求,Consumer应该当即感知而后快速失败来释放线程。后来发现,彻底是Dispatcher配置得不对,默认是all,咱们应该配置成message。 调度器 Dispatcher 调度策略缓存
线程池 ThreadPool fixed 固定大小线程池,启动时创建线程,不关闭,一直持有。(缺省) cached 缓存线程池,空闲一分钟自动删除,须要时重建。 limited 可伸缩线程池,但池中的线程数只会增加不会收缩。只增加不收缩的目的是为了不收缩时忽然来了大流量引发的性能问题。 eager 优先建立Worker线程池。在任务数量大于corePoolSize可是小于maximumPoolSize时,优先建立Worker来处理任务。当任务数量大于maximumPoolSize时,将任务放入阻塞队列中。阻塞队列充满时抛出RejectedExecutionException。(相比于cached:cached在任务数量超过maximumPoolSize时直接抛出异常而不是将任务放入阻塞队列) 从上面能够看到全部的请求、响应、链接、断开都通过Dispatcher之手。 每种Dispatcher,都有对应的ChannelHandler,ChannelHandler将Handler的调动造成调用链。若是配置的是all,那么接下来上场的就是AllChannelHandler;若是配置的是message,那么接下来上场的就是MessageOnlyChannelHandler,这些ChannelHandler都是WrappedChannelHandler的子类,WrappedChannelHandler默认把请求、响应、链接、断开、心跳操做都交给Handler来处理。 AllChannelHandler覆盖了WrappedChannelHandler全部的关键操做,都将其放进到ExecutorService(这里指的是业务线程池)中异步来处理,但惟一没有异步操做的就是sent方法,该方法主要用于应答,但官方文档却说使用all时应答也是放到业务线程池的,写错了?这里,关键的地方来了,一旦业务线程池满了,将抛出执行拒绝异常,将进入caught方法来处理,而该方法使用的仍然是业务线程池,因此颇有可能这时业务线程池仍是满的,因而悲剧了,直接致使下游的一个HeaderExchangeHandler没机会调用,而异常处理后的应答消息正是HeaderExchangeHandler#caught来完成的,因此最后NettyHandler#writeRequested也没有被调用,Consumer只能死等到超时,没法收到Provider的线程池打满异常。 MessageOnlyChannelHandler只覆盖了WrappedChannelHandler的received方法,意味着只有请求处理会用到业务线程池,其余的非业务操做直接在IO线程池执行,这不正是咱们想要的吗?因此使用message的Dispatcher,不会存在Provider线程池满了,Consumer却还在傻等的状况,由于默认IO线程池是无界的,必定会有线程来处理异常和应答(若是你把它设置成有界,那我也没啥好说的了)。并发