Haskell中的并发HTTP请求
我有一组函数,用于从AsanaAPI构建子任务树。为此,我有一个相当简单的模块“Asana.hs”,它最重要的两个功能是使用Haskell中的并发HTTP请求,haskell,asynchronous,concurrency,Haskell,Asynchronous,Concurrency,我有一组函数,用于从AsanaAPI构建子任务树。为此,我有一个相当简单的模块“Asana.hs”,它最重要的两个功能是使用Network.HTTP.simple执行请求: getTasksForProject::String->String->IO[Task] getTasksForProject令牌projectId=getFromAsana令牌$“projects/”++projectId++“/tasks” getSubtasks::String->String->IO[任务] getS
Network.HTTP.simple
执行请求:
getTasksForProject::String->String->IO[Task]
getTasksForProject令牌projectId=getFromAsana令牌$“projects/”++projectId++“/tasks”
getSubtasks::String->String->IO[任务]
getSubtasks token taskId=getFromAsana token$“任务/”+++taskId++“/subtasks”
问题是,当我想构建一个包含所有任务的图表时,我必须:
type TaskGraph=([Task],[Edge])
合并::TaskGraph->TaskGraph->TaskGraph
合并(aTasks,aEdges)(任务,bEdges)=(aTasks++bTasks,aEdges++bEdges)
makeEdge::关系->任务->任务->边缘
makeEdge rel父级子级=Edge rel(taskId父级)(taskId子级)
rFetchTaskGraph::String->Task->IO TaskGraph
rFetchTaskGraph令牌任务=do
子任务而不是
mapM (rFetchTaskGraph token) subtasks
使用
图书馆在哪里
但是,在进行并发HTTP请求时,应该小心地限制它们,以免使远程服务器崩溃或被其禁止。进行节流的一种简单方法是,使用a对rFetchTaskGraph
的所有调用进行选通,如中所述
因为rFetchTaskGraph
是递归的,所以它应该接受信号量作为参数,以便将其传递给其子调用:
rFetchTaskGraph :: QSem -> String -> Task -> IO TaskGraph
rFetchTaskGraph sem token task =
bracket_
(waitQSem sem)
(signalQSem sem)
(do
subtasks <- getSubtasks token $ taskId task
let edges = map (makeEdge Subtask task) subtasks
foldr merge ([task], edges) <$> mapConcurrently (rFetchTaskGraph sem token) subtasks)
rFetchTaskGraph::QSem->String->Task->IO TaskGraph
rFetchTaskGraph sem令牌任务=
括号
(waitQSem)
(信号QSEM sem)
(做
子任务听起来像是一个很好的用例,但我自己还没有使用过。感谢您的全面回答,我将研究所有这些选项,因为节流对我来说是一个重要的后续问题。出于兴趣,为什么您会发出信号说,QSem
的单元在等待后立即可用?这难道不会释放资源吗当它还在“使用中”,也就是说,如果do
块需要很长的时间,那么大量操作同时运行也会遇到同样的问题?没关系,我不知道方括号
做了什么,但在hoogle上查找了它:@GTF我想我的限制代码可能有问题。它同时包装了getsubtask
调用和递归ive步骤。但这可能会导致死锁。也许你应该将括号
的范围仅限于getSubtasks token$taskId task
部分。我所做的是分析代码,我大部分时间都能看到这一点(我想)花费在getFromAsana
中,这是HTTP请求和反序列化(通过Aeson)发生的地方。merge
中的累积时间几乎为零。
rFetchTaskGraph :: QSem -> String -> Task -> IO TaskGraph
rFetchTaskGraph sem token task =
bracket_
(waitQSem sem)
(signalQSem sem)
(do
subtasks <- getSubtasks token $ taskId task
let edges = map (makeEdge Subtask task) subtasks
foldr merge ([task], edges) <$> mapConcurrently (rFetchTaskGraph sem token) subtasks)
rFetchTaskGraph sem token task = do
subtasks <- bracket_ (waitQSem sem) (signalQSem sem) $ getSubtasks token $ taskId task
let edges = map (makeEdge Subtask task) subtasks
foldr merge ([task], edges) <$> mapConcurrently (rFetchTaskGraph sem token) subtasks