Most correct way work with IO/blocking operations in Akka

时间:2016-12-22 19:01:25

标签: akka

I have a question about a correct way work with IO blocking operations, i read that the right model it's wrap blocking actions into Future and work with this object instead of call blocking operations inside actor.

But i see this have as minimum two solutions

case class AccountBalance(id: Int, userId: Int, total: Int)

object AccountBalance {
  def getUserAccountBalance(userId: Int)(
      implicit ec: ExecutionContext): Future[AccountBalance] = {
    Future {
      AccountBalance(1, userId, 1)
    }
  }

  def updateAccountBalance(id: Int, total: Int)(implicit ec:   ExecutionContext): Future[AccountBalance] = {
    Future {
      AccountBalance(id, 1, total)
    }
  }

  // Main logic 
  def getAndInc(userId: Int)(implicit ec: ExecutionContext) = {
    getUserAccountBalance(userId).flatMap { balance =>
    updateAccountBalance(balance.id, balance.total + 1)
  }
 }
}

In first approach i use AccountBalanece.getAndInc method inside actor:

class Approach1 extends Actor {
  implicit val executionContext = context.dispatcher

  def receive = {
    case Calculate(userId) =>
      AccountBalance.getAndInc(userId) pipeTo sender
  }
}

For another solution (more comfortable for me)

class Approach2 extends Actor {
  implicit val executionContext = context.dispatcher

  var firstSender: ActorRef = null
  var userId: Int = -1

  def receive = {
    case Calculate(givenUserId) =>
      userId = givenUserId
      firstSender = sender

      AccountBalance.getUserAccountBalance(userId) pipeTo self

    case AccountBalance(id, _, total) =>
      AccountBalance.updateAccountBalance(id, total + 1) pipeTo    firstSender

  }
} 

What solution is better (or both dangerous and not usable)?

And second question about ExecutionContext, for examples i use context.dispatcher for real application i use the next:

class A extends Actor {

  implicit val executionContext = ExecutionContext.fromExecutorService(Executors.newFixedThreadPool(10)) 

  override def postStop = {
     executionContext.shutdown()
  }

  def receive = { case _ => }

}

If i use executionContext.shutdown after actor is stopped, this is correct way to shutdown thread pool and free all resources?

1 个答案:

答案 0 :(得分:0)

因为需要反应式编程定义以确保不阻止执行线程。因此,任何阻塞操作都应该由不同的执行上下文来处理,以便不阻止调度actor命令/消息的线程。我使用我演员正在使用的第二种模式"成为"并转到不同的行为,等待阻止操作的响应。希望不处理任何新请求并丢失原始发送者actorRef。如果您仍希望演员接收其他消息并发送,则需要在原始发件人和收到的回复之间建立链接。

是的,以这种方式停止,会起作用,但我建议在创建线程池时要非常小心,并在阻塞actor之间共享它们。如果关闭引发异常并且您只想关闭actor A而不是所有akka系统,请注意主管A不会被主管重新启动。