在 heroku 上保持后台进程 运行

keeping a background process running on heroku

我正在尝试 运行 heroku 上的 rabbitmq 后台进程从队列中挑选任务并处理它们。我正在使用 AMQP haskell 库,他们给出了以下内容 example(部分省略或简洁。

main = do
    --setup connection omitted
    --connect to queue, wait for messages
    consumeMsgs chan "myQueue" Ack myCallback
    --halts process so messages are taken off the queue until a key is presseed
    getLine -- wait for keypress
    closeConnection conn -- close connection after key
    putStrLn "connection closed"

这在本地工作正常,因为 getLine 保持进程 运行ning 直到你按下一个键。但是,当我将其部署到 heroku 时,进程以

退出

2016-04-19T08:37:23.373087+00:00 app[worker.1]: worker: <stdin>: hGetChar: end of file

我从 the accepted answer to this question 中了解到,这是因为为了通过 ssh 部署后台进程,您需要将 /dev/null/ 重定向到 stdin,它会发送一个 EOF向进程发出信号。

在我们的例子中,getLine 函数因为这个信号而退出,整个过程停止,防止我们的工作人员熬夜。

我如何在部署时保留此工作人员 运行ning?

编辑:最终解决方案 使用@carstons 评论我最终得到了以下有效的实现:

main :: IO ()
main = do
    mvar <- newEmptyMVar
    conn <- setupConnection
    queueName <- pack <$> getEnv "QUEUE_NAME"
    chan <- openChannel conn
    consumeMsgs chan queueName Ack processMessage
    installHandler sigINT (Catch (cleanupConnection conn mvar)) Nothing
    putStrLn "Running forever, press ctrl c to exit"
    -- this blocks until sigint is recieved and the handler for SIGINT
    -- "fills" the mvar. once that is filled the process exits
    run <- takeMVar mvar
    case run of
      _ -> return ()

mixpanelConfig :: IO Config
mixpanelConfig = liftM2 Config (ApiToken . pack <$> getEnv "MIXPANEL_API_TOKEN") (newManager tlsManagerSettings)

cleanupConnection :: Connection -> MVar () -> IO ()
cleanupConnection conn mvar = do
  closeConnection conn
  putStrLn "SIGINT received.. closing rabbitmq connection"
  putMVar mvar ()

processMessage :: (Message, Envelope) -> IO ()

正如我在评论中指出的那样,如果你只想永久保留它 运行,你可以使用 forever with - for example - threadDelay:

import Control.Concurrent (threadDelay)
import Control.Monad (forever)

main = do
    --setup connection omitted
    --connect to queue, wait for messages
    consumeMsgs chan "myQueue" Ack myCallback
    --halts process so messages are taken off the queue forever
    forever $ threadDelay 10000
    -- so this will never happen and you could remove it
    closeConnection conn -- close connection after key
    putStrLn "connection closed"

请注意,这当然不会真正关闭连接或退出应用程序 - 您必须终止该进程。


备选方案会更加复杂,因为您需要一些 message/way 来向您的程序发送终止信号。

一种简单的方法是使用 MVars,当您的队列收到某个 stop 消息时,您可以在 myCallback 中设置它:

import Control.Concurrent.MVar

main = do
    -- MVar to receve the quit-signal
    quitSignal <- newEmptyMVar
    --setup connection omitted
    --connect to queue, wait for messages - the callback should
    --set the quitSignal with putMVar quitSignal ()
    consumeMsgs chan "myQueue" Ack (myCallback quitSignal)
    --halts process so messages are taken off the queue till quitSignal
    takeMVar quitSignal
    -- so this will happen after quitSignal was set
    closeConnection conn -- close connection after key
    putStrLn "connection closed"