我在http4s& amp;写了一个顺序REST API爬虫。 fs2在这里:
https://gist.github.com/NicolasRouquette/656ed7a2d6984ce0995fd78a3aec2566
这是查询REST API服务以获取一组ID,获取一批ID的元素,并根据这些元素中找到的交叉引用ID继续,直到没有新的ID来获取和返回获取的所有元素的地图。
这有效;然而,表现不足 - 太慢了!
由于我无法访问服务器,因此我尝试了不同的批量大小,从10,50,100,200,500,甚至在单个查询中批量处理所有ID。查询时间随批量大小而显着增加。 在大尺寸(500和全部),我甚至从服务器获得HTTP 500响应。
我想尝试使用线程池以负载平衡方式批处理并行查询;但是,我不清楚如何根据fs2文档执行此操作。
有人可以提供如何实现这一目标的建议吗?
关于使用http4s& fs2:我发现这个库很容易用于简单的客户端编程。鉴于强调支持任务,流等...,我认为批处理并行查询应该是可行的。
答案 0 :(得分:1)
fs2.concurrent.join
将允许您同时运行多个流。指南中的特定部分位于https://github.com/functional-streams-for-scala/fs2/blob/v0.9.7/docs/guide.md#concurrency
对于您的用例,您可以获取ID队列,对其进行分块,创建http任务,然后将其包装在流中。然后,您将与join
同时运行此流流并合并结果。
def createHttpRequest(ids: Seq[ID]): Task[(ElementMap, Set[ID])] = ???
def fetch(queue: Set[ID]): Task[(ElementMap, Set[ID])] = {
val resultStreams = Stream.emits(queue.toSeq)
.vectorChunkN(batchSize)
.map(createHttpRequest)
.map(Stream.eval)
val resultStream = fs2.concurrent.join(maxOpen)(resultStreams)
resultStream.runFold((Map.empty[ID, Element], Set.empty[ID])) {
case ((a, b), (_a, _b)) => (a ++ _a, b ++ _b)
}
}