Неожиданно низкая пропускная способность для сетевых операций ввода-вывода с использованием Scotty
Я попытался сравнить Scotty, чтобы проверить эффективность сетевого ввода/вывода и общую пропускную способность.
Для этого я настроил два локальных сервера, написанных в 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"
Я написал другой сервер, который вызывает этот сервер и возвращает ответ.
{-# 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
При работе обоих этих серверов я выполнил сравнительный анализ 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
Хотя это было значительно выше, чем другие веб-серверы, такие как Phoenix, я понял, что это ничего не значит, поскольку большинство ответов было 500 ошибок, возникающих из-за исчерпания файлового дескриптора.
Я проверяю пределы, которые были довольно низкими.
ulimit -n
256
Я увеличил эти пределы до
ulimit -n 10240
Я снова запустил 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
Хотя количество 500 ошибок уменьшилось, они не были устранены. Я сравнивал Gin и Phoenix, и они были лучше, чем Scotty
, не давая никаких 500 ответов.
Какую часть головоломки мне не хватает? Я подозреваю, что есть проблема, которую я не отлаживаю.
Я понимаю, что http-conduit имеет много общего с этими ошибками, а библиотека http-client
использует его под капотом, и это не имеет ничего общего с Scotty
.
Ответы
Ответ 1
@Аналогичная аналогия была правильной. При повторном запуске сервера все проблемы, связанные с кодом состояния не 2xx, исчезли.
Первой строкой в главном блоке был виновник.
Я изменил строку с
main = do
let man = newManager defaultManagerSettings
к
main = do
man <- newManager defaultManagerSettings
и вуаля, проблем не было. Также высокий уровень использования памяти в программе стабилизировался до 21 МБ от 1 ГБ ранее.
Однако я не знаю причины. Было бы неплохо объяснить это.