写并发程序很难。程序员不得不处理线程、锁和竞态条件等等,这个过程很容易出错,而且 会导致程序代码难以阅读、测试和维护。
Akka 是 JVM 平台上构建高并发、分布式和容错应用的工具包和运行时。Akka 用 Scala 语言写成,同时提供了 Scala 和 JAVA 的开发接口。
Akka 处理并发的方法基于 Actor 模型。在基于 Actor 的系统里,所有的事物都是 Actor,就好像在面向对象设计里面所有的事物都是对象一样。但是有一个重要区别,那就是 Actor 模型是作为一个并发模型设计和架构的,而面向对象模式则不是。Actor 与 Actor 之间只能通过消息通信。
为什么 Actor 模型是一种处理并发问题的解决方案?
处理并发问题就是如何保证共享数据的一致性和正确性,为什么会有保持共享数据正确 性这个问题呢?无非是我们的程序是多线程的,多个线程对同一个数据进行修改,若不加同 步条件,势必会造成数据污染。那么我们是不是可以转换一下思维,用单线程去处理相应的 请求,但是又有人会问了,若是用单线程处理,那系统的性能又如何保证。Actor 模型的出现解决了这个问题,简化并发编程,提升程序性能。
从图中可以看到,Actor 与 Actor 之前只能用消息进行通信,当某一个 Actor 给另外一个 Actor 发消息,消息是有顺序的,只需要将消息投寄的相应的邮箱,至于对方 Actor 怎么处理你的消息你并不知道,当然你也可等待它的回复。
Actor 是 ActorSystem 创建的, ActorSystem 的职责是负责创建并管理其创建的 Actor, ActorSystem 的单例的,一个 JVM 进程中有一个即可,而 Acotr 是多例的。
Pom 依赖:
|
package com.zpark.actor import akka.actor.{Actor, ActorRef, ActorSystem, Props} //线程池,会一直运行,怎么停止?context.stop(self) class HelloActor extends Actor { //接收消息 override def receive: Receive = { //偏函数怎么用?case: case "hello" => println("hi") case "hey" => println("oooops") case "stop" => { context.stop(self) context.system.terminate() //关闭ActorySystem } } } object HelloActor { //里面有apply方法 private val lucasFactory = ActorSystem("lucasFactory")//工厂 //helloActorRef:用来发送消息的 private val helloActorRef: ActorRef = lucasFactory.actorOf(Props[HelloActor]) def main(args: Array[String]): Unit = { helloActorRef ! "hello" //! 的作用是 发送 helloActorRef ! "hey" helloActorRef ! "stop" } }
LucasActor
package com.zpark.actor import akka.actor.{Actor, ActorRef} class LucasActor(val jk: ActorRef) extends Actor{ override def receive: Receive = { case "start" => { println("lucas:i'm ready") jk ! "pa" } case "papa" => { println("ok") Thread.sleep(1000) jk ! "pa" } } }
JackActory
package com.zpark.actor import akka.actor.Actor class JackActor extends Actor{ override def receive: Receive = { case "start" => println("jack:i'm ready") case "pa" => { println("jack: go") Thread.sleep(1000) sender() ! "papa" //发给case "pa"的发送者 } } }
Main
package com.zpark.actor import akka.actor.{ActorSystem, Props} object PingpangApp { def main(args: Array[String]): Unit = { //通过ActorSystem创建ActorRef val pingPangActorSystem = ActorSystem("PingPangActorSystem") val jfActorRef = pingPangActorSystem.actorOf(Props[JackActor], "jf") val lfActorRef = pingPangActorSystem.actorOf(Props(new LucasActor(jfActorRef)), "lf") lfActorRef ! "start" jfActorRef ! "start" } }
package com.zpark.robot import akka.actor.{Actor, ActorRef, ActorSystem, Props} import com.typesafe.config.ConfigFactory /** * 服务端 */ class ZparkServer extends Actor{ override def receive: Receive = { case "start" => println("就绪....") case ClientMessage(msg) => { println(s"收到客户端消息$msg") msg match { case "你叫啥" => sender() ! ServerMessage("我叫机器人") case "你是男是女" => sender() ! ServerMessage("男") case "你有男票吗" => sender() ! ServerMessage("没有") case _ => sender() ! ServerMessage("不知道") } } } } /** * 服务端启动入口 */ object ZparkServer extends App { val host: String = "127.0.0.1" val port: Int = 8878 val conf = ConfigFactory.parseString( s""" |akka.actor.provider = "akka.remote.RemoteActorRefProvider" |akka.remote.netty.tcp.hostname = $host |akka.remote.netty.tcp.port = $port """.stripMargin ) //启动的时候指定ip地址和端口 private val actorSystem = ActorSystem("Server",conf) private val serverActorRef: ActorRef = actorSystem.actorOf(Props[ZparkServer],"xiaohua") serverActorRef ! "start" }
package com.zpark.robot import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props} import com.typesafe.config.ConfigFactory import scala.io.StdIn /** * 客户端 */ class ClientActor extends Actor{ var serverActorRef: ActorSelection = _ override def preStart(): Unit = { serverActorRef = context.actorSelection("akka.tcp://[email protected]:8878/user/xiaohua") } override def receive: Receive = { case "start" => "client01 已启动" case msg: String => { serverActorRef ! ClientMessage(msg) } case ServerMessage(msg) => println(s"收到服务端消$msg") } } /** * 客户端启动入口 */ object ClientActor extends App { val host: String = "127.0.0.1" val port = 8880 val conf = ConfigFactory.parseString( s""" |akka.actor.provider = "akka.remote.RemoteActorRefProvider" |akka.remote.netty.tcp.hostname = $host |akka.remote.netty.tcp.port = $port """.stripMargin ) private val clientSystem = ActorSystem("client",conf) private val client01ActorRef: ActorRef = clientSystem.actorOf(Props[ClientActor],"client02") client01ActorRef ! "start" while (true) { val question = StdIn.readLine() client01ActorRef ! question } }
package com.zpark.robot //服务端发送给客户段的消息 case class ServerMessage(msg: String) //客户端发送给服务器端的消息格式 case class ClientMessage(msg: String)
参数:
akka.actor.provider = "akka.remote.RemoteActorRefProvider"
akka.remote.netty.tcp.hostname = $host
akka.remote.netty.tcp.port = $port
端!