在 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 来向您的程序发送终止信号。
一种简单的方法是使用 MVar
s,当您的队列收到某个 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"
我正在尝试 运行 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 来向您的程序发送终止信号。
一种简单的方法是使用 MVar
s,当您的队列收到某个 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"