我目前正在使用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
这种模式无休止地重复着。
答案 0 :(得分:2)
首先,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