我有一个安静的博客服务器(http://jsonplaceholder.typicode.com)响应这两个URI
/posts/{id} : get a blog post by ID
/comments?postId={id} : get all the comments on a blog by the blog's id
从一批博客ID开始,我想创建一个执行以下步骤的流程:
/posts
端点获取帖子的json Blog
案例类/comments
端点,然后获取该帖子的JSON评论列表Comment
案例对象列表是的,我知道如果我有博客ID,我可以直接跳到第3步。假装我不能
我想在步骤1中向服务器发送一堆HTTP请求。为此,我使用cachedHostConnectionPool
。以下是我到目前为止的情况:
final case class Blog(id: Int, userId: Int, title: String, body: String)
final case class Comment(id: Int, postId: Int, name: String, email: String, body: String)
object AkkaStreamProcessor extends App {
implicit val actorSystem = ActorSystem("blogProcessor")
import actorSystem.dispatcher
implicit val flowMaterializer = ActorMaterializer()
private def getBlogUri(id: Integer): String = "/posts/" + id
private def getCommentsUri(blog: Blog): String = "/comments?postId=" + blog.id
private def parseBlogResponse(jsonResponse: String): Blog = Json.parse(jsonResponse).as[Blog]
private def parseCommentsResponse(jsonResponse: String): List[Comment] = Json.parse(jsonResponse).as[List[Comment]]
val pooledConnectionFlow = {
val connectionSettings = ConnectionPoolSettings(actorSystem)
.withMaxConnections(32)
.withMaxOpenRequests(32)
.withMaxRetries(3)
Http().cachedHostConnectionPool[Int](host = "jsonplaceholder.typicode.com", settings = connectionSettings)
}
val source = Source(1 to 32)
val fetchBlogsFlow = Flow[Int]
.map((id: Int) => (getBlogUri(id),id))
.map{ case(uri:String, id:Int) => (HttpRequest(method = HttpMethods.GET, uri = uri), id) }
.via(pooledConnectionFlow)
.map { case(response: Try[HttpResponse], id:Int) => handleBlogResponse(response, id) }
.map((jsonText: Try[String]) => jsonText.map(j => parseBlogResponse(j)))
val sink = Sink.foreach[Try[Blog]](blog => blog.map(b=> println(b)))
source.via(fetchBlogsFlow).runWith(sink)
private def handleBlogResponse(response: Try[HttpResponse], id: Int): Try[String] = {
println(s"Received response for id $id on thread ${Thread.currentThread().getName}")
response.flatMap((r: HttpResponse) => {
r.status match {
case StatusCodes.OK => {
Success(Await.result(Unmarshal(r.entity).to[String], Duration.Inf))
}
case _ => Failure(new RuntimeException("Invalid response : " + r.status.toString()))
}
})
}
}
现在我想要的是创建另一个流程来执行第3步和第4步,我将在第一个流程之后链接。但是,我正在努力解决第一个流程中令人讨厌的Try[Blog]
输出问题。如何将Try[Blog]
传输到另一个HTTP请求中?有没有办法拆分管道,失败是单向的,成功又走向另一条道路?
以下是我对第二个流程的内容,但我不确定如何在get
上调用Try
来进行链接工作:
val processBlogsFlow = Flow[Try[Blog]]
.map((tryBlog: Try[Blog]) => tryBlog.get)
.map((blog: Blog) => (HttpRequest(method=HttpMethods.GET, uri=getCommentsUri(blog)), blog.id ))
.via(pooledConnectionFlow)
答案 0 :(得分:1)
处理Try
非常好blog entry。在您的特定示例中,我将保留Try
,以便您可以获得有关原始故障的信息:
def blogToTuple(blog : Blog) =
(HttpRequest(method=HttpMethods.GET, uri=getCommentsUri(blog)), blog.id )
val processBlogsFlow : Flow[Try[Blog], Try[HttpResponse], _] =
Flow[Try[Blog]]
.map(_ map blogToTuple)
.mapAsync(1) { _ match {
case Success(req) =>
Source.single(req).via(pooledConnectionFlow).runWith(Sink.head)
case ex => Future { x }
}
}
现在Try
可以传递给您的Sink
,SELECT event_cust.*
FROM event_cust
WHERE TimeValue([start_time]) >= CDate([Forms]![CustEventRptForm]![FromHour])
AND TimeValue([start_time]) <= CDate([Forms]![CustEventRptForm]![ToHour])
可以报告任何错误消息并报告有效回复。