Scala(8)Actor编程、AkkaRPC案例

Scala在2.11.x版本中将Akka加入其中,作为其默认的Actor,老版本的Actor已经废弃

一、什么是 Actor

  1. 概念
    Scala中的Actor能够实现并行编程的强大功能,它是基于事件模型的并发机制,Scala是运用消息(message)的发送、接收来实现多线程的。

  2. actor 方法执行顺序
    首先调用start()方法启动Actor
    调用start()方法后其act()方法会被执行
    向Actor发送消息

  3. 发送消息的方式
    方法 作用
    ! 发送异步消息,没有返回值。
    !? 发送同步消息,等待返回值。
    !! 发送异步消息,返回值是 Future[Any]。

二、 实战

  1. 不断的接收消息
package cn.itcast.actor

import scala.actors.Actor

/**
  * Created by ZX on 2016/4/4.
  */
class MyActor extends Actor {

  override def act(): Unit = {
    while (true) {
      receive {
        case "start" => {
          println("starting ...")
          Thread.sleep(5000)
          println("started")
        }
        case "stop" => {
          println("stopping ...")
          Thread.sleep(5000)
          println("stopped ...")
        }
      }
    }
  }
}

object MyActor {
  def main(args: Array[String]) {
    val actor = new MyActor
    actor.start()
    actor ! "start"
    actor ! "stop"
    println("消息发送完成!")
  }
}

说明:在act()方法中加入了while (true) 循环,就可以不停的接收消息

注意:发送start消息和stop的消息是异步的,但是Actor接收到消息执行的过程是同步的按顺序执行

  1. react方式会复用线程,比receive更高效
package cn.itcast.actor

import scala.actors.Actor

/**
  * Created by ZX on 2016/4/4.
  */
class YourActor extends Actor {

  override def act(): Unit = {
    loop {
      react {
        case "start" => {
          println("starting ...")
          Thread.sleep(5000)
          println("started")
        }
        case "stop" => {
          println("stopping ...")
          Thread.sleep(8000)
          println("stopped ...")
        }
      }
    }
  }
}


object YourActor {
  def main(args: Array[String]) {
    val actor = new YourActor
    actor.start()
    actor ! "start"
    actor ! "stop"
    println("消息发送完成!")
  }
}

说明: react 如果要反复执行消息处理,react外层要用loop,不能用while

  1. 结合case class发送消息
package cn.itcast.actor
import scala.actors.Actor

class AppleActor extends Actor {

  def act(): Unit = {
    while (true) {
      receive {
        case "start" => println("starting ...")
        case SyncMsg(id, msg) => {
          println(id + ",sync " + msg)
          Thread.sleep(5000)
          sender ! ReplyMsg(3,"finished")
        }
        case AsyncMsg(id, msg) => {
          println(id + ",async " + msg)
          Thread.sleep(5000)
        }
      }
    }
  }
}

object AppleActor {
  def main(args: Array[String]) {
    val a = new AppleActor
    a.start()
    //异步消息
    a ! AsyncMsg(1, "hello actor")
    println("异步消息发送完成")
    //同步消息
    //val content = a.!?(1000, SyncMsg(2, "hello actor"))
    //println(content)
    val reply = a !! SyncMsg(2, "hello actor")
    println(reply.isSet)
    //println("123")
    val c = reply.apply()
    println(reply.isSet)
    println(c)
  }
}
case class SyncMsg(id : Int, msg: String)
case class AsyncMsg(id : Int, msg: String)
case class ReplyMsg(id : Int, msg: String)

三、综合案例,Actor 实现 WordCount

package cn.itcast.scala.cn.itcast.scala.actor

import scala.actors.{Actor, Future}
import scala.collection.mutable.{HashSet, ListBuffer}
import scala.io.Source

object ActorWordCount {
  def main(args: Array[String]): Unit = {
    val replySet = new HashSet[Future[Any]]
    val resultSet = new ListBuffer[ResultTask]
    val taskList = new ListBuffer[WordCountTask]
    val files = List("F:/words1.txt","F:/words2.txt")
    for (f <- files) {
      val task = new WordCountTask
      val future = task.start() !! SubmitTask(f)
      replySet += future
      taskList += task
    }
    while (replySet.size > 0) {
      val toComplete = replySet.filter(_.isSet)
      for (f <- toComplete) {
        val result = f().asInstanceOf[ResultTask]
        resultSet += result
        replySet -= f
      }
    }
    val count = resultSet.flatMap(_.result).groupBy(_._1).mapValues(_.foldLeft(0)(_ + _._2))
    println(count)
    taskList.foreach(_ ! Stop)

  }
}

class WordCountTask extends Actor {
  override def act(): Unit = {
    loop {
      react {
        case SubmitTask(path) =>
          val result: Map[String, Int] = Source.fromFile(path).getLines().flatMap(_.split(" ")).map((_, 1)).toList.groupBy(_._1).mapValues(_.size)
          sender ! ResultTask(result)
        case Stop => exit()
      }
    }
  }
}

case class SubmitTask(path: String)
case class ResultTask(result: Map[String, Int])
case object Stop

什么是Actors
Akka Actors遵循Actor模型
我们可以吧Actors当做一个人,这个人不会自己和其他人直接说话,他们只通过mail来进行交流。
现在来探讨Actors的一些特性;

一、消息传递
假设有两个人:学生和聪明的老师。学生每天早上都会给老师发送邮件,而聪明的老师都会回复一句名言。这里需要解释:
1.学生发送邮件。一旦发送成功,邮件不能再修改。这天然就具备了不可变性;
2.老师会自己决定何时检查邮箱;
3.老师还会回复一封邮件(也是不可变的);
4.学生会自己决定何时检查邮箱;
5.学生不会一直等待回信(非阻塞的)
这就可以总结出Actor模型的一个基本特征---------消息传递

二、并发
现在,假设有三个聪明的老师和学生。每个学生都会给每个老师发送邮件。这会发生什么事情?
其实什么都没改变,每个人都有他自己的邮箱。这里需要注意的一点:默认情况下,邮箱里面的邮件是按照他们先后达到的次序进行阅读和处理的。
本质上,这很像是ConcurrentLinkedQueue。没有人去等待邮件被阅读,简单来说这就是一个非阻塞的消息(在Akka中内置了许多的mailboxes)
在这里插入图片描述
1.Actor就是通过发生消息实现并发
2.一个人就相当于一个Actor
3.Actor和Actor直接是可以发送消息的,自己也可以给自己发生消息
4.消息是有类型的,里面封装的数据(case class)
5.Actor是有生命周期的,是由ActorSystem创建的,ActorSystem负责管理和监控Actors,ActorSystem是单例的(object),Actor是多例的
6.消息的接收者可以将消息返回给消息的发送者

package cn._51doit.akka.rpc

import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory

class Master extends Actor{

  //是Actor接收消息的方法
  override def receive: Receive = {

    case "connect" => {
      println("a client connected!")
      //服务端接收到消息后,向worker返回消息
      sender() ! "response"
    }

    case "hello" => {
      println("client send a hello")
    }
  }
}

object Master {

  def main(args: Array[String]): Unit = {

    val host = "localhost"
    val port = 8888

    val config = ConfigFactory.parseString(
      s"""
        |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
        |akka.remote.netty.tcp.hostname = $host
        |akka.remote.netty.tcp.port = $port
        """.stripMargin
    )
    //创建一个ActorSystem(在一个进程中只要有一个即可,是单例的)
    val masterActorSystem = ActorSystem("MasterActorSystem", config)
    //创建Actor
    val masterActor = masterActorSystem.actorOf(Props[Master], "MasterActor")
    //发消息
    //masterActor ! "connect"

  }


}
package cn._51doit.akka.rpc

import akka.actor.{Actor, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory

class Worker extends Actor {

  var materActorRef: ActorSelection = _

  //Worker生命周期方法
  //在构造方法之后,receive方法之前,执行一次
  override def preStart(): Unit = {
    //通过网络先跟Master建立连接,选择跟谁建立连接
    materActorRef = context.actorSelection("akka.tcp://MasterActorSystem@localhost:8888/user/MasterActor")
    //Worker向Master发送的消息
    materActorRef ! "connect"
  }

  override def receive: Receive = {

    case "response" => {
      println("a response msg from master")
    }

  }
}

object Worker {

  def main(args: Array[String]): Unit = {

    val host = "localhost"
    val port = 9999

    val config = ConfigFactory.parseString(
      s"""
        |akka.actor.provider = "akka.remote.RemoteActorRefProvider"
        |akka.remote.netty.tcp.hostname = $host
        |akka.remote.netty.tcp.port = $port
        |""".stripMargin
    )
    val workerActorSystem = ActorSystem("WorkerActorSystem", config)

    workerActorSystem.actorOf(Props[Worker], "WorkerActor")
  }


}

worker向master发送注册信息

代码待完善

Master保存worker的注册信息

Worker向Master定期发送心跳

Master定期检查超时的Worker

程序完善和打成Jar包

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值