我有一个方法,其中有多个db调用。由于我没有实现任何并发处理,第二个db调用必须等到第一个db调用完成,第三个必须等到第二个db调用完成,依此类推。
所有数据库调用都是相互独立的。我希望以所有数据库调用同时运行的方式进行此操作。
我是Akka框架的新手。
有人可以帮我提供小样本或参考资料会有所帮助。应用程序是在Scala Lang开发的。
答案 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