我试图在heroku上运行rabbitmq后台进程,从队列中挑选任务并处理它们。我正在与AMQP·哈斯克尔库合作,他们给出了下面的例子(为了简洁,省略了一些部分。
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
保持进程运行,直到您按下某个键。但是,当我将其部署到 heroku 时,进程退出
2016-04-19T08:37:23.373087 00:00 app[worker.1]: worker:
我从这个问题的公认答案中发现,这是因为为了通过ssh部署后台进程,您需要将/dev/null/
重定向到stdin
,它会向进程发送EOF
信号。
在我们的例子中,getLine
函数因为这个信号而退出,整个过程停止,防止我们的工作人员熬夜。
我如何在部署时保持该工作人员运行?
编辑:最终解决方案 使用@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
和-例如-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"
请注意,这当然永远不会真正关闭连接或退出应用程序-您必须终止该进程。
另一种可能会更复杂一些,因为你需要一些消息/方法来给你的程序发送一个终止信号。
一种简单的方法是使用< code>MVar,当在队列中收到某个stop消息时,可以在< code>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"