我刚刚开始使用Haskell,现在我专注于网络。我跟着一些 教程和源代码示例将一个非常简单的echo服务器组合在一起:
main = withSocketsDo $ do
forkIO $ acceptor 8080
print "Server running ... " >> getLine >>= print
tcpSock :: IO Socket
tcpSock = socket AF_INET Stream 0
acceptor :: PortNumber -> IO ()
acceptor port = do
-- Setup server socket
sock <- tcpSock
setSocketOption sock ReuseAddr 1
bindSocket sock (SockAddrInet port iNADDR_ANY)
listen sock 50
-- Start with zero index
loop sock 0
where
loop sock sockId = do
-- Accept socket
(nextSock, addr) <- accept sock
-- Setup the socket for performance
(_, handle) <- setupClient nextSock
-- Run client in own thread
forkIO $ do
-- Get a stream of bytes
stream <- BS.hGetContents handle
-- Echo the first received char
BS.hPut handle $ BS.take 1 stream
-- Kill the socket
SIO.hClose handle
-- Accept next client
loop sock (sockId + 1)
setupClient :: Socket -> IO (Socket, SIO.Handle)
setupClient sock = do
-- Disable nagle
setSocketOption sock NoDelay 1
-- Disable buffering
hdl <- socketToHandle sock SIO.ReadWriteMode
SIO.hSetBuffering hdl SIO.NoBuffering
return (sock, hdl)
现在,我已经使用ab-Tool测试了代码来对服务器进行基准测试。代码已编译 使用-O2和-threaded并使用+ RTS -N启动程序以使用多个OS线程。
代码为每个客户端创建一个新的轻量级线程,据我记忆的是这些线程 非常便宜,因为它们是由一堆真正的操作系统线程安排的。
运行该工具后,结果为:
ab -n 10000 -c 1000 http://localhost:8080/
~500 - 1600 req / sec
是的,它有时会在500到1600之间发生变化!
起初我认为好,不错。然后我运行程序没有“+ RTS -N”,结果几乎是每一个 时间~20000 req / sec。
显然,线程会严重影响性能,但为什么呢? 我的猜测是,IO经理在处理大量连接时做得非常糟糕。
BTW:我使用的是Ubuntu 13.04和ghc 7.6,但是我已经在Windows 8下测试了代码,这给我带来了更糟糕的结果,但我认为IO管理器已经针对linux进行了调整,这是有道理的。我在这里做些蠢事吗?我知道,这个例子非常简单,但显然出现了问题。
此致 克里斯
答案 0 :(得分:1)
好吧,我认为我已经解决了这个问题,但我仍然不确定错误在哪里。
我现在正在使用Network包,因此accept例程是基于句柄的。我试过这个是因为我在几次测试后发现了内存泄漏。
这种方式我立刻解决了两个问题,因为现在线程没有区别。 我真的不知道为什么会这样,但基于句柄的impl。更简单,显然更快/更安全。
也许这有助于其他人遇到同样的问题。