Consider the simplest scotty app:
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Data.Monoid (mconcat)
main = scotty 3000 $ do
get "/:word" $ do
beam <- param "word"
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
I put this code into app.hs
and compile it with GHC. I run it with ./app
. Simple.
What really happens when people visit the site? It's only one ./app
that's running. Does a new thread get created within this same app whenever each user triggers a get "/:word" $ do
line? How many such threads can exist? Thousand? Ten thousand?
After running ./app
it shows the message Setting phasers to stun... (port 3000) (ctrl-c to quit)
. But it shows nothing more. It doesn't output the incoming web requests. How can I make it do that? This would be useful for logging purposes.
Assuming you are using GHC, each request to the scotty server essentially creates a "green thread" that is scheduled by the GHC runtime. You can easily have thousands of those running at a time.
Scotty itself doesn't do any request logging but since it is built on top of WAI, you can use any middleware component that exists for it, for example RequestLogger
.
{-# LANGUAGE OverloadedStrings #-}
import Web.Scotty
import Network.Wai.Middleware.RequestLogger
import Data.Monoid (mconcat)
main = scotty 3000 $ do
middleware logStdoutDev
get "/:word" $ do
beam <- param "word"
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
1. What really happens when people visit the site? It's only one ./app that's running. Does a new thread get created within this same app whenever each user triggers a get "/:word" $ do line? How many such threads can exist? Thousand? Ten thousand?
Scotty is build around warp, but could use any other library that implements the web application interface (WAI). A new lightweight thread gets created with forkIOUnmasked
(hidden in fork
in the module Network.Wai.Handler.Warp.Run
). You can have many of those:
Concurrency is "lightweight", which means that both thread creation and context switching overheads are extremely low. Scheduling of Haskell threads is done internally in the Haskell runtime system, and doesn't make use of any operating system-supplied thread packages. (source)
Here's a performance comparison between nginx and warp, which also includes information about the general idea behind warp.
2. After running ./app it shows the message Setting phasers to stun... (port 3000) (ctrl-c to quit). But it shows nothing more. It doesn't output the incoming web requests. How can I make it do that? This would be useful for logging purposes.
What's the type of your do
block? It should be ScottyM
, since scotty :: Port -> ScottyM () -> IO ()
. If ScottyM
is an instance of MonadIO
, you can use liftIO
together with putStrLn
(or any other IO
action).
Now, ScottyM
is actually a type synonym for ScottyT
, which is in fact a instance of MonadIO
. Also, the inner monad ActionM
is also a type synonym for ActionT
, which is also a MonadIO
. Therefore, logging is as easy as
main = scotty 3000 $ do
liftIO $ putStrLn "incoming request"
get "/:word" $ do
beam <- param "word"
liftIO $ print $ mconcat ["get, word = ", beam]
html $ mconcat ["<h1>Scotty, ", beam, " me up!</h1>"]
However, keep in mind that logging to a terminal might not be a good idea when you really expect ten thousand requests per second.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With