我有一段关于Haskell使用RabbitMQ的简单教程,
main :: IO ()
main = do
conn <- openConnection "127.0.0.1" "/" "guest" "guest"
ch <- openChannel conn
declareQueue ch newQueue {queueName = "hello",
queueAutoDelete = False,
queueDurable = False}
putStrLn " [*] Waiting for messages. To exit press CTRL+C"
consumeMsgs ch "hello" NoAck deliveryHandler
-- waits for keypresses
getLine
closeConnection conn
deliveryHandler :: (Message, Envelope) -> IO ()
deliveryHandler (msg, metadata) =
BL.putStrLn $ " [x] Received " <> msgBody msg
它仅说明了如何从队列中获取消息并通过回调对其进行处理。
一件事可能很容易解决,但是我很难理解如何在回调中添加一些可变的上下文,因此每次函数运行时都可以对其进行更改。简单来说,如何按队列顺序计算消息号。我发现一个可能的解决方案是State monad,是吗?
第二个问题-所有这些回调是否并行处理?如果不是,该如何并行处理它们并保持可变上下文而不发生数据争用?
答案 0 :(得分:3)
如果您打算并行处理多个消息(在同一Haskell进程中),我将从MVar开始以保持共享状态。
MVar基本上是一个带有锁和合理接口的共享变量。在简单的情况下(例如计数器),足以防止数据争用。这是共享内存的较低层(IORef)和较高层(STM)抽象之间的中间立场。我认为这是最容易理解的方法,我将其用于所有初始原型制作。
我不知道RabbitMQ库,因此无法回答您的第二个问题,即是否并行处理邮件。
答案 1 :(得分:2)
以@bergey的答案为基础-您可以创建可变引用,例如IORef或MVar。可以使用部分函数应用程序将这些引用传递给您的处理程序。键入但未经测试的代码。
main :: IO ()
main = do
conn <- openConnection "127.0.0.1" "/" "guest" "guest"
ch <- openChannel conn
ref <- newMVar 0
请注意上面的ref
和newMVar
中的生成函数Control.Concurrent.MVar
。
declareQueue ch newQueue {queueName = "hello",
queueAutoDelete = False,
queueDurable = False}
putStrLn " [*] Waiting for messages. To exit press CTRL+C"
consumeMsgs ch "hello" NoAck (deliveryHandler ref)
看看我们如何通过函数应用程序将引用传递给deliveryHandler
。
-- waits for keypresses
getLine
closeConnection conn
deliveryHandler :: MVar Int -> (Message, Envelope) -> IO ()
deliveryHandler ref (msg, metadata) =
BL.putStrLn $ " [x] Received " <> msgBody msg
withMVar' ref $ \val ->
do print val
pure (val + 1)
最后,我们可以使用Control.Concurrent.MVar
中的函数使用ref,获取旧值并根据需要用新值替换。