大量新线程(每60秒10k)导致内存问题

时间:2017-12-29 14:07:24

标签: ruby multithreading server

我有一个Ruby应用程序,每60秒为每个用户提取最近的消息。由于消息托管在远程API上,这是目前最实用的方法:

while true
  fetch_recent_messages do |result|
    # Handle result
  end

  sleep 60
end

def fetch_recent_messages()
  Thread.new do
    sleep_duration = 60 / @fetch_users.count

    for fetch_user in @fetch_users
      Thread.new do
        @api_manager.fetch_recent_messages_for_user(fetch_user)
        yield result
      end

      sleep sleep_duration
    end
  end
end
    每隔60秒就会调用
  1. fetch_recent_messages()
  2. @fetch_users循环播放,在每次迭代中休眠一段时间,以便函数大约需要60秒才能在所有用户中调用fetch_recent_messages_for_user()。这有助于分散网络和CPU负载。
  3. @fetch_users循环的迭代初始化一个新线程,在该线程上执行API调用,然后返回while循环结果。
  4. 这样做的关键是为每个API请求初始化一个新线程,以便允许它们彼此异步发生。可能存在大量用户,例如10k,因此对于所有用户的获取消息请求能够在相同的60秒内被处理是很重要的。这适用于我服务器上的CPU /内存使用情况。

    我注意到此应用的内存使用量在很长一段时间内自发地上下跳动,并且无法将其与任何特定行为相关联。这可能发生在运行15%CPU使用24小时或更长时间后 - 它会突然跳到30%,然后一段时间后可能会达到40%或再次下降。似乎没有一种模式。我最后评论了除了创建新线程以观察性能之外的一切,看起来这会导致内存问题。

    我想知道Ruby中是否存在维护大量线程的问题,以及我是否应该以不同方式处理它们,或者可能以不同方式执行此任务。

1 个答案:

答案 0 :(得分:2)

对于大多数实现线程的语言,存在一个令人惊讶的低阈值(我不知道,数百种可能,取决于许多事情),超出这个阈值会产生额外的线程问题。 (Erlang / Elixir流程是一个引人注目的例外。)

我建议您每隔一段时间窥视一下您的系统,看看有多少线程在任何时候都处于活动状态。

此外,对于这样的用途,在程序的生命周期中使用了许多线程,线程池通常用于大幅减少创建和销毁过程中产生的开销。线程。使用这些池,您可以获得固定数量的线程,这些线程在作业完成后可以重复使用。

以下是Ruby中一个线程池实现的文档链接:https://github.com/ruby-concurrency/concurrent-ruby/blob/master/doc/thread_pools.md