zoukankan      html  css  js  c++  java
  • poolboy的坑

    poolboy是Erlang中运用非常广泛的进程池库,它有很多优点,使用简单,在很多项目中都能看到它的身影。不过,它也有一些坑,使用时候需要注意。(本文对poolboy的分析基于1.5.1版本)

    worker创建不能失败

    poolboy初始化的时候,或者当前进程池的worker数量超过默认值,都会新建worker。我们看一下新建worker的代码:

    new_worker(Sup) ->  
        {ok, Pid} = supervisor:start_child(Sup, []),  
        true = link(Pid),  
        Pid.  
    

    可以看到,supervisor:start_child的时候是不能失败的,也就是说worker创建如果失败,会导致poolboy这个gen_server挂掉,导致整个进程池崩溃。

    这会有什么影响呢,我们看一下用poolboy管理eredis的例子,参考其中的一个实现eredis_pool

    创建worker失败,pool无法启动

    eredis初始化代码,如果连接失败,eredis直接退出。如果用eredis_pool的话,当redis没有起来,或者某些其它原因导致eredis初始化失败(只要一次失败),会导致eredis_pool无法正常启动。

    当然,如果redis无法正常工作,eredis_pool是不应该启动成功。但是如果进程池有100个worker,创建成功99个,第100个失败了,结果导致整个进程池退出,似乎有点太严格了。

    所以有人对eredis提了个 issue,应该就是针对这个问题的。

    创建worker失败,pool异常退出

    具体可以看 create_pool的代码。

    假如现在连接池配置有100个eredis client,当超过100个client时,poolboy会尝试启用overflow,新建eredis client。如果这时候因为某些原因,创建失败,结果也是一样,eredis_pool 异常退出。

    观察网络连接,就会发现,这时候已有的redis client全部断链。当poolboy被重新拉起来的话,又会重新尝试建链。

    根据上面分析可以看到,poolboy管理的worker有非常严格的规定,worker创建不能失败。如果失败,可能导致进程池无法正常启动,或者正常运行的进程池异常退出。

    解决方法,加代理进程

    在poolboy和进程之间加一个proxy process,proxy创建时不会去尝试建链,只做一些很简单的工作,确保进程初始化可以成功。在进行具体操作时,再去尝试建链。这样可以避免前面的问题,可以看 epgsql_pool或者 phoenix,或者我们自己fork的eredis_pool

    proxy代理进程的问题

    但是proxy有一个问题,那就是proxy进程里面的client不一定是正常的。看epgsql_pool和phoenix代码可以知道,proxy只保证自己创建的时候不会失败,至于它管理的client是不是正常的,只有在进行具体工作的时候,才可以知道。

    这个大部分情况也没有什么问题,当新建worker,如果client连接有问题时,只会影响本次的poolboy调用,但是不会导致进程池崩溃。

    当然可以在proxy进程里面加个定时器,定时去检查client的连接情况,如果失败,尝试重新建链。

    但是深入代码时,会发现还是有一个坑。看poolboy checkin代码。当checkin时候,如果这个时候进程池数量大于默认值,已经启用了overflow,那么它会尝试关闭这个worker,dismiss_worker代码如下:

    dismiss_worker(Sup, Pid) ->
        true = unlink(Pid),
        supervisor:terminate_child(Sup, Pid).
    

    这个会有什么影响呢,我们分析一下这种情况。

    poolboy默认配置100个worker,当worker超过100时,会启用overflow数量的worker。比如overflow为20,现在已经110个worker了。如果再次新建的client建链不成功,而同时110个worker已经有11个worker在checkin。这会导致10个worker被关闭,而这个不正常的worker checkin时可能没有被关闭。

    换句话说,由于下面原因,导致正常的client被关闭,而不正常的client被保留。

    1. worker启动不能失败
    2. proxy不了解它管理的client是否正常
    3. 当进程启用overflow后,poolboy checkin会关闭worker

    poolboy是一个简单,高效的进程池库,但是它对管理的worker有很严格的限制。例如管理redis client时,启动redis client不能失败,而且需要redis client自己管理链接,重连等等情况。即使采用proxy进程来管理redis client,仍然可能导致正常的redis client被关闭,而不正常的redis client存在pool中。

    关于作者

    微博@liaolinbo,云巴首席工程师。曾于Oracle工作。

  • 相关阅读:
    Android学习——体验活动的生命周期
    Android学习——活动的生存期
    《程序员修炼之道:从小工到专家》 阅读笔记01
    开课第七周周总结
    Android学习——活动状态
    Android学习——返回栈
    Android学习——Intent传递数据
    Android学习——隐式Activity的用法
    Android学习——隐式Activity
    Android学习——使用显示Intent
  • 原文地址:https://www.cnblogs.com/yunba/p/4564236.html
Copyright © 2011-2022 走看看