You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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长连接的标准行为。可行的解决办法有两种:

  1. 发送保活请求:在长间隔期间定期发送轻量哑请求(比如HEAD请求),维持连接活跃状态
  2. 调整连接池空闲超时:虽然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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.24 08:12:13