使用zeromq的100%CPU使用率推送具有0个侦听器的套接字

时间:2014-08-10 03:00:46

标签: haskell zeromq

我目前正在使用ZeroMQ在多个进程之间实现通信管道,所有这些都使用推/拉机制。管道以一个生成任务的'呼吸机'开始,这也是我的问题也开始的地方:当没有连接工作人员时,ZeroMQ似乎正在使用100%的CPU负载。

以下是有问题的代码,它只尝试发送一条消息:

module Main where

import System.ZMQ4.Monadic
import Data.ByteString.Char8 (pack)

main :: IO ()
main = do
     runZMQ $ do
            publisher <- socket Push
            bind publisher "tcp://*:10150"

            send publisher [] (pack "foo")

            close publisher

如您所见,此代码非常简单,只是尝试将消息“foo”发送给任何订阅者。我希望这段代码能够在后台对此消息进行排队,但它看起来似乎是使用send命令进入一个永无止境的循环。在插座上设置高水位标记无效。

zguide中有一个与我想要实现的相似的示例:https://github.com/imatix/zguide/blob/master/examples/Haskell/taskvent.hs

在这个例子中,他们明确要求用户输入开始发送(具体来说,'当工人准备就绪时按下输入') - 这是他们解决这个问题的方式吗?

任何人都可以告诉我这里我做错了什么,或解决这个问题的最佳方法是什么?

修改

详细说明,以下程序(具有连接的侦听器)可以完美地运行:

module Main where

import System.ZMQ4.Monadic
import Data.ByteString.Char8 (pack, unpack)
import Control.Applicative ((<$>))

main :: IO ()
main = do
     runZMQ $ do
            publisher <- socket Push
            receiver  <- socket Pull

            bind    publisher "tcp://*:10150"
            connect receiver "tcp://127.0.0.1:10150"

            send publisher [] (pack "foo")    
            message <- unpack <$> receive receiver

            liftIO $ putStrLn ("received data: " ++ message)

按预期打印出接收的数据。

编辑2

使用strace,我能够破译显然zeromq处于轮询/选择无限循环中:

poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
--- SIGVTALRM {si_signo=SIGVTALRM, si_code=SI_TIMER, si_pid=0, si_uid=0, si_value=0} ---
rt_sigreturn()                          = 1
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
poll([{fd=8, events=POLLIN}], 1, 0)     = 0 (Timeout)
select(9, [], [8], NULL, NULL)          = 1 (out [8])
--- SIGVTALRM {si_signo=SIGVTALRM, si_code=SI_TIMER, si_pid=0, si_uid=0, si_value=0} ---
rt_sigreturn()                          = 1

这种模式无休止地重复着。

2 个答案:

答案 0 :(得分:2)

ZeroMQ架构不仅仅是另一个套接字包装

首先,ZeroMQ系列的共同父亲Pieter Hintjens建议忘记到目前为止处理套接字时可能使用的所有内容。

这有很多原因。

第一个是,ZeroMQ为您提供了一对新的,抽象的世界,你应该理解&#34;原则上&#34;宁愿生活在和平中而不是与之抗争 - 一个微观COSMOS (内部机制,一个人应该以某种方式尊重和生活)和一个 MACRO-cosmos ,构建了一套非常强大的可扩展 - 正式 - 通信 - 模式,可以进一步利用和集成到更高阶的分布式处理系统。

因此?

由于微型COSMOS ,您可以选择从低延迟/高性能实践中受益,而不是在代码的早期某处启动ZMQ Context实例,同样assign 1}} / setup / bind / connect所需的ZMQ原语元素&#39;实例(在你的案例中给出了一个可行的ZMQ原语原型的插座...... PUSH / PULL),而不是设置/关闭/设置/关闭资源ad-hoc,在无限循环中越少

ZeroMQ实例不是一次性用品,而是系统的资产。重新思考架构,微型COSMOS和MACRO-cosmos将为您提供巨大的工作。

下一步

由于MACRO-cosmos规则,PUSH / PULL(分布式)形式 - 通信模式假定消息仍在PUSH - 呃的内部(内部队列,直到PULL - 呃(无形中并且在对手的代码控制之外)握手并检索它。

这也意味着,如果你的代码试图

main :: IO ()
main = do
     runZMQ $ do
            -- PUSH side
            -- ^... beware the ZMQ uses a keyword PUB ( publish )
            --      BUT in a different pattern, very different pattern
            ...
            close publisher

代码碰到微COSMOS 内部性,其中ZMQ的 ZMQ_LINGER (默认== -1)参数的默认值为套接字导致 close 的尝试无限期等待,直到任何其他进程检索已经在 PUSH -er。

这就解释了EDIT1中的初始异议和​​临时观察,以及#34;连接&#34; PULL - 无法出现无休止的ZMQ_LINGER等待循环。此外,EDIT2只是可视化在低级{select | poll}循环中导致的省略。 Q.E.D。

最后的注释

绝对值得花几天时间和努力阅读Pieter Hintjens&#39;有关ZeroMQ的书籍。

宝石的最终堆积和那里的最佳实践。

比在试错循环中打几个单行好多了

(因为你已经把代码变成了一个主要的死胡同,&#34;问题&#34;在你的代码中,而不是在ZMQ-haskel绑定中)

答案 1 :(得分:1)

图书馆作者已将100%的CPU使用率确认为错误。它已在此提交中修复:

https://github.com/twittner/zeromq-haskell/commit/4a6bc238dcc81cee4c8407c32edb018e371ab1e4