Подтвердить что ты не робот

Как реализовать команду shutdown на сервере WAI?

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

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

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

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

Я признаюсь, что у меня нет очень сильного понимания итерации, достаточно, чтобы знать, что я могу использовать свой вклад и что Iteratee является экземпляром MonadIO.

4b9b3361

Ответ 1

  • Используйте MVar. Блокируйте основной поток до тех пор, пока не будет сигнализирован MVAR, затем очистка и выход.
  • Вызов exitImmediately. Один из самых быстрых способов снести процесс, а также ужасно раздражает отладки. Я не верю, что финализаторы/скобки/окончательные блоки будут вызваны по пути вниз, в зависимости от вашего приложения оно может испортить состояние.
  • Выбросить исключение из основного потока. 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