2015-06-03 5 views
16

Я попытался оценить Скотти, чтобы проверить эффективность сетевого ввода-вывода и общую пропускную способность.Неожиданно низкая пропускная способность для сетевых операций ввода-вывода с использованием 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 

Хотя это было значительно выше, чем у других веб-сервера, как Феникс, я понял, что это ничего не значит, как большинство ответов было 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.

+0

Что делать, если вы повторно используете 'Manager' вместо его создания каждый раз? Например. передайте 'Manager' вместо' IO Manager' в 'testGet'. Кроме того, 'http-client' полагается на' GC' для повторного использования подключений, которые могут (или не могут) объяснять исчерпывание дескриптора файла. – Yuras

+0

Я удалил обертку IO вокруг менеджера и протестировал ее снова. Не имеет значения.Существует основная проблема, которую я не могу отлаживать. – user2512324

+0

Возможно, попробуйте запустить серверы из 'strace'? – MaxB

ответ

1

@ Аналогия Юраса была правильной. При повторном запуске сервера все проблемы, связанные с кодом состояния не 2xx, исчезли.

Первой линией в главном блоке был виновник. я изменил линию от

main = do 
    let man = newManager defaultManagerSettings 

в

main = do 
    man <- newManager defaultManagerSettings 

и вуаля, не было никаких проблем. Также высокий уровень использования памяти в программе стабилизировался до 21 МБ от 1 ГБ ранее.

Однако я не знаю причины. Было бы неплохо объяснить это.