如何在akka中实现并发处理?

时间:2018-06-12 16:59:05

标签: scala concurrency akka

我有一个方法,其中有多个db调用。由于我没有实现任何并发处理,第二个db调用必须等到第一个db调用完成,第三个必须等到第二个db调用完成,依此类推。

所有数据库调用都是相互独立的。我希望以所有数据库调用同时运行的方式进行此操作。

我是Akka框架的新手。

有人可以帮我提供小样本或参考资料会有所帮助。应用程序是在Scala Lang开发的。

2 个答案:

答案 0 :(得分:1)

对于给定的示例需求,有三种主要方法可以实现并发。

<强>期货

对于问题中询问的特定用例,我会在任何akka构造之前推荐Futures。

假设我们将数据库调用作为函数:

type Data = ???

val dbcall1 : () => Data = ???

val dbcall2 : () => Data = ???

val dbcall3 : () => Data = ???

可以轻松应用并发,然后使用Futures收集结果:

val f1 = Future { dbcall1() }
val f2 = Future { dbcall2() }
val f3 = Future { dbcall3() }

for {
  v1 <- f1
  v2 <- f2
  v3 <- f3
} {
  println(s"All data collected: ${v1}, ${v2}, ${v3}")
}

Akka Streams

a similar stack answer演示如何使用akka-stream库进行并发数据库查询。

Akka Actors

也可以编写Actor来进行查询:

object MakeQuery

class DBActor(dbCall : () => Data) extends Actor {
  override def receive = {
    case _ : MakeQuery => sender ! dbCall()
  }
}

val dbcall1ActorRef = system.actorOf(Props(classOf[DBActor], dbcall1)) 

但是,在这个用例中,Actors不太有用,因为你仍然需要一起收集所有数据。

您可以使用与“期货”部分相同的技术:

val f1 : Future[Data] = (dbcall1ActorRef ? MakeQuery).mapTo[Data]

for {
  v1 <- f1
  ...

或者,您必须通过构造函数手动连接Actors并处理所有回调逻辑以等待另一个Actor:

class WaitingDBActor(dbCall : () => Data, previousActor : ActorRef) {
  override def receive = {
    case _ : MakeQuery => previousActor forward MakeQuery

    case previousData : Data => sender ! (dbCall(), previousData)
  }
}

答案 1 :(得分:0)

如果要查询数据库,则应使用slick之类的东西,它是Scala的现代数据库查询和访问库。

光滑的快速示例:

case class User(id: Option[Int], first: String, last: String)

class Users(tag: Tag) extends Table[User](tag, "users") {
  def id = column[Int]("id", O.PrimaryKey, O.AutoInc)
  def first = column[String]("first")
  def last = column[String]("last")
  def * = (id.?, first, last) <> (User.tupled, User.unapply)
}
val users = TableQuery[Users]

然后您需要为数据库创建配置:

mydb = {
  dataSourceClass = "org.postgresql.ds.PGSimpleDataSource"
  properties = {
    databaseName = "mydb"
    user = "myuser"
    password = "secret"
  }
  numThreads = 10
}

并在代码中加载配置:

val db = Database.forConfig("mydb")

然后使用db.run方法运行查询,该方法为您提供将来的结果,例如,您可以通过调用方法result获得所有行

val allRows: Future[Seq[User]] = db.run(users.result)

此查询在不阻止当前线程的情况下运行。

如果您的任务需要很长时间才能执行或调用其他服务,则应使用期货。

示例是对外部服务的简单HTTP调用。您可以在here

中找到示例

如果您需要花很长时间来执行任务,并且必须这样做,则必须保持可变状态,在这种情况下,最好的选择是使用Akka Actors,它将状态封装在一个Actor中,从而解决并发和线程安全问题尽可能简单。suck任务的示例是:

import akka.actor.Actor

import scala.concurrent.Future

case class RegisterEndpoint(endpoint: String)

case class NewUpdate(update: String)

class UpdateConsumer extends Actor {
  val endpoints = scala.collection.mutable.Set.empty[String]

  override def receive: Receive = {

    case RegisterEndpoint(endpoint) =>
      endpoints += endpoint

    case NewUpdate(update) =>
      endpoints.foreach { endpoint =>
        deliverUpdate(endpoint, update)
      }
  }

  def deliverUpdate(endpoint: String, update: String): Future[Unit] = {
    Future.successful(Unit)
  }

}

如果要处理大量实时数据或Websocket连接,处理随时间增长的CSV文件,等等,最好的选择是Akka流。例如,使用Alpakka从kafka主题中读取数据:Alpakka kafka connector