packages feed

c10k (empty) → 0.1.0

raw patch · 5 files changed

+264/−0 lines, 5 filesdep +basedep +haskell98dep +hdaemonizesetup-changed

Dependencies added: base, haskell98, hdaemonize, network, unix

Files

+ LICENSE view
@@ -0,0 +1,29 @@+Copyright (c) 2009, IIJ Innovation Institute Inc.+All rights reserved.++Redistribution and use in source and binary forms, with or without+modification, are permitted provided that the following conditions+are met:++  * Redistributions of source code must retain the above copyright+    notice, this list of conditions and the following disclaimer.+  * Redistributions in binary form must reproduce the above copyright+    notice, this list of conditions and the following disclaimer in+    the documentation and/or other materials provided with the+    distribution.+  * Neither the name of the copyright holders nor the names of its+    contributors may be used to endorse or promote products derived+    from this software without specific prior written permission.++THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS+"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT+LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS+FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE+COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,+INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,+BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER+CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT+LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN+ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE+POSSIBILITY OF SUCH DAMAGE.
+ Network/C10kServer.hs view
@@ -0,0 +1,162 @@+{-|+  Network server library to handle over 10,000 connections. Since+  GHC 6.10.4 or earlier uses select(), it cannot handle connections over+  1,024. This library uses the \"prefork\" technique to get over the+  barrier. Each process handles 'threadNumberPerProcess' connections.+  'preforkProcessNumber' child server processes are preforked. So, this+  server can handle 'preforkProcessNumber' * 'threadNumberPerProcess'+  connections. To stop all server, send SIGTERM to the parent+  process. (e.g. @kill `cat PIDFILE`@ where the PID file name is+  specified by 'pidFile')+-}+module Network.C10kServer (C10kServer, C10kConfig(..),+                           runC10kServer) where++import Control.Concurrent+import Control.Exception+import Control.Monad+import IO hiding (catch, try)+import Network hiding (accept)+import Network.Socket hiding (accept)+import Network.TCPInfo+import Prelude hiding (catch)+import System.Posix.Process+import System.Posix.Signals+import System.Posix.User+import System.Exit++----------------------------------------------------------------++{-|+  The type of the first argument of 'runC10kServer'.+-}+type C10kServer = Handle -> TCPInfo -> IO ()++{-|+  The type of configuration given to 'runC10kServer' as the second+  argument.+-}+data C10kConfig = C10kConfig {+  -- | A hook called initialization time. This is used topically to+  --   initialize syslog.+    initHook :: IO ()+  -- | A hook called when the server exits due to an error.+  , exitHook :: String -> IO ()+  -- | A hook to be called in the parent process when all child+  --   process are preforked successfully.+  , parentStartedHook :: IO ()+  -- | A hook to be called when each child process is started+  --   successfully.+  , startedHook :: IO ()+  -- | The time in seconds that a main thread of each child process+  --   to sleep when the number of connection reaches+  --   'threadNumberPerProcess'.+  , sleepTimer :: Int+  -- | The number of child process.+  , preforkProcessNumber :: Int+  -- | The number of thread which a process handle.+  , threadNumberPerProcess :: Int+  -- | A port name. e.g. \"http\" or \"80\"+  , portName :: ServiceName+  -- | A file where the process ID of the parent process is written.+  , pidFile :: FilePath+  -- | A user name. When the program linked with this library runs+  --   in the root privilege, set user to this value. Otherwise,+  --   this value is ignored.+  , user :: String+  -- | A group name. When the program linked with this library runs+  --   in the root privilege, set group to this value. Otherwise,+  --   this value is ignored.+  , group :: String+}++----------------------------------------------------------------++{-|+  Run 'C10kServer' with 'C10kConfig'.+-}+runC10kServer :: C10kServer -> C10kConfig -> IO ()+runC10kServer srv cnf = do+    initHook cnf `catch` ignore+    initServer srv cnf `catch` errorHandle+    parentStartedHook cnf `catch` ignore+    doNothing+  where+    errorHandle :: SomeException -> IO ()+    errorHandle e = do+      exitHook cnf (show e)+      exitFailure+    doNothing = do+      threadDelay $ 5 * microseconds+      doNothing++----------------------------------------------------------------++initServer :: C10kServer -> C10kConfig -> IO ()+initServer srv cnf = do+    let port = Service $ portName cnf+        n    = preforkProcessNumber cnf+        pidf = pidFile cnf+    s <- listenOn port+    setGroupUser+    preFork s n srv cnf+    sClose s+    writePidFile pidf+  where+    writePidFile pidf = do+        pid <- getProcessID+        writeFile pidf $ show pid ++ "\n"+    setGroupUser = do+      uid <- getRealUserID+      when (uid == 0) $ do+        getGroupEntryForName (group cnf) >>= setGroupID . groupID+        getUserEntryForName (user cnf) >>= setUserID . userID++preFork :: Socket -> Int -> C10kServer -> C10kConfig -> IO ()+preFork s n srv cnf = do+    ignoreSigChild+    pid <- getProcessID+    cids <- replicateM n $ forkProcess (runServer s srv cnf)+    mapM_ (terminator pid cids) [sigTERM,sigINT]+  where+    ignoreSigChild = installHandler sigCHLD Ignore Nothing+    terminator pid cids sig = installHandler sig (Catch (terminate pid cids)) Nothing+    terminate pid cids = do+        mapM_ terminateChild cids+        signalProcess killProcess pid+    terminateChild cid =  signalProcess sigTERM cid `catch` ignore++----------------------------------------------------------------++runServer :: Socket -> C10kServer -> C10kConfig -> IO ()+runServer s srv cnf = do+    startedHook cnf+    mvar <- newMVar 0+    dispatchOrSleep mvar s srv cnf++dispatchOrSleep :: MVar Int -> Socket -> C10kServer -> C10kConfig -> IO ()+dispatchOrSleep mvar s srv cnf = do+    n <- howMany+    if n > threadNumberPerProcess cnf+        then sleep (sleepTimer cnf * microseconds)+        else dispatch+    dispatchOrSleep mvar s srv cnf+  where+    dispatch = do+        (hdl,tcpi) <- accept s+        increase+        forkIO $ srv hdl tcpi `finally` (decrease >> hClose hdl)+        return ()+    howMany = readMVar mvar+    increase = modifyMVar_ mvar (return . succ)+    decrease = modifyMVar_ mvar (return . pred)+    sleep = threadDelay+++----------------------------------------------------------------++ignore :: SomeException -> IO ()+ignore _ = return ()++microseconds :: Int+microseconds = 1000000
+ Network/TCPInfo.hs view
@@ -0,0 +1,44 @@+{-|+  Yet another accept() to tell TCP information.+-}+module Network.TCPInfo where++import Data.List+import Network.Socket+import System.IO++{-|+  A Type to carry TCP information.+-}+data TCPInfo = TCPInfo {+  -- | Local IP address+    myAddr :: HostName+  -- | Local port number+  , myPort :: ServiceName+  -- | Remote IP address+  , peerAddr :: HostName+  -- | Remote port number+  , peerPort :: ServiceName+  } deriving (Eq,Show)++----------------------------------------------------------------++{-|+  Yet another accept() to return both 'Handle' and 'TCPInfo'.+-}+accept :: Socket -> IO (Handle, TCPInfo)+accept sock = do+    (sock', sockaddr) <- Network.Socket.accept sock+    let getInfo = getNameInfo [NI_NUMERICHOST, NI_NUMERICSERV] True True+    (Just vMyAddr,   Just vMyPort)   <- getSocketName sock' >>= getInfo+    (Just vPeerAddr, Just vPeerPort) <- getInfo sockaddr+    hdl <- socketToHandle sock' ReadWriteMode+    let info = TCPInfo { myAddr = strip vMyAddr+                       , myPort = vMyPort+                       , peerAddr = strip vPeerAddr+                       , peerPort = vPeerPort}+    return (hdl, info)+  where+    strip x+      | "::ffff:" `isPrefixOf` x = drop 7 x+      | otherwise                = x
+ Setup.hs view
@@ -0,0 +1,2 @@+import Distribution.Simple+main = defaultMain
+ c10k.cabal view
@@ -0,0 +1,27 @@+Name:                   c10k+Version:                0.1.0+Author:                 Kazu Yamamoto <kazu@iij.ad.jp>+Maintainer:             Kazu Yamamoto <kazu@iij.ad.jp>+License:                BSD3+License-File:           LICENSE+Synopsis:               C10k server+Description:            Network server library to handle+			over 10,000 connections. Since GHC 6.10.4 or+			earlier uses select(), it cannot handle+			connections over 1,024. This library uses+			the prefork technique to get over the barrier.+                        Programs complied by GHC 6.10.4 or earlier+                        with the -threaded option kill the IO thread+                        when forkProcess is used. So, don't specify+                        the -threaded option to use this library.+Homepage:               http://github.com/kazu-yamamoto/c10k+Category:               Network+Cabal-Version:          >= 1.6+Build-Type:             Simple+Library+  GHC-Options:          -Wall -O2+  Exposed-Modules:      Network.C10kServer, Network.TCPInfo+  Build-Depends:        base >= 4 && < 10, haskell98, network, unix, hdaemonize+Source-Repository head+  Type:                 git+  Location:             git://github.com/kazu-yamamoto/c10k.git