在AMQP回调之间共享上下文

时间:2018-11-30 11:34:45

标签: haskell rabbitmq amqp

我有一段关于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,是吗?

第二个问题-所有这些回调是否并行处理?如果不是,该如何并行处理它们并保持可变上下文而不发生数据争用?

2 个答案:

答案 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

请注意上面的refnewMVar中的生成函数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,获取旧值并根据需要用新值替换。