了解Akka Dispatcher

时间:2018-10-20 05:21:02

标签: scala akka

基于以下示例代码,我有几个问题。

1)我指定了

akka.cluster.use-dispatcher = cluster-dispatcher in my config. 

当我在Frontend.scala的这一行上放置一个断点时,

   _frontend = system.actorOf(Props[Frontend],
        name = "frontend")

我看到“ _frontend”对象内的默认调度程序。为什么没有从配置中获取集群调度程序?

2)我想模拟本文档所讨论的阻塞情况。 https://doc.akka.io/docs/akka/2.5/dispatchers.html#problem-blocking-on-default-dispatcher 我试图放入默认调度程序

default-dispatcher {
  fork-join-executor {
    parallelism-min = 1
    parallelism-max = 1
    throughput = 1
  }
}

我认为后端的一个“接收”将一次处理。首先,我再次调试到“ _frontend”对象,但我认为它不会读取默认值。其次,如果您有多个参与者在不同的远程进程中运行,那么所有参与者共享同一个默认调度程序意味着什么,阻塞任务可能导致线程匮乏?如果参与者在不同的进程中运行,难道他们不各自拥有自己的线程池吗?最重要的是,如果您可以给我一个示例或在下面进行修改以使我产生线程饥饿的情况,那么我可以理解它在说什么更好。谢谢您的帮助。
恩典

  akka {
      actor {
        provider = "akka.cluster.ClusterActorRefProvider"
    //    default-dispatcher {
    //      fork-join-executor {
    //        parallelism-min = 1
    //        parallelism-max = 1
    //        throughput = 1
    //      }
    //    }
      }
      remote {
        log-remote-lifecycle-events = off
        netty.tcp {
          hostname = "127.0.0.1"
          port = 0
        }
      }

      akka.cluster.use-dispatcher = cluster-dispatcher

      cluster-dispatcher {
        type = "Dispatcher"
        executor = "fork-join-executor"
        fork-join-executor {
          parallelism-min = 1
          parallelism-max = 1
        }
      }


      cluster {
        seed-nodes = [
          "akka.tcp://ClusterSystem@127.0.0.1:2551",
          "akka.tcp://ClusterSystem@127.0.0.1:2552"]

        auto-down-unreachable-after = 10s
      }
    }

    akka.cluster.min-nr-of-members = 3


    akka.cluster.role {
      frontend.min-nr-of-members = 1
      backend.min-nr-of-members = 2
    }

    akka.actor.deployment {
      /frontend/backendRouter {
        # Router type provided by metrics extension.
        router = adaptive-group
        # Router parameter specific for metrics extension.
        # metrics-selector = heap
        # metrics-selector = load
        # metrics-selector = cpu
        metrics-selector = mix
        #
        nr-of-instances = 100
        routees.paths = ["/user/backend"]
        cluster {
          enabled = on
          use-role = backend
          allow-local-routees = off
        }
      }
    }

============================

package com.packt.akka.loadBalancing

import com.packt.akka.commons.Add

object LoadBalancingApp extends App {

//initiate three nodes from backend
Backend.initiate(2551)

Backend.initiate(2552)

Backend.initiate(2561)

//initiate frontend node
Frontend.initiate()

Thread.sleep(10000)

Frontend.getFrontend ! Add(2, 4)

}

============================

package com.packt.akka.loadBalancing

import akka.cluster._
import com.packt.akka.commons._
import com.typesafe.config.ConfigFactory
import akka.cluster.ClusterEvent.MemberUp
import akka.actor.{ Actor, ActorRef, ActorSystem, Props, RootActorPath }

class Backend extends Actor {

  def receive = {
    case Add(num1, num2) =>
      println(s"I'm a backend with path: ${self} and I received add operation.")
      Thread.sleep(60000)
      println(s"I'm a backend with path: ${self} and I am done with add operation.")
  }

}

object Backend {
  def initiate(port: Int){
    val config = ConfigFactory.parseString(s"akka.remote.netty.tcp.port=$port").
      withFallback(ConfigFactory.parseString("akka.cluster.roles = [backend]")).
      withFallback(ConfigFactory.load("loadbalancer"))

    val system = ActorSystem("ClusterSystem", config)

    val Backend = system.actorOf(Props[Backend], name = "backend")

    Backend
  }
}

====================

    package com.packt.akka.loadBalancing

    import com.packt.akka.commons._
    import scala.concurrent.duration._
    import com.typesafe.config.ConfigFactory
    import akka.actor.{ Actor, ActorRef, ActorSystem, Props } 
    import akka.cluster.Cluster
    import akka.routing.FromConfig
    import akka.actor.ReceiveTimeout
    import scala.util.Random


    class Frontend extends Actor {
      import context.dispatcher

      val backend = context.actorOf(FromConfig.props(), name = "backendRouter")

      context.system.scheduler.schedule(3.seconds, 3.seconds, self,
        Add(Random.nextInt(100), Random.nextInt(100)))

      def receive = {
        case addOp: Add =>
          println("Frontend: I'll forward add operation to backend node to handle it.")
          backend forward addOp

      }

    }


object Frontend {

  private var _frontend: ActorRef = _ 

  val upToN = 200

  def initiate() = {
    val config = ConfigFactory.parseString("akka.cluster.roles = [frontend]").
      withFallback(ConfigFactory.load("loadbalancer"))

    val system = ActorSystem("ClusterSystem", config)
    system.log.info("Frontend will start when 2 backend members in the cluster.")
    //#registerOnUp
    Cluster(system) registerOnMemberUp {
      _frontend = system.actorOf(Props[Frontend],
        name = "frontend")
    }
    //#registerOnUp

  }

  def getFrontend = _frontend
}

1 个答案:

答案 0 :(得分:1)

1)参见reference.confakka.cluster.use-dispatcher = cluster-dispatcher in my config.的文档:

# The id of the dispatcher to use for cluster actors. If not specified
# default dispatcher is used.
# If specified you need to define the settings of the actual dispatcher.
use-dispatcher

此设置可让您自定义用于“内部”集群参与者而不是您自己的参与者的调度程序。

2)ForkJoinPool的parallelism-max参数不限制实际线程数。如note in the documentation中所述:

  

请注意,parallelism-max不会设置ForkJoinPool分配的线程总数的上限。此设置专门讨论池保持运行的热线程数,以减少处理新传入任务的等待时间。您可以在JDK的ForkJoinPool documentation中阅读有关并行性的更多信息。

您是正确的,当角色在不同的JVM进程中运行时,它们具有单独的调度程序。

如果您想进行实验并查看实际的线程匮乏问题,最简单的方法是创建一个在消息处理中使用阻塞调用(例如Thread.sleep)的actor。现在继续创建该参与者的许多实例,并向其发送消息。您会看到程序进度非常缓慢。

相反,如果您编写相同的actor,但使用调度程序而不是Thread.sleep来实现“延迟计算”,则应该会看到更好的性能。