Elixir Phoenix应用程序中的工作进程队列

Elixir Phoenix应用程序中的工作进程队列,elixir,phoenix-framework,Elixir,Phoenix Framework,我需要PubSub之类的东西,但不是向所有订阅者广播,而是只向1个订阅者发送消息(最好根据接收缓冲区中的消息数自动选择订阅者,越低越好) 我尝试的是使用受控数量的分布式工作者发送数十万个http请求。要解决这个问题,我首先要尝试的是让工作者拉取要发出的请求,而不是将请求推送到他们那里 因此,我有一个全局注册的代理,它保存了http请求的列表,这些请求将通过API来执行,用于添加和检索请求。然后,我将使用worker(Task,…)和one-for-one启动N个工人,而不是在这个阶段添加pool

我需要PubSub之类的东西,但不是向所有订阅者广播,而是只向1个订阅者发送消息(最好根据接收缓冲区中的消息数自动选择订阅者,越低越好)


我尝试的是使用受控数量的分布式工作者发送数十万个http请求。

要解决这个问题,我首先要尝试的是让工作者拉取要发出的请求,而不是将请求推送到他们那里

因此,我有一个全局注册的
代理
,它保存了http请求的列表,这些请求将通过API来执行,用于添加和检索请求。然后,我将使用
worker(Task,…)
one-for-one
启动N个工人,而不是在这个阶段添加poolboy。每个工作人员都会要求
代理
发出http请求并执行任何必要的工作,然后正常终止,由主管重新启动并请求新的url

工作者从代理中的列表中提取http任务,而不是将它们推送到代理中,这将确保在有工作要做的情况下,可用工作者始终处于忙碌状态


如果这个解决方案看起来不错,我会考虑加入台球男孩。您需要小心管理者选项,这样一堆导致工作人员崩溃的错误URL不会触发管理者删除所有其他内容。

如我在评论中所述,我的方法是使用Poolboy处理工作人员,但不可能只请求N个工作人员(N是请求的URL数量)因为这将很快超过进程限制,并导致签出请求超时。相反,您需要一个循环来检查工人是否可用,如果可用,则异步请求url。如果没有工作线程空闲,它应该休眠一段时间,然后重试

为此,Poolboy具有
:Poolboy.checkout/2
函数,第二个参数允许我们指定它是否应该阻止。如果没有可用的工作进程,它将返回
:full
,否则您将返回一个工作进程pid

例如:

def crawl_parallel(urls) do
  urls
  |> Enum.map(&crawl_task/1)
  |> Enum.map(&Task.await/1)
end

defp crawl_task(url) do
  case :poolboy.checkout Crawler, false do
    :full ->
      # No free workers, wait a bit and retry
      :timer.sleep 100
      crawl_task url
    worker_pid -> 
      # We have a worker, asynchronously crawl the url
      Task.async fn ->
        Crawler.Worker.crawl worker_pid, url
        :poolboy.checkin Crawler, worker_pid
      end
  end
end

不确定这是否是您正在寻找的,但请看一看:@OnorioCatenacci poolboy将只是此类系统的一个组件。它将处理一批工人,但在重载情况下,工人将开始失败。你不能简单地要求任意数量的工人,他们最终会超时。如果我错了,有人会纠正我,但是将超时设置为
:infinity
似乎也不是一个特别好的主意。我假设您可以构建一个带有运行循环的消息队列,该循环尝试以非阻塞方式从poolboy请求工作线程,然后使用该工作线程,或者如果没有可用的工作线程,则等待一定时间,然后重试。因此(对于分布式操作)每个节点是否有一个代理,工作人员将以循环方式检查其他节点代理?您可以这样做。在我看来,我只是一个经纪人,但那将是一个单一的失败点。您可以将分布在mnesia中的数据存储起来,并在本地工作人员可以与之交谈的每个节点上都有一个受监督的代理或普通GenServer。有一种叫做健忘症的长生不老药记忆包装器,但是直接使用erlang库也很容易,我还没有试过。似乎可以使用全局名称注册池,然后可以从不同的节点请求工作者。但是,我非常确信,对worker的genserver调用最终会在生成池的节点上执行。也许您需要使用RabbitMQ或其他更复杂的方法。我在这里阅读固定URL列表,但您可以将其替换为从队列弹出的循环,然后每个节点有一个这样的循环和一个池。您应该看看Pooler-。它似乎能够管理不同节点上的多个池。我目前正在使用我的rails项目,刚刚进入elixir,希望有一种内置的方法可以跨集群分发任务(并消除依赖关系),这会很好,尽管这种系统有很多细节,可以根据您的需要而有所不同。很难,甚至不可能想出一个标准库附带的通用解决方案。Elixir中甚至还没有pmap,因为有很多可能的实现。