将RabbitMQ的流数据并行加载到Postgres

时间:2018-11-18 09:39:58

标签: node.js postgresql parallel-processing rabbitmq

我对Node.js还是有些陌生,所以我对并行性如何与并发I / O操作一起工作并不熟悉。

我正在计划一个Node.js应用程序,以将流数据从RabbitMQ加载到Postgres。这些负载将在系统运行期间发生,因此不是大负载。

我希望开始时的吞吐量要求相当低(也许每分钟50-100条记录)。但是我想对应用程序进行规划,以便可以根据需求扩展到更大的数量。

我正在尝试思考并行性如何工作。对流的第一印象以及如何引入并行性是:

  1. 从队列中读取的消息
  2. 启动将数据加载到Postgres的查询,这将回调推送到Node堆栈
  3. 事件循环可以自由地从队列中读取另一条消息(如果有),这将启动另一个查询
  4. 重复

相信以这种方式启动的查询将并行运行,直到PG连接池中的连接数量达到为止。这是一个很好的假设吗?

通过这种简单的流程,并行查询的限制似乎就是Postgres连接池的大小。我可以将其设置为与吞吐量所需的大小一样大(并且服务器和后端数据库可以处理),这将成为我可以并行处理多少条消息的限制因素。听起来不错吗?

关于实例化多少个并行I / O节点,我还没有找到很好的参考。 Node最终会因为我的事件循环生成太多尚未解决的I / O请求而阻塞(如果没有,我假设pg在必须等待连接时会将查询放在回调堆栈上)?启动Node时是否可以通过设置开关来影响这些限制?我是否正确假设libuv和“ pg” lib实际上将在一个Node.js进程中并行运行这些查询?如果这些假设是正确的,我会认为在达到libuv并行性限制之前(或者如果同时将连接池的大小调整为服务器上的内核数),我会达到连接池大小的限制。

此外,与上面有关节点启动并行I / O请求的讨论有关,如何防止Node尽快将消息从队列中拉出并排队I / O请求?我认为这可能会导致内存消耗问题。这与我有关启动参数的问题有关,以限制创建的并行I / O请求的数量。在这一点上,我对此不太了解,所以可能不必担心(默认情况下,Node不会创建比内核更多的并行I / O请求,从而提供自然的限制?)。

我想知道的另一件事是何时/如何并行运行该程序的多个副本会有所帮助?因为Postgres连接池似乎是此处并行化的驱动程序,所以甚至在一台主机上也没有关系吗?如果是这种情况,我可能每个主机只运行一个副本,而仅在其他主机上运行其他副本以分散负载。

如您所见,在尝试这条路之前,我正在尝试获得一些基本假设。洞察力和指向好的参考文档的指针将不胜感激。

1 个答案:

答案 0 :(得分:0)

我通过编写的原型测试解决了这个问题。一些观察:

  1. 如果我未在RabbitMQ通道上设置预提取,Node将在几秒钟内将所有消息从队列中拉出。我进行了一个测试,将10万条消息从队列中取出,然后Node在几秒钟内将所有10万条消息都删除了,尽管实际处理这些消息需要花费几分钟。
  2. 上面#1中提到的行为是不希望的,因为Node必须将所有消息缓存在内存中。在我的测试中,当快速拉出所有这些消息时,Node占用了2GB,而如果我设置预取以匹配数据库连接的数量,则Node仅占用80 MB并缓慢耗尽了队列,因为它完成了对消息和消息的处理。发送回ACK。
  3. 运行该程序的单个Node实例使我的CPU利用率达到100%。

所以,这个故事的寓意似乎是:

  1. 节点可以产生任意数量的异步I / O处理程序(受可用内存限制)
  2. 在这种情况下,您希望限制节点产生的异步I / O请求数量,以避免过多的内存使用。
  3. 为此工作负载创建其他子进程没有影响。并行度的单位是数据库连接池的大小。如果我的工作量在JavaScript中做得更多,而不仅仅是委派给Postgres,则其他子进程将有所帮助。但是在这种情况下,所有的I / O(而且幸运的是I / O不需要Node线程池),因此其他子进程什么也不做。