2011-10-24 8 views
17

Mi piacerebbe realizzare un comando 'graziosa shutdown' per la mia webapp (in contrapposizione al mio primo istinto, che è quello di chiedere solo alle persone di uccidere il processo)Come si implementa un comando di spegnimento in un server WAI?

I miei primi due tentativi consistevano

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

Entrambi i quali appena allegramente ha restituito un risultato al client e continuò ad ascoltare. C'è qualcosa che un'applicazione può fare per uccidere il server? Questa è anche una cosa ragionevole da voler fare?

Confesso che non ho una comprensione molto forte di iteratee, solo abbastanza per sapere che posso consumare il mio input e che Iteratee è un'istanza MonadIO.

+2

strizzando gli occhi https://groups.google.com/forum/#!topic/yesodweb/VoenrabRUBQ, un trucco che sembra * * a lavorare per me è quello di utilizzare Concurrent Haskell: (1) Forcella fuga applicazione (2) attendere su un MVar e (3) quando sono pronto a smettere, basta mettere qualcosa in quel MVar ... – kowey

+2

Questo è quello che consiglierei pure. –

risposta

11
  1. Utilizzare un MVar. Blocca il thread principale fino a quando l'MVar è stato segnalato, quindi pulisci e esci.
  2. Chiama exitImmediately. Uno dei modi più veloci per abbattere il processo, e anche terribilmente fastidioso per il debug. Non credo che i finalizzatori/parentesi/i blocchi finali verranno richiamati durante la discesa, a seconda dell'applicazione potrebbe corrompere lo stato.
  3. Genera un'eccezione sul thread principale. Warp.run non rileva eccezioni, quindi funziona consentendo al gestore delle eccezioni predefinito sul thread principale (e solo il thread principale) di terminare il processo.

Come altri hanno già detto, l'utilizzo di un MVar è probabilmente l'opzione migliore. Ho incluso gli altri per completezza, ma hanno il loro posto. throwTo viene utilizzato un po 'nella libreria di base e ho lavorato su alcune applicazioni che utilizzano l'equivalente C di exitImmediately: exit(), anche se non ho incontrato alcuna app Haskell che utilizza questo metodo.

{-# 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 
+0

Grazie per aver coperto tutte le possibilità. Sono d'accordo che l'esaustività è utile, se non altro, per avere un'idea migliore di come funziona Warp. Ho finito per prendere la rotta shutdownByMVar, quindi accetterò la tua risposta – kowey

+0

Hm .. Ho provato l'approccio MVar e il server si ferma effettivamente. Tuttavia, l'arresto non è stato "aggraziato": le richieste rimanenti sono state eliminate. – wiz

+1

@wiz Sì, esce immediatamente. L'implementazione di un arresto regolare non è molto più coinvolgente, ho caricato un campione * non testato * qui: https://gist.github.com/NathanHowell/5435345 –

Problemi correlati