Scala的Akka期货

Akka是基于参与者的事件驱动框架,用于构建高度并发,可靠的应用程序。 未来的概念在这样的系统中无处不在,这不足为奇。 通常,您从不阻止等待响应,而是发送消息并期望响应在将来的某个时间到达。 听起来很适合……期货。 此外,Akka的期货之所以特别,有两个原因:Scala语法和类型推断一起大大提高了可读性和单子性。 要充分了解后者的优势,请查看scala.Option备忘单(如果您尚未在Scala中实际掌握monad)。

我们将继续采用另一种方法的Web爬网程序示例 ,这次将Akka置于Scala之上。 首先是基本语法:

val future = Future {
    Source.fromURL(
        new URL("http://www.example.com"), StandardCharsets.UTF_8.name()
    ).mkString

那很快! futurescala.concurrent.Future[String]推断的类型。 所提供的代码块将在以后异步执行,并且futureFuture[String]类型的)表示该块的值的句柄。 现在,您应该想知道,如何配置运行此任务的线程? 很好的问题,此代码无法按现状进行编译,需要ExecutionContext才能进行处理。 ExecutionContext类似于ExecutorService但可以隐式给出。 您有几种选择:

import ExecutionContext.Implicits.global
 
//or
 
implicit val ec = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(50))
 
//or (inside actor)
 
import context.dispatcher
 
//or (explicitly)
 
val future = Future {
    //...
} (ec)

第一种方法使用内置的执行上下文,该上下文由尽可能多的CPU /内核组成。 仅在小型应用程序中使用此上下文,因为它无法很好地扩展并且非常不灵活。 第二种方法采用现有的ExecutorService并将其包装。 您可以完全控制线程的数量及其行为。 注意implicit val是如何自动拾取的。 如果您在actor内部,则可以重用Akka dispatcher以使用actor使用的相同线程池来运行任务。 最后,您当然可以显式传递ExecutionContext 。 在下一个示例中,我假设一些隐式上下文可用。

Future实例,我们想处理结果。 我什至没有提到同步阻止和等待它们(但如果确实需要,请查看官方文档 )。 ListenableFuture番石榴ListenableFuture精神,我们将首先注册一些完成回调:

Future {
    Source.fromURL(new URL("http://www.example.com"), StandardCharsets.UTF_8.name()).mkString
} onComplete {
    case Success(html) => logger.info("Result: " + html)
    case Failure(ex) => logger.error("Problem", ex)
}

感觉很像ListenableFuture但语法更ListenableFuture 。 但是,我们的包装盒中还有更强大的工具。 请记住,上次我们有一种同步方法来解析下载HTML,另一种异步方法来计算文档的相关性 (无论如何):

def downloadPage(url: URL) = Future {
    Source.fromURL(url, StandardCharsets.UTF_8.name()).mkString
}
 
def parse(html: String): Document = //...
 
def calculateRelevance(doc: Document): Future[Double] = //...

当然,我们可以注册onComplete回调,但是Akka / Scala中的Future是monad,因此我们可以将数据作为一系列链接的强类型转换(为清楚起见而保留的显式类型)进行处理:

val htmlFuture:         Future[String]   = downloadPage(new URL("http://www.example.com"))
val documentFuture:     Future[Document] = htmlFuture map parse
val relevanceFuture:    Future[Double]   = documentFuture flatMap calculateRelevance
val bigRelevanceFuture: Future[Double]   = relevanceFuture filter {_ > 0.5}
bigRelevanceFuture foreach println

我想在这里说清楚。 调用Future.map(someOperation)不会等待该将来完成。 它只是把它包装并运行someOperation它完成的那一刻,一段时间的,ekhem,未来。 同样适用于Future.filterFuture.foreach 。 在这种情况下,您可能会感到惊讶,因为我们通常将此类运算符与集合相关联。 但是,就像Option[T]Future[T]大大简化了一个可能包含也可能不包含一个元素的集合。 通过这种比较,显然上面的代码是做什么的。 Future.filter调用可能不清楚,但是它基本上表明我们对不满足某些条件的异步操作的结果不感兴趣。 如果谓词产生false ,则永远不会执行foreach操作。

当然,您可以利用类型推断和链接来获得更简洁的信息,但不一定更容易阅读代码:

downloadPage(new URL("http://www.example.com")).
    map(parse).
    flatMap(calculateRelevance).
    filter(_ > 0.5).
    foreach(println

但是最大的胜利来自于悟性。 您可能没有意识到,但是由于Future实现了mapforeachfilter等(简化),因此我们可以在内部使用它进行理解(与Option[T] )。 因此,另一种可能是最易读的方法是:

for {
    html <- downloadPage(new URL("http://www.example.com"))
    relevance <- calculateRelevance(parse(html))
    if(relevance > 0.5)
} println(relevance)
 
println("Done")

感觉非常必要和有序,但是实际上理解的每个步骤都是异步执行的,这里没有阻塞。 "Done"消息将立即显示,远远早于计算的相关性。 这种构造带来了两全其美的效果–看起来是顺序的,但实际上是在后台运行。 此外,它隐藏了返回值与Future值的函数( mapflatMap )之间的模糊差异。

假设我们在上面的网站列表代码中运行了代码,该列表为我们提供了List[Future[Double]] ,现在我们希望在该List[Future[Double]]中找到最大的相关性。 现在,您应该拒绝所有涉及阻塞的解决方案。 在Scala中,有两种巧妙的方法可以做到这一点–通过将List[Future[Double]]Future[List[Double]]或折叠期货列表。 第一个解决方案与Guava中的Futures.allAsList相同:

val futures: Seq[Future[Double]] = //...
val future: Future[Seq[Double]] = Future sequence futures
future.onSuccess{
    case x => println(s"Max relevance: ${x.max}")
}

甚至更简洁(请记住,在两种情况下x都是Seq[Double]

Future.sequence(futures).map {x =>
    println(s"Max relevance: ${x.max}")
}

请记住,这里没有阻塞。 当上一个基础Future[Double]报告完成时, Future[Seq[Double]]完成。 如果您像一样喜欢foldLeft() (但不一定在这里),请考虑以下成语:

Future.fold(futures)(0.0) {_ max _} map {maxRel =>
    println(s"Max relevance: $maxRel")
}

这些给定的期货一个接一个地迭代,并在给定的期货成功时调用我们提供的{_ max _}折叠函数。

摘要

Scala和Akka中的期货功能非常强大:它们允许非阻塞,CPU有效的异步编程,但感觉像是命令式单线程编程。 您可以将转换序列应用于单个未来或它们的集合,就像该未来已经解决一样。 在您等待一个阶段,进行一些转换并运行第二阶段的地方,代码看起来绝对必要。 但实际上,一切都是异步的,并且是事件驱动的。 由于Future[V]单字性质和简洁的语法,Scala中的Future[V]是一个很好的工具,无需引入过多的仪式。

参考: NoBlogDefFound博客中来自我们JCG合作伙伴 Tomasz Nurkiewicz的Scala中的Akka期货

翻译自: https://www.javacodegeeks.com/2013/03/futures-in-akka-with-scala.html

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值