使用Scotty的网络I/O的吞吐量意外低

use*_*324 16 benchmarking haskell http-conduit scotty

我试图对Scotty进行测试,以测试网络I/O效率和整体吞吐量.

为此,我设置了两个用Haskell编写的本地服务器.一个不做任何事情而只是充当API的人.

代码相同

{-# LANGUAGE OverloadedStrings #-}


import Web.Scotty

import Network.Wai.Middleware.RequestLogger 

import Control.Monad
import Data.Text
import Control.Monad.Trans
import Data.ByteString
import Network.HTTP.Types (status302)
import Data.Time.Clock
import Data.Text.Lazy.Encoding (decodeUtf8)
import Control.Concurrent
import Network.HTTP.Conduit
import Network.Connection (TLSSettings (..))
import Network.HTTP.Client
import Network
main = do 
  scotty 4001 $ do
    middleware logStdoutDev
    get "/dummy_api" $ do
        text $ "dummy response"
Run Code Online (Sandbox Code Playgroud)

我写了另一个调用此服务器并返回响应的服务器.

{-# LANGUAGE OverloadedStrings #-}


import Web.Scotty

import Network.Wai.Middleware.RequestLogger 

import Control.Monad
import Control.Monad.Trans
import qualified Data.Text.Internal.Lazy as LT
import Data.ByteString
import Network.HTTP.Types (status302)
import Data.Time.Clock
import Data.Text.Lazy.Encoding (decodeUtf8)
import Control.Concurrent
import qualified Data.ByteString.Lazy as LB
import Network.HTTP.Conduit
import Network.Connection (TLSSettings (..))
import Network.HTTP.Client
import Network


main = do 
  let man = newManager defaultManagerSettings 
  scotty 3000 $ do
    middleware logStdoutDev

    get "/filters" $ do
        response <- liftIO $! (testGet man)
        json $ decodeUtf8 (LB.fromChunks response)

testGet :: IO Manager -> IO [B.ByteString]
testGet manager = do
    request <- parseUrl "http://localhost:4001/dummy_api"
    man <- manager
    let req = request { method = "GET", responseTimeout = Nothing, redirectCount = 0}
    a <- withResponse req man $ brConsume . responseBody
    return $! a
Run Code Online (Sandbox Code Playgroud)

在这两个服务器都运行的情况下,我执行了wrk基准测试并获得了极高的吞吐量.

wrk -t30 -c100 -d60s "http://localhost:3000/filters"
Running 1m test @ http://localhost:3000/filters
  30 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency    30.86ms   78.40ms   1.14s    95.63%
    Req/Sec   174.05     62.29     1.18k    76.20%
  287047 requests in 1.00m, 91.61MB read
  Socket errors: connect 0, read 0, write 0, timeout 118
  Non-2xx or 3xx responses: 284752
Requests/sec:   4776.57
Transfer/sec:      1.52MB
Run Code Online (Sandbox Code Playgroud)

虽然这明显高于凤凰城等其他网络服务器,但我意识到这并不意味着大多数响应是由于文件描述符耗尽而发生的500个错误.

我检查了非常低的限制.

ulimit -n
256
Run Code Online (Sandbox Code Playgroud)

我增加了这些限制

ulimit -n 10240
Run Code Online (Sandbox Code Playgroud)

我再次运行wrk,这次显然已经大大减少了吞吐量.

wrk -t30 -c100 -d60s "http://localhost:3000/filters"
Running 1m test @ http://localhost:3000/filters
  30 threads and 100 connections
  Thread Stats   Avg      Stdev     Max   +/- Stdev
    Latency   105.69ms  161.72ms   1.24s    96.27%
    Req/Sec    19.88     16.62   120.00     58.12%
  8207 requests in 1.00m, 1.42MB read
  Socket errors: connect 0, read 0, write 0, timeout 1961
  Non-2xx or 3xx responses: 1521
Requests/sec:    136.60
Transfer/sec:     24.24KB
Run Code Online (Sandbox Code Playgroud)

尽管500个错误的数量减少了,但它们并没有消除.我对杜松子酒和菲尼克斯进行了基准测试,他们比Scotty没有给出任何500回应更好.

我错过了什么谜题?我怀疑有一个问题我没有调试.

据我所知,http-conduit与这些错误有很大关系,而http-client库在引擎盖下使用它并没有任何关系Scotty.

use*_*324 1

@Yuras 的类比是正确的。再次运行服务器时,与非 2xx 状态代码相关的所有问题都消失了。

主区块中的第一行是罪魁祸首。我改变了线路

main = do 
  let man = newManager defaultManagerSettings
Run Code Online (Sandbox Code Playgroud)

main = do 
  man <- newManager defaultManagerSettings
Run Code Online (Sandbox Code Playgroud)

瞧,没有任何问题。此外,程序的高内存使用量也从之前的 1GB 稳定到了 21MB。

但我不知道原因。如果能对此做出解释就好了。