Akka(9): 分布式运算:Remoting-远程构建式

   上篇我们讨论了Akka-Remoting。我们说Akka-Remoting是一种点对点的通讯方式,能使两个不同JVM上Akka-ActorSystem上的两个Actor之间可以相互沟通。Akka-Remoting还没有实现完全的Actor位置透明(location transparency),因为一个Actor还必须在获得对方Actor确切地址信息后才能启动与之沟通过程。Akka-Remoting支持“远程查找”和“远程构建”两种沟通方式。由于篇幅所限,我们只介绍了“远程查找”。在这一篇里我们将会讨论“远程构建”方式。

同样,我们先通过项目结构来分析:

lazy val local = (project in file("."))
  .settings(commonSettings)
  .settings(
    name := "remoteCreateDemo"
  ).aggregate(calculator,remote).dependsOn(calculator)

lazy val calculator = (project in file("calculator"))
  .settings(commonSettings)
  .settings(
    name := "calculator"
  )

lazy val remote = (project in file("remote"))
  .settings(commonSettings)
  .settings(
    name := "remoteSystem"
  ).aggregate(calculator).dependsOn(calculator)

远程构建的过程大致是这样的:由local通知remote启动构建Actor;remote从本地库中查找Actor的类定义(class)并把它载入内存。由于驱动、使用远程Actor是在local进行的,所以local,remote项目还必须共享Calculator,包括Calculator的功能消息。这项要求我们在.sbt中用aggregate(calculator)来协同编译。

我们把Calculator的监管supervisor也包括在这个源码文件里。现在这个calculator是个包括监管、功能、消息的完整项目了。Calculator源代码如下:

package remoteCreation.calculator

import akka.actor._
import scala.concurrent.duration._

object Calcultor {
  sealed trait MathOps
  case class Num(dnum: Double) extends MathOps
  case class Add(dnum: Double) extends MathOps
  case class Sub(dnum: Double) extends MathOps
  case class Mul(dnum: Double) extends MathOps
  case class Div(dnum: Double) extends MathOps

  sealed trait CalcOps
  case object Clear extends CalcOps
  case object GetResult extends CalcOps

  def props = Props(new Calcultor)
  def supervisorProps = Props(new SupervisorActor)
}

class Calcultor extends Actor with ActorLogging {
  import Calcultor._

  var result: Double = 0.0   //internal state

  override def receive: Receive = {
    case Num(d) => result = d
    case Add(d) => result += d
    case Sub(d) => result -= d
    case Mul(d) => result *= d
    case Div(d) =>
      val _ = result.toInt / d.toInt   //yield ArithmeticException
      result /= d
    case Clear => result = 0.0
    case GetResult =>
      sender() ! s"Result of calculation is: $result"
  }

  override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
    log.info(s"Restarting calculator: ${reason.getMessage}")
    super.preRestart(reason, message)
  }
}

class SupervisorActor extends Actor {
  def decider: PartialFunction[Throwable,SupervisorStrategy.Directive] = {
    case _: ArithmeticException => SupervisorStrategy.Resume
  }

  override def supervisorStrategy: SupervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 5, withinTimeRange = 5 seconds){
      decider.orElse(SupervisorStrategy.defaultDecider)
    }

  val calcActor = context.actorOf(Calcultor.props,"calculator")

  override def receive: Receive = {
    case msg@ _ => calcActor.forward(msg)
  }

}

与上一个例子的”远程查找式“相同,remote需要为Remoting公开一个端口。我们可以照搬.conf配置文件内容:remote/src/main/resources/application.conf

akka {
  actor {
    provider = remote
  }
  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "127.0.0.1"
      port = 2552
    }
    log-sent-messages = on
    log-received-messages = on
  }
}

由于远程构建和使用是在local上进行的,在remote上我们只需要启动ActorSystem就行了:

import com.typesafe.config.ConfigFactory
import akka.actor._

object CalculatorRunner extends App {
  val remoteSystem = ActorSystem("remoteSystem",ConfigFactory.load("application"))
  println("Remote system started.")

  scala.io.StdIn.readLine()
  remoteSystem.terminate()

}

Calculator的构建是在localSystem上启动的,我们需要在配置文件中描述远程构建标的(还是未能实现位置透明):local/src/main/resources/application.conf 

akka {
  actor {
    provider = remote,
    deployment {
      "/calculator" {
        remote = "akka.tcp://remoteSystem@127.0.0.1:2552"
      }
    }
  }
  remote {
    netty.tcp {
      hostname = "127.0.0.1",
      port=2554
    }
  }
}

注意:上面这个/calculator设置实际上指的是SupervisorActor。

现在我们可以在local上开始构建calculator,然后使用它来运算了:

import akka.actor._
import remoteCreation.calculator.Calcultor._
import scala.concurrent.duration._
import akka.pattern._

object RemotingCreate extends App {
  val localSystem = ActorSystem("localSystem")
  val calcActor = localSystem.actorOf(props,
    name = "calculator")   //created SupervisorActor

  import localSystem.dispatcher

  calcActor ! Clear
  calcActor ! Num(13.0)
  calcActor ! Mul(1.5)

  implicit val timeout = akka.util.Timeout(1 second)

  ((calcActor ? GetResult).mapTo[String]) foreach println
  scala.io.StdIn.readLine()

  calcActor ! Div(0.0)
  calcActor ! Div(1.5)
  calcActor ! Add(100.0)
  ((calcActor ? GetResult).mapTo[String]) foreach println

  scala.io.StdIn.readLine()
  localSystem.terminate()

}

从代码上看构建calculator(SupervisorActor)过程与普通的Actor构建没分别,所有细节都放在配置文件里了。但是,要注意actorOf的name必须与配置文档中的设置匹配。

试运行结果与上一个例子相同。值得注意的是实际远程构建的是一个SupervisorActor。Calculator的构建是SupervisorActor构建的其中一部分。从运算结果看:这个SupervisorActor也实现了它的功能。

下面是这次示范的源代码:

local/build.sbt

azy val commonSettings = seq (
  name := "RemoteCreateDemo",
  version := "1.0",
  scalaVersion := "2.11.8",
  libraryDependencies := Seq(
    "com.typesafe.akka" %% "akka-actor" % "2.5.2",
    "com.typesafe.akka" %% "akka-remote" % "2.5.2"
  )
)


lazy val local = (project in file("."))
  .settings(commonSettings)
  .settings(
    name := "remoteCreateDemo"
  ).aggregate(calculator).dependsOn(calculator)

lazy val calculator = (project in file("calculator"))
  .settings(commonSettings)
  .settings(
    name := "calculator"
  )

lazy val remote = (project in file("remote"))
  .settings(commonSettings)
  .settings(
    name := "remoteSystem"
  ).aggregate(calculator).dependsOn(calculator)

calculator/calculator.scala

package remoteCreation.calculator

import akka.actor._
import scala.concurrent.duration._

object Calcultor {
  sealed trait MathOps
  case class Num(dnum: Double) extends MathOps
  case class Add(dnum: Double) extends MathOps
  case class Sub(dnum: Double) extends MathOps
  case class Mul(dnum: Double) extends MathOps
  case class Div(dnum: Double) extends MathOps

  sealed trait CalcOps
  case object Clear extends CalcOps
  case object GetResult extends CalcOps

  def props = Props(new Calcultor)
  def supervisorProps = Props(new SupervisorActor)
}

class Calcultor extends Actor with ActorLogging {
  import Calcultor._

  var result: Double = 0.0   //internal state

  override def receive: Receive = {
    case Num(d) => result = d
    case Add(d) => result += d
    case Sub(d) => result -= d
    case Mul(d) => result *= d
    case Div(d) =>
      val _ = result.toInt / d.toInt   //yield ArithmeticException
      result /= d
    case Clear => result = 0.0
    case GetResult =>
      sender() ! s"Result of calculation is: $result"
  }

  override def preRestart(reason: Throwable, message: Option[Any]): Unit = {
    log.info(s"Restarting calculator: ${reason.getMessage}")
    super.preRestart(reason, message)
  }
}

class SupervisorActor extends Actor {
  def decider: PartialFunction[Throwable,SupervisorStrategy.Directive] = {
    case _: ArithmeticException => SupervisorStrategy.Resume
  }

  override def supervisorStrategy: SupervisorStrategy =
    OneForOneStrategy(maxNrOfRetries = 5, withinTimeRange = 5 seconds){
      decider.orElse(SupervisorStrategy.defaultDecider)
    }

  val calcActor = context.actorOf(Calcultor.props,"calculator")

  override def receive: Receive = {
    case msg@ _ => calcActor.forward(msg)
  }

}

remote/src/main/resources/application.conf

akka {
  actor {
    provider = remote
  }
  remote {
    enabled-transports = ["akka.remote.netty.tcp"]
    netty.tcp {
      hostname = "127.0.0.1"
      port = 2552
    }
    log-sent-messages = on
    log-received-messages = on
  }
}

remote/CalculatorRunner.scala

package remoteCreation.remote
import com.typesafe.config.ConfigFactory
import akka.actor._

object CalculatorRunner extends App {
  val remoteSystem = ActorSystem("remoteSystem",ConfigFactory.load("application"))
  println("Remote system started.")

  scala.io.StdIn.readLine()
  remoteSystem.terminate()

}

local/src/main/resources/application.conf

akka {
  actor {
    provider = remote,
    deployment {
      "/calculator" {
        remote = "akka.tcp://remoteSystem@127.0.0.1:2552"
      }
    }
  }
  remote {
    netty.tcp {
      hostname = "127.0.0.1",
      port=2554
    }
  }
}

local/RemotingCreation.scala

import akka.actor._
import remoteCreation.calculator.Calcultor._
import scala.concurrent.duration._
import akka.pattern._

object RemotingCreate extends App {
  val localSystem = ActorSystem("localSystem")
  val calcActor = localSystem.actorOf(props,
    name = "calculator")  //created SupervisorActor

  import localSystem.dispatcher

  calcActor ! Clear
  calcActor ! Num(13.0)
  calcActor ! Mul(1.5)

  implicit val timeout = akka.util.Timeout(1 second)

  ((calcActor ? GetResult).mapTo[String]) foreach println
  scala.io.StdIn.readLine()

  calcActor ! Div(0.0)
  calcActor ! Div(1.5)
  calcActor ! Add(100.0)
  ((calcActor ? GetResult).mapTo[String]) foreach println

  scala.io.StdIn.readLine()
  localSystem.terminate()

}

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏GIS讲堂

excel中提取中文拼音

概述:在工作时,有时候会用到汉语拼音,本文讲述如何在Excel中通过vba程序提取汉字的拼音。

893
来自专栏Kubernetes

runC源码分析——namespace

runc/libcontainer/configs/config.go中定义了container对应的Namespaces。另外对于User Namespace...

2918
来自专栏JackieZheng

Spring读书笔记——bean加载

我们的日常开发几乎离不开Spring,他为我们的开发带来了很大的便捷,那么Spring框架是如何做到方便他人的呢。今天就来说说bean如何被加载加载。 我们在x...

2039
来自专栏逸鹏说道

我这么玩Web Api(二)

数据验证,全局数据验证与单元测试 目录 一、模型状态 - ModelState 二、数据注解 - Data Annotations 三、自定义数据注解 四、全局...

4466
来自专栏函数式编程语言及工具

Akka(13): 分布式运算:Cluster-Sharding-运算的集群分片

    通过上篇关于Cluster-Singleton的介绍,我们了解了Akka为分布式程序提供的编程支持:基于消息驱动的运算模式特别适合分布式程序编程,我们不...

3688
来自专栏飞扬的花生

在ASP.MVC中使用Ajax

      Asp.net MVC 抛弃了Asp.net WebForm那种高度封装的控件,让我们跟底层的HTML有了更多的亲近。可以更自由、更灵活的去控制HT...

1929
来自专栏Android 开发学习

JsBridge 源码分析

1303
来自专栏chenssy

【追光者系列】HikariCP源码分析之evict、时钟回拨、连接创建生命周期

evict定义在com.zaxxer.hikari.pool.PoolEntry中,evict的汉语意思是驱逐、逐出,用来标记连接池中的连接不可用。

2544
来自专栏恰同学骚年

ASP.Net请求处理机制初步探索之旅 - Part 5 ASP.Net MVC请求处理流程

开篇:上一篇我们了解了在WebForm模式下一个Page页面的生命周期,它经历了初始化Init、加载Load以及呈现Render三个重要阶段,其中构造了页面控件...

1113
来自专栏大前端_Web

easyUI组件datagrid的二次封装

版权声明:本文为吴孔云博客原创文章,转载请注明出处并带上链接,谢谢。 https://blog.csdn.net/wkyseo/articl...

1213

扫码关注云+社区