Haskell req包:如何用withReqManager复用同一主机连接?
问题
我想复用HTTPS连接来降低同一主机多次请求的延迟。用Haskell的req包时,短时间发送过多请求会触发Manager的连接限制,导致新建连接、延迟升高,还无法修改该包中Manager的连接限制。官方文档推荐用withReqManager复用连接,但我搞不懂用法,希望有人说明如何用它让指定的一系列请求始终复用连接。
另外,请求间隔过久时连接也无法复用:间隔5秒可复用,间隔60秒则不行。如果能提供使用withReqManager让每次调用ttst都复用连接的示例,我将十分感激。
示例代码:
lttest :: IO () lttest = do ttst threadDelay 5000000 ttst threadDelay 60000000 ttst where ttst = do metm <- getCurrentTime runReq defaultHttpConfig { httpConfigCheckResponse = \_ _ _ -> Nothing } $ do v <- req GET (https "ifconfig.me") (NoReqBody) lbsResponse mempty liftIO $ print (responseBody v :: Data.ByteString.Lazy.ByteString) metm2 <- getCurrentTime print (diffUTCTime metm2 metm)
解决方案
用req包的withReqManager实现连接复用
你之前的代码每次调用ttst都会新建一个Manager,自然无法复用连接。withReqManager的核心作用是创建一个持久的Manager实例,并在其作用域内统一处理所有请求,让所有请求共享同一个连接池。
修改后的示例代码如下:
import Data.Time.Clock import Control.Concurrent (threadDelay) import Network.HTTP.Req lttest :: IO () lttest = withReqManager defaultHttpConfig { httpConfigCheckResponse = \_ _ _ -> Nothing } $ \manager -> do ttst manager threadDelay 5000000 ttst manager threadDelay 60000000 ttst manager where ttst manager = do metm <- getCurrentTime v <- runReq manager $ req GET (https "ifconfig.me") NoReqBody lbsResponse mempty print (responseBody v :: Data.ByteString.Lazy.ByteString) metm2 <- getCurrentTime print (diffUTCTime metm2 metm)
关键要点:
- 把
withReqManager作为外层容器,一次性创建共享的Manager实例 - 将Manager传递给
ttst,所有请求都通过这个Manager执行,避免重复创建 runReq可直接接收Manager参数,无需重复传入完整HttpConfig(配置已在创建Manager时指定)
解决长间隔后连接无法复用的问题
间隔60秒后连接失效,本质是服务器会主动关闭空闲过久的长连接,这是HTTP长连接的标准行为。可行的解决办法有两种:
- 发送保活请求:在长间隔期间定期发送轻量哑请求(比如HEAD请求),维持连接活跃状态
- 调整连接池空闲超时:虽然req包Manager的默认参数无法直接修改,但可以通过自定义
HttpConfig中的httpConfigManagerSettings调整连接池的空闲超时时间。不过要注意,即使客户端延长超时,服务器端的超时设置仍可能导致连接关闭,因此保活请求是更可靠的方案。
替代方案:用wreq包实现连接复用
如果愿意换用wreq包,可以通过Network.Wreq.Session模块创建持久会话,所有请求传入同一个Session实例即可复用连接:
import Data.Time.Clock import Control.Concurrent (threadDelay) import Network.Wreq import qualified Data.ByteString.Lazy as LBS lttest :: IO () lttest = withSession $ \session -> do ttst session threadDelay 5000000 ttst session threadDelay 60000000 ttst session where ttst session = do metm <- getCurrentTime resp <- getWith defaults session "https://ifconfig.me" print (responseBody resp :: LBS.ByteString) metm2 <- getCurrentTime print (diffUTCTime metm2 metm)
注意:wreq的同一个Session不能并发使用,否则无法复用连接,所有请求必须串行执行。
内容的提问来源于stack exchange,提问作者Anon
相关产品推荐
相关产品推荐

