Scala语法(六) Akka与线程通信

前言

在初期, Scala可以通过Akka来实现线程通信. 当然, 现在还支持使用Netty方式进行通信.

本章主要介绍使用Akka方式进行通信的写法.


正文

  • Master结点

import akka.actor.Actor
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import akka.actor.Props

class AkkaMaster extends Actor{
  // start 之前
  override def preStart() : Unit = {
    println("pre master invoke.")
  }
  // 用于接收消息
  override def receive:Receive = {
    case "connect" => {
        println("a client connected.")
        sender ! "reply"
      }
    case "hello" => {println("hello")}
  }
}
object AkkaMaster{
  def main(args: Array[String]): Unit = {
    // 使用创建ActorSystem来创建和监控下面的Actor对象. 单例的.
      val host = "127.0.0.1"
      val port = 8090
      // 准备配置
      val configStr = 
      s"""
          |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
          |akka.remote.netty.tcp.hostname = "$host"
          |akka.remote.netty.tcp.port = "$port"
      """.stripMargin
    val config = ConfigFactory.parseString(configStr)
    // 注意名称中间不要加空格
    val actorSysetm = ActorSystem("MasterSystem",config) 
    // 创建Actor
    val master = actorSysetm.actorOf(Props(new AkkaMaster),"Master")
    master ! "hello"
    // 等待信号停止
    actorSysetm.awaitTermination()
  }
}


// 顺利输出
//[INFO] [04/29/2019 16:43:20.512] [main] [Remoting] Starting remoting
//[INFO] [04/29/2019 16:43:20.770] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://MasterSystem@127.0.0.1:8090]
//[INFO] [04/29/2019 16:43:20.771] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://MasterSystem@127.0.0.1:8090]
//pre master invoke.
//hello

// 1. 名称中间不要加空格
//Exception in thread "main" java.lang.IllegalArgumentException: invalid ActorSystem name [Master System], must contain only word characters (i.e. [a-zA-Z0-9] plus non-leading '-' or '_')
//	at akka.actor.ActorSystemImpl.<init>(ActorSystem.scala:498)
//	at akka.actor.ActorSystem$.apply(ActorSystem.scala:142)
//	at akka.actor.ActorSystem$.apply(ActorSystem.scala:119)
//	at com.yanxml.quick_scala.multi.akka.AkkaMaster$.main(AkkaMaster.scala:33)
//	at com.yanxml.quick_scala.multi.akka.AkkaMaster.main(AkkaMaster.scala)
  • Worker结点
import akka.actor.Actor
import akka.actor.ActorSelection
import akka.actor.ActorSystem
import akka.actor.Props
import com.typesafe.config.ConfigFactory

class AkkaWorker extends Actor{
  // 成员变量
  val master:ActorSelection = null
  // 建立链接
  override def preStart():Unit = {
    val master = context.actorSelection("akka.tcp://MasterSystem@127.0.0.1:8090/user/Master")
    println(master)
    master ! "connect"
  }
  
  override def receive:Receive = {
    case "reply" => {
      println("a reply from master")
    }
  }
}

object AkkaWorker{
   def main(args: Array[String]): Unit = {
     // 使用创建ActorSystem来创建和监控下面的Actor对象. 单例的.
      val host = "127.0.0.1"
      val port = 8091
      // 准备配置
      val configStr = 
      s"""
          |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
          |akka.remote.netty.tcp.hostname = "$host"
          |akka.remote.netty.tcp.port = "$port"
      """.stripMargin
    val config = ConfigFactory.parseString(configStr)
    // 注意名称中间不要加空格
    val actorSysetm = ActorSystem("WorkerSystem",config) 
    // 创建Actor
    val master = actorSysetm.actorOf(Props(new AkkaWorker),"Worker")
    master ! "hello"
    // 等待信号停止
    actorSysetm.awaitTermination()
   }
}

模拟RPC

在模拟RPC中主要有这样的流程.

其中主要包括两个结点: Worker结点&Master结点.

  • 运行流程:
    • Master结点先进行启动.
    • Worker结点后进行启动.
    • Worker结点Master结点发送注册消息.
    • Matser结点接收注册消息, 并进行记录. 并将主结点的地址返回给Worker结点(模拟Master是集群的情况).并记录,最后的通信时间作为心跳标志.
    • Worker结点接收主结点地址, 并形成通信链接. 开始通信. 并定时发送心跳消息.

改造上方的Demo代码. 其基本代码如下所示:

  • RemoteMessage

trait RemoteMessage  extends Serializable{
  
}

// Worker -> Master 用来封装Worker信息 
case class RegisterWorker(id:String,memory:Int,cores:Int)

class WorkerInfo(val id:String, val memory:Int, val cores:Int){
  // 上一次心跳
  var heartbeatTime:String = _
}
  • Master

import akka.actor.Actor
import akka.actor.ActorSystem
import com.typesafe.config.ConfigFactory
import akka.actor.Props
import scala.collection.immutable.HashMap

private [simulate] class AkkaMaster extends Actor{
  val idToWorker = new scala.collection.mutable.HashMap[String,WorkerInfo]()
  // start 之前
  override def preStart() : Unit = {
    println("pre master invoke.")
  }
  // 用于接收消息
  override def receive:Receive = {
    case "connect" => {
        println("a client connected.")
        sender ! "reply"
      }
    case "hello" => {println("hello")}
    // 传输样例类
    case RegisterWorker(id,memory,cores)=>{
      if(!idToWorker.contains(id)){
        idToWorker.put(id, new WorkerInfo(id,memory,cores))
      }
      sender ! "123"
    }
  }
}
object AkkaMaster{
  def main(args: Array[String]): Unit = {
    // 使用创建ActorSystem来创建和监控下面的Actor对象. 单例的.
      val host = "127.0.0.1"
      val port = 8090
      // 准备配置
      val configStr = 
      s"""
          |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
          |akka.remote.netty.tcp.hostname = "$host"
          |akka.remote.netty.tcp.port = "$port"
      """.stripMargin
    val config = ConfigFactory.parseString(configStr)
    // 注意名称中间不要加空格
    val actorSysetm = ActorSystem("MasterSystem",config) 
    // 创建Actor
    val master = actorSysetm.actorOf(Props(new AkkaMaster),"Master")
    master ! "hello"
    // 等待信号停止
    actorSysetm.awaitTermination()
  }
}


// 顺利输出
//[INFO] [04/29/2019 16:43:20.512] [main] [Remoting] Starting remoting
//[INFO] [04/29/2019 16:43:20.770] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://MasterSystem@127.0.0.1:8090]
//[INFO] [04/29/2019 16:43:20.771] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://MasterSystem@127.0.0.1:8090]
//pre master invoke.
//hello

// 1. 名称中间不要加空格
//Exception in thread "main" java.lang.IllegalArgumentException: invalid ActorSystem name [Master System], must contain only word characters (i.e. [a-zA-Z0-9] plus non-leading '-' or '_')
//	at akka.actor.ActorSystemImpl.<init>(ActorSystem.scala:498)
//	at akka.actor.ActorSystem$.apply(ActorSystem.scala:142)
//	at akka.actor.ActorSystem$.apply(ActorSystem.scala:119)
//	at com.yanxml.quick_scala.multi.akka.AkkaMaster$.main(AkkaMaster.scala:33)
//	at com.yanxml.quick_scala.multi.akka.AkkaMaster.main(AkkaMaster.scala)

  • Worker

import akka.actor.Actor
import akka.actor.ActorSelection
import akka.actor.ActorSystem
import akka.actor.Props
import com.typesafe.config.ConfigFactory
import com.yanxml.quick_scala.multi.akka.simulate.RegisterWorker
import java.util.UUID

private [simulate] class  AkkaWorker(val masterHost:String, val masterPort:String, val memory:Int, val cores:Int) extends Actor{
  // 成员变量
  val master:ActorSelection = null
  // 建立链接
  override def preStart():Unit = {
    // 和Master建立链接
    val master = context.actorSelection(s"akka.tcp://MasterSystem@$masterHost:$masterPort/user/Master")
    val workerId = UUID.randomUUID().toString()
    //println(master)
    // 向Master发送消息
    master ! RegisterWorker(workerId,memory,cores)
  }
  
  override def receive:Receive = {
    case "reply" => {
      println("a reply from master")
    }
  }
}

object AkkaWorker{
   def main(args: Array[String]): Unit = {
     // 使用创建ActorSystem来创建和监控下面的Actor对象. 单例的.
      val host = "127.0.0.1"
      val port = 8091
      // 准备配置
      val configStr = 
      s"""
          |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
          |akka.remote.netty.tcp.hostname = "$host"
          |akka.remote.netty.tcp.port = "$port"
      """.stripMargin
    val config = ConfigFactory.parseString(configStr)
    // 注意名称中间不要加空格
    val actorSysetm = ActorSystem("WorkerSystem",config) 
    // 创建Actor
    val master = actorSysetm.actorOf(Props(new AkkaWorker("127.0.0.1","8090",2,2)),"Worker")
    master ! "hello"
    // 等待信号停止
    actorSysetm.awaitTermination()
   }
}

注: 后续的通信逻辑就是丰富双方的receive()方法.

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值