http4s,Service Executor和Mongodb:我如何等待insertOne完成

时间:2016-11-27 02:54:48

标签: scala http4s

提前为基本问题道歉。我开始用http4s学习Scala,在路由器处理程序中,我试图输入MongoDB的一个条目。据我所知,insertOne会返回Observable[Completed]

在返回响应之前,我知道如何等待observalbe完成?

我的代码是:

class Routes {
    val service: HttpService = HttpService {
        case r @ GET -> Root / "hello" => {
            val mongoClient: MongoClient = MongoClient()
            val database: MongoDatabase = mongoClient.getDatabase("scala")
            val collection: MongoCollection[Document] = database.getCollection("tests")
            val doc: Document = Document("_id" -> 0, "name" -> "MongoDB", "type" -> "database",
                                 "count" -> 1, "info" -> Document("x" -> 203, "y" -> 102))
            collection.insertOne(doc)
            mongoClient.close()
            Ok("Hello.")
        }
    }
}

class GomadApp(host: String, port: Int) {
  private val pool = Executors.newCachedThreadPool()

  println(s"Starting server on '$host:$port'")

  val routes = new Routes().service

  // Add some logging to the service
  val service: HttpService = routes.local { req =>
    val path = req.uri
    val start = System.nanoTime()
    val result = req
    val time = ((System.nanoTime() - start) / 1000) / 1000.0
    println(s"${req.remoteAddr.getOrElse("null")} -> ${req.method}: $path in $time ms")
    result
  }

  // Construct the blaze pipeline.
  def build(): ServerBuilder =
    BlazeBuilder
      .bindHttp(port, host)
      .mountService(service)
      .withServiceExecutor(pool)
}

object GomadApp extends ServerApp {
  val ip   = "127.0.0.1"
  val port = envOrNone("HTTP_PORT") map (_.toInt) getOrElse (8787)

  override def server(args: List[String]): Task[Server] =
    new GomadApp(ip, port)
      .build()
      .start

}

1 个答案:

答案 0 :(得分:0)

我建议https://github.com/haghard/mongo-query-streams - 尽管你必须稍微分叉一下依赖项,但scalaz 7.1和7.2并不是二进制兼容的。

不那么流动(并且引用程度较低)的方式:https://github.com/Verizon/delorean

collection.insertOne(doc).toFuture().toTask.flatMap({res => Ok("Hello")})

后一种解决方案看起来更容易,但它有一些隐藏的陷阱。见https://www.reddit.com/r/scala/comments/3zofjl/why_is_future_totally_unusable/

  

这条推文让我想知道:https://twitter.com/timperrett/status/684584581048233984   你认为期货"完全无法使用"或者这只是夸张?我从未遇到过重大问题,但我愿意受到启发。以下代码没有有效地使Futures"懒惰&#34 ;? def myFuture =未来{42}   而且,最后,我也听到了scalaz的任务也有一些失误的声音,但我还没有发现它。有人有更多细节吗?

答案:

  

根本问题在于,构建具有副作用表达的Future本身就是一种副作用。你只能推理未来的纯计算,遗憾的是它们不是常用的。以下是此操作打破引用透明度的演示:

import scala.concurrent.Future
import scala.concurrent.ExecutionContext.Implicits.global
import scala.util.Random

val f1 = { 
  val r = new Random(0L)
  val x = Future(r.nextInt)
  for { 
    a <- x
    b <- x
  } yield (a, b) 
}

// Same as f1, but I inlined `x`
val f2 = { 
  val r = new Random(0L)
  for { 
    a <- Future(r.nextInt)
    b <- Future(r.nextInt)
  } yield (a, b) 
}

f1.onComplete(println) // Success((-1155484576,-1155484576))
f2.onComplete(println) // Success((-1155484576,-723955400))    <-- not the same
  

然而,这适用于Task。请注意,有趣的是非内联版本,它设法生成两个不同的Int值。这是重要的一点:Task有一个构造函数,它将副作用捕获为值,而Future则没有。

import scalaz.concurrent.Task

val task1 = { 
  val r = new Random(0L)
  val x = Task.delay(r.nextInt)
  for { 
    a <- x
    b <- x 
  } yield (a, b) 
}

// Same as task1, but I inlined `x`
val task2 = { 
  val r = new Random(0L)
  for { 
    a <- Task.delay(r.nextInt)
    b <- Task.delay(r.nextInt)
  } yield (a, b) 
}

println(task1.run) // (-1155484576,-723955400)
println(task2.run) // (-1155484576,-723955400)
  

大多数常见的差异,例如&#34;一个任务,直到你问它&#34;和#34;你可以一遍又一遍地组成同一个任务&#34;追溯到这一根本区别。   这就是它完全无法使用的原因&#34;一旦你习惯于用纯粹的价值观编程并依靠等式推理来理解和操纵程序,就很难回到那些事情难以理解的副作用世界。