Как реализовать команду выключения на WAI-сервере? - PullRequest
18 голосов
/ 25 октября 2011

Я бы хотел реализовать команду «изящного выключения» для моего веб-приложения (в отличие от моего первого инстинкта, который заключается в том, чтобы просто попросить людей убить процесс)

Мои первые две попытки состояли из

  1. liftIO exitSuccess
  2. E.yield (responseLBS statusOK [G.contentType "text/plain"] "") E.EOF

Оба из них просто весело вернули результат клиенту и продолжили слушать. Что-нибудь, что приложение может сделать, чтобы убить сервер? Это даже разумная вещь, чтобы хотеть сделать?

Признаюсь, у меня нет четкого понимания iteratee, достаточно лишь знать, что я могу использовать свой ввод и что Iteratee является экземпляром MonadIO.

1 Ответ

12 голосов
/ 17 ноября 2011
  1. Используйте MVar.Блокируйте основной поток до тех пор, пока MVar не получит сигнал, затем очистите и выйдите.
  2. Вызов exitImmediately.Один из самых быстрых способов снести процесс, а также ужасно раздражает отладку.Я не верю, что блоки finalizer / brackets / finally будут вызываться по пути вниз, в зависимости от вашего приложения это может привести к повреждению состояния.
  3. Бросить исключение в основной поток.Warp.run не перехватывает исключения, поэтому это работает, позволяя обработчику исключений по умолчанию в главном потоке (и только в основном потоке) завершать процесс.

Как уже упоминалось, использованиеMVar, вероятно, лучший вариант.Я включил другие ради полноты, но у них есть свое место.throwTo в некоторой степени используется в базовой библиотеке, и я работал над несколькими приложениями, которые используют эквивалент C * exitImmediately: exit(), хотя я не запускал никаких приложений на Haskell, использующих этот метод.

{-# LANGUAGE DeriveDataTypeable #-}
{-# LANGUAGE OverloadedStrings #-}

module Main (main) where

import Control.Concurrent (MVar, ThreadId, forkIO, myThreadId, newEmptyMVar, putMVar, takeMVar)
import Control.Exception (Exception, throwTo)
import Control.Monad.Trans (liftIO)
import Data.ByteString (ByteString)
import Data.Data (Data, Typeable)
import Data.Enumerator (Iteratee)
import Network.HTTP.Types
import Network.Wai as Wai
import Network.Wai.Handler.Warp as Warp
import System.Exit (ExitCode (ExitSuccess))
import System.Posix.Process (exitImmediately)

data Shutdown = Shutdown deriving (Data, Typeable, Show)
instance Exception Shutdown

app :: ThreadId -> MVar () -> Request -> Iteratee ByteString IO Response
app mainThread shutdownMVar Request{pathInfo = pathInfo} = do
  liftIO $ case pathInfo of
    ["shutdownByThrowing"] -> throwTo mainThread Shutdown
    ["shutdownByMVar"]     -> putMVar shutdownMVar ()
    ["shutdownByExit"]     -> exitImmediately ExitSuccess
    _                      -> return ()
  return $ responseLBS statusOK [headerContentType "text/plain"] "ok"

main :: IO ()
main = do
  mainThread <- myThreadId
  shutdownMVar <- newEmptyMVar
  forkIO $ Warp.run 3000 (app mainThread shutdownMVar)
  takeMVar shutdownMVar 
...