1.Akka介绍
Akka是JVM平台上构建高并发,分布式应用的编程框架。它采用scala语言编写,同时提供了scala和java开发接口。Akka主要解决的问题是:可以轻松的写出高效稳定的并发程序,程序员不需要过多的考虑线程,锁和资源竞争等细节。
2.Akka Actor模型
Akka框架采用Actor模型,如下图所示。
Actor与Actor之间用消息进行通信,当某一个Actor给另外一个Actor发消息,消息是有序的,当消息投寄到相应的邮箱后,对方Actor怎么处理是异步的,当然也可以等待它的回复。
JVM中的Actor有以下几个特点:
-
每个Actor都有对应一个邮箱
-
Actor是串行处理消息的
-
Actor中的消息是不可变的
优势:
- 简化并发编程:每个Actor都是相互独立的,因此没有共享资源,这也就避免了并发编程中锁的问题。
3.案例
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
class SelfActor extends Actor{
override def receive: Receive = {
case "hello" => { println("收到hello,回应 hello too") }
case "ok" => { println("收到ok,回应 ok too") }
case "exit" => { context.stop(self)}
case _ => { println("匹配不到") }
}
}
object SelfActor {
def main(args: Array[String]): Unit = {
// 创建一个ActorSystem
val actorSystem = ActorSystem("actorsystem")
// 创建一个actor
val actor01:ActorRef = actorSystem.actorOf(Props[SelfActor], "actor01")
actor01 ! "hello"
actorSystem.shutdown()
}
}
maven依赖
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.11</version>
</dependency>
运行结果
收到hello,回应 hello too
Akka实现跨进程通信
本例实现了一个echo服务器,服务器将从客户端收到的消息返回给客户端。
服务端 AkkaServer.scala
import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
class AkkaServer extends Actor {
override def receive: Receive = {
case "start" => println("服务器开始工作了....")
case msg:ClientMessage => {
println("客户端id: " + msg.clientId + ", 客户端name: " + msg.clientName
+ ",消息: " + msg.msg)
// 返回服务端的消息
sender() ! ServerMessage(msg.msg)
}
}
}
object AkkaServer {
def main(args: Array[String]): Unit = {
val host = "127.0.0.1"
val port = 9999
// 创建 config 对象,指定协议类型、监听的ip和端口
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 actorSystem = ActorSystem("server", config)
// 创建一个actor
val serverActor:ActorRef = actorSystem.actorOf(Props[AkkaServer], "server-actor")
serverActor ! "start"
}
}
客户端 AkkaClient.scala
import akka.actor.{Actor, ActorRef, ActorSelection, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
import scala.io.StdIn
class AkkaClient(clientID:String, clientName:String, host: String, port: Int) extends Actor{
var serverActorRef: ActorSelection = _
override def preStart(): Unit = {
serverActorRef = context.actorSelection(s"akka.tcp://server@${host}:${port}/user/server-actor")
println("this.serverActorRefer=" + this.serverActorRef)
}
override def receive: Receive = {
case "start" => println("客户端开始工作...")
case msg:String => {
serverActorRef ! ClientMessage(clientID, clientName, msg)
}
case msg:ServerMessage => {
println("收到服务器消息:" + msg.toString )
}
}
}
object AkkaClient {
def main(args: Array[String]): Unit = {
val (host, port, serverHost, serverPort) = ("127.0.0.1", 9990, "127.0.0.1", 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)
// 创建 ActorSystem
val actorSystem = ActorSystem("client-actorsystem", config)
// 创建 clientActor 的 Actor 和 ActorRef
val clientActor: ActorRef = actorSystem.actorOf(Props(new AkkaClient("01", "client-01", serverHost, serverPort)), "client-01")
// 启动客户端
clientActor ! "start"
while (true) {
val mes = StdIn.readLine()
clientActor ! mes
}
}
}
maven依赖
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_2.11</artifactId>
<version>2.3.11</version>
</dependency>
运行结果:
server
[INFO] [07/08/2020 11:13:04.385] [main] [Remoting] Starting remoting
[INFO] [07/08/2020 11:13:05.091] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://server@127.0.0.1:9999]
[INFO] [07/08/2020 11:13:05.092] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://server@127.0.0.1:9999]
服务器开始工作了....
客户端id: 01, 客户端name: client-01,消息: hello world
client
[INFO] [07/08/2020 11:13:06.549] [main] [Remoting] Starting remoting
[INFO] [07/08/2020 11:13:07.249] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://client-actorsystem@127.0.0.1:9990]
[INFO] [07/08/2020 11:13:07.250] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://client-actorsystem@127.0.0.1:9990]
this.serverActorRefer=ActorSelection[Anchor(akka.tcp://server@127.0.0.1:9999/), Path(/user/server-actor)]
客户端开始工作...
hello world
收到服务器消息:ServerMessage(hello world)