知识卡片

用组件并发度代替线程池:避免单个组件在内部悄悄吃光整机资源

普通读书笔记卡

内容

Storm本身就是一个分布式、多线程的框架,每个Spout和Bolt都可以单独设置并发度,也支持通过rebalance命令动态调整并发度,把负载分摊到多个Worker上——这套官方提供的并发扩展机制,本身就已经能够满足绝大多数场景下的扩展需求。但如果开发者不使用这套机制,而是在自己的组件内部另起线程池去做一些计算密集型任务(比如解析),会带来一个隐藏风险:不同组件之间的资源消耗可能变得极不均衡,这个问题在组件并行度设置得比较低时尤其明显。一个真实案例很好地说明了这个风险:某个Bolt的并行度只设置了1,但这个Bolt内部又自己启动了一个线程池,结果就是集群中分配了这个Bolt的那个Worker进程,把整台机器的资源都吃光了,直接影响到同一台机器上其他Topology任务的正常运行——本该由Storm集群统一调度分配的计算资源,被这个组件内部自行其是的线程池悄悄”偷走”了,而Storm的调度层对此毫不知情,无法据此做出正确的资源分配决策。正确的做法是:如果确实存在计算密集型任务,应该把这个组件本身的并发度设大,相应地增加Worker数量,让Storm的调度机制把这部分计算负载真正分配到多个节点上去,而不是在单个组件内部私自开辟线程池;此外还可以配合CGroup来限制每个Worker能使用的资源上限,作为额外的一道防线,防止某个Topology的某些组件把整台机器的资源全部占满。这个案例揭示了一条使用分布式计算框架的重要原则:框架提供的官方扩展机制(这里是组件并发度)之所以值得优先使用,不只是因为它用起来方便,更是因为只有通过这套机制表达出来的资源需求,才能被框架的调度层真正感知和统一管理;一旦绕开框架的扩展机制、在组件内部私自搞小动作(自建线程池),这部分资源消耗就会变成框架调度盲区,容易在不经意间破坏整个集群资源分配的公平性和可预测性。

参考来源

- 位置:《高可用架构(第1卷)》第6章《大数据与数据库》"6.2 实时计算在点评"节,"6.2.6 Storm使用经验分享"(源文件:_epub-src/OEBPS/Text/Chapter6_2_7.xhtml) - 结论依据:原文说明"如果自己在组件内部采用线程池做一些计算密集型的任务……有可能使得某些组件的资源消耗特别高……比如某个Bolt设置了1个并行度,但在Bolt中又启动了线程池,这样导致的一种后果就是,集群中分配了这个Bolt的Worker进程可能会把机器的资源都给消耗光了,影响到其他Topology在这台机器上的任务的运行",直接支撑本卡片结论。 - 原始内容:比如某个Bolt设置了1个并行度,但在Bolt中又启动了线程池,这样导致的一种后果就是,集群中分配了这个Bolt的Worker进程可能会把机器的资源都给消耗光了,影响到其他Topology在这台机器上的任务的运行。