Akka并发编程框架
1. Akka特性
Akka是一个用于构造高并发,分布式和可扩展的基于事件驱动的应用的工具包,Akka是使用scala开发的库,同时可以使用scala和java语言来开发基于Akka的应用程序
2. Akka特性
- 提供基于异步非阻塞,高性能的事件驱动编程模型
- 内置容错机制,允许Actor在出错的时候进行恢复或者重置操作
- 超级轻量级的事件处理(每GB堆内存几百万的Actor)
- 使用Akka可以在单机上构建高并发程序,也可以在网络中构建分布式程序
3. Akka通信过程
以下图片说明了Akka Actor的并发编程模型的基本流程:
- 学生创建一个ActorSystem
- 通过ActorSystem来创建一个ActorRef(老师的引用),并将消息发送给ActorRef
- ActorRef将消息发送给Message Dispatcher(消息分发器)
- Message Dispatcher将消息按照顺序保存到目标Actor的MailBox中
- Message Dispatcher将MailBox放到一个线程中
- MailBox按照顺序取出消息,最终将它递给TeacherActor接受的方法中
4.创建Actor
Akka中.也是基于Actor来进行编程,只是有点小区别
5.API介绍
ActorSystem
- 在Akka中,ActorSystem是一个重量级的结构,它需要分配多个线程,ActorSystem通常是一个单例对象,可以使用这个ActorSystem创建多个
- Actor,它只是负责创建和监督actor
Actor中获取ActorSystem
- 直接使用context.system就可以获取到管理该Actor的ActorSystem的引用
实现Actor类:
- 继承Actor(导入akka.actor下的Actor)
- 实现receive方法,在receive方法中直接处理消息,不需要添加loop和react方法调用,Akka会自动调用receive来接收消息
**[可选]**还可以实现perStart()方法,该方法在Actor对象构建之后执行,在Actor生命周期中仅执行一次
加载Akka Actor
- 要创建Akka的Actor,必须要先获取创建一个ActorSystem,需要给ActorSystem指定一个名称,并且可以去加载一些配置项
- 调用ActorS.actorOf(Props(Actor对象),“Actor名字”)来加载Actor
Actor Path
在每一个Actor都有一个Path,路径格式如下:
Actor类型 | 路径 | 示例 |
本地Actor | akka://actorSystem名称/user/Actor名称 | akka://SimpleAkkaDemo/user/senderActor |
远程Actor | akka.tcp://my-sys@ip地址:port/user/Actor名称 | akka.tcp://192.168.10.17:5678/user/service-b |
6. 入门案例
基于Akka创建两个Actor,Actor之间可以相互发送消息
创建并加载Actor
- 创建两个Actor
- SenderActor:用来发送消息
- ReceiveActor:用来接收,回复消息 - 创建Actor
- 创建ActorSys
- 创建自定义Actor
- ActorSystem加载Actor
发送/接收消息
- 使用样例类封装消息
- SubmitTaskMessage----提交任务消息
- SussessSubmitTaskMessage----任务提交成功消息
参考代码
SimpleAkkaDemo
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object SimpleAkkaDemo {
case class SubmitTaskMessage(msg:String)//提交任务消息
case class SuccessSubmitTaskMessage(msg:String)//任务提交成功消息
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())
val senderActor = actorSystem.actorOf(Props(SenderActor),"senderActor")
val receiverActor = actorSystem.actorOf(Props(ReceiverActor),"receiverActor")
senderActor ! "start"
}
}
SenderActor
import SimpleAkkaDemo.{SubmitTaskMessage, SuccessSubmitTaskMessage}
import akka.actor.Actor
object SenderActor extends Actor{
override def receive: Receive = {
case "start"=>{
//path路径格式为: akka://ActorSystem的名字/user/接收消息的Actor名字
val receiverActor=context.actorSelection("akka://actorSystem/user/receiverActor")
receiverActor ! SubmitTaskMessage("提交任务信息,发送消息")
}
case SuccessSubmitTaskMessage(msg)=>println(s"SenderActor接收到:${msg}")
}
}
ReceiverActor
import SimpleAkkaDemo.{SubmitTaskMessage, SuccessSubmitTaskMessage}
import akka.actor.Actor
object ReceiverActor extends Actor{
override def receive: Receive = {
case SubmitTaskMessage(msg)=>{
println(s"ReceiverActor,接收到的消息是${msg}")
val senderActor = context.actorSelection("akka://actorSystem/user/senderActor")
senderActor ! SuccessSubmitTaskMessage("提交任务后回执信息,任务提交成功")
}
}
}
程序输出:
ReceiverActor,接收到的消息是提交任务信息,发送消息
SenderActor接收到:提交任务后回执信息,任务提交成功
7.Akka定时任务
Akka中,提供了一个scheduler对象来实现定时调度功能,
schedule方法针对scala提供了两种形式:
第一种:发送消息:
def schedule(
initialDelay: FiniteDuration, // 延迟多久后启动定时任务
interval: FiniteDuration, // 每隔多久执行一次
receiver: ActorRef, // 给哪个Actor发送消息
message: Any) // 要发送的消息
(implicit executor: ExecutionContext) // 隐式参数:需要手动导入
第二种:自定义实现
def schedule(
initialDelay: FiniteDuration, // 延迟多久后启动定时任务
interval: FiniteDuration // 每隔多久执行一次
)(f: ⇒ Unit) // 定期要执行的函数,可以将逻辑写在这里
(implicit executor: ExecutionContext) // 隐式参数:需要手动导入
示例一
示例说明
- 定义一个Actor,每1秒发送一个消息给Actor,Actor收到后打印消息
- 使用发送消息方式实现
参考代码
import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object ActorDemo {
// 1. 创建一个Actor,用来接收消息,打印消息
object ReceiverActor extends Actor {
override def receive: Receive = {
case x=>println(x)
}
}
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())
val receiveActor = actorSystem.actorOf(Props(ReceiverActor))
// 导入一个隐式转换
import scala.concurrent.duration._
// 导入隐式参数
import actorSystem.dispatcher
actorSystem.scheduler.schedule(0 seconds,1 seconds,receiveActor,"hello")
}
}
示例二
示例说明
- 定义一个Actor,每1秒发送一个消息给Actor,Actor收到后打印消息
- 使用自定义方式实现
参考代码
import akka.actor.{Actor, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object SechdulerActor extends Actor{
override def receive: Receive = {
case "timer"=>println("收到消息....")
}
}
object ActorDemo1 {
def main(args: Array[String]): Unit = {
val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())
val sechdulerActor = actorSystem.actorOf(Props(SechdulerActor),"sechdulerActor")
import scala.concurrent.duration._
import actorSystem.dispatcher
actorSystem.scheduler.schedule(0 seconds,1 seconds){
sechdulerActor ! "timer"
}
}
}
注意:
- 需要导入隐式转换import scala.concurrent.duration._才能调用0 seconds方法
- 需要导入隐式参数import actorSystem.dispatcher才能启动定时任务
8.实现两个进程之间的通信
基于Akka实现两个进程间发送,接收消息,Worker启动后去连接Master,并发送消息,Master接收到消息后,在回复给Worker
整体架构
Akka进程间通信-Worker实现
步骤:
创建一个Maven模块,导入**依赖(附件)**和配置文件
- 创建Maven模块.
GroupId: com.csdn
ArtifactID: akka-worker - 在src/main/resources文件夹下创建application.conf
- 在pom.xml中添加依赖
- 添加配置信息,修改端口为127.0.0.1:9999
创建启动WorkerActor
- 在src/main/scala文件夹下创建包: akka
- 在该包下创建 WorkerActor. //单例对象的形式创建.
- 在该包下创建Entrance单例对象, 里边定义main方法
发送"setup"消息给WorkerActor,WorkerActor接收打印消息
启动测试
application.conf
akka.actor.provider = "akka.remote.RemoteActorRefProvider"
akka.remote.netty.tcp.hostname = "127.0.0.1"
akka.remote.netty.tcp.port = "9999"
参考代码:
Worker
package akka
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Worker {
def main(args: Array[String]): Unit = {
//创建ActorSystem.
val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())
//通过ActorSystem, 加载自定义的WorkActor.
val workeractor = actorSystem.actorOf(Props(WorkerActor),"workeractor")
// 给WorkActor发送一句话.
workeractor ! "setup"
}
}
WorkerActor
package akka
import akka.actor.Actor
//akka.tcp://actorSystem@127.0.0.1:9999
object WorkerActor extends Actor{
override def receive = {
//打印接收到的消息
case "setup"=>println("WorkerActor 接收到的消息是:setup")
// 获取MasterActor的引用.
//格式: akka.tcp://ip地址/user/要获取的actor的名字.
val masterActor = context.actorSelection("akka.tcp://actorSystem@127.0.0.1:8888/user/masterActor")
//给MasterActor发送消息.
masterActor ! "Connect"
//接收MasterActor回复的消息.
case "Success"=>println("WorkerActor 接收到回应消息是 Sucess!")
}
}
程序输出结果
D:\develop\jdk1.8.0_181\bin\java "-javaagent:D:\IntelliJ IDEA 2017.3.2\lib\idea_rt.jar=50825:D:\IntelliJ IDEA 2017.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\develop\jdk1.8.0_181\jre\lib\charsets.jar;D:\develop\jdk1.8.0_181\jre\lib\deploy.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\dnsns.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\jaccess.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\localedata.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\nashorn.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunec.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\zipfs.jar;D:\develop\jdk1.8.0_181\jre\lib\javaws.jar;D:\develop\jdk1.8.0_181\jre\lib\jce.jar;D:\develop\jdk1.8.0_181\jre\lib\jfr.jar;D:\develop\jdk1.8.0_181\jre\lib\jfxswt.jar;D:\develop\jdk1.8.0_181\jre\lib\jsse.jar;D:\develop\jdk1.8.0_181\jre\lib\management-agent.jar;D:\develop\jdk1.8.0_181\jre\lib\plugin.jar;D:\develop\jdk1.8.0_181\jre\lib\resources.jar;D:\develop\jdk1.8.0_181\jre\lib\rt.jar;E:\a黑马javaweb视频\code\ScalaDSJ32\akka-master\target\classes;D:\develop\apache-maven-3.3.9-bin\repository_maven\org\scala-lang\scala-library\2.11.8\scala-library-2.11.8.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\typesafe\akka\akka-actor_2.11\2.3.14\akka-actor_2.11-2.3.14.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\typesafe\config\1.2.1\config-1.2.1.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\typesafe\akka\akka-remote_2.11\2.3.14\akka-remote_2.11-2.3.14.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\io\netty\netty\3.8.0.Final\netty-3.8.0.Final.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\org\uncommons\maths\uncommons-maths\1.2.2a\uncommons-maths-1.2.2a.jar akka.Master
[INFO] [11/04/2019 01:27:37.411] [main] [Remoting] Starting remoting
[INFO] [11/04/2019 01:27:38.090] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://actorSystem@127.0.0.1:8888]
[INFO] [11/04/2019 01:27:38.091] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://actorSystem@127.0.0.1:8888]
MasterActor 接收到的消息是:Connect
Akka进程间通信-Master实现
步骤:
创建一个Maven模块,导入依赖和配置文件
- 创建Maven模块.
GroupId: com.csdn
ArtifactID: akka-master - 在src/main/resources文件夹下创建application.conf
- 在pom.xml中添加依赖
- 添加配置信息,修改端口为127.0.0.1:8888
创建启动MasterActor
- 在src/main/scala文件夹下创建包: akka
- 在该包下创建 MasterActor. //单例对象的形式创建.
- 在该包下创建Entrance单例对象, 里边定义main方法
WorkerActor发送"connect"消息给MasterActor
MasterActor回复"success"消息给WorkerActor
WorkerActor接收并打印接收到的消息
启动Master、Worker测试
application.conf
akka.actor.provider = "akka.remote.RemoteActorRefProvider"
akka.remote.netty.tcp.hostname = "127.0.0.1"
akka.remote.netty.tcp.port = "8888"
参考代码:
Master
package akka
import akka.actor.{ActorSystem, Props}
import com.typesafe.config.ConfigFactory
object Master {
def main(args: Array[String]): Unit = {
// 创建ActorSystem.
val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())
// 通过ActorSystem, 关联MasterActor.
val masterActor = actorSystem.actorOf(Props(MasterActor),"masterActor")
}
}
MasterActor
package akka
import akka.actor.Actor
//akka.tcp://actorSystem@127.0.0.1:8888
object MasterActor extends Actor{
override def receive = {
// 打印接收到的消息
case "Connect"=>println("MasterActor 接收到的消息是:Connect")
//获取WorkerActor的引用.
val workerActor = context.actorSelection("akka.tcp://actorSystem@127.0.0.1:9999/user/workeractor")
//给WorkerActor回复一句话.
workerActor !"Success"
}
}
程序输出结果
D:\develop\jdk1.8.0_181\bin\java "-javaagent:D:\IntelliJ IDEA 2017.3.2\lib\idea_rt.jar=50843:D:\IntelliJ IDEA 2017.3.2\bin" -Dfile.encoding=UTF-8 -classpath D:\develop\jdk1.8.0_181\jre\lib\charsets.jar;D:\develop\jdk1.8.0_181\jre\lib\deploy.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\access-bridge-64.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\cldrdata.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\dnsns.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\jaccess.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\jfxrt.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\localedata.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\nashorn.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunec.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunjce_provider.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunmscapi.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\sunpkcs11.jar;D:\develop\jdk1.8.0_181\jre\lib\ext\zipfs.jar;D:\develop\jdk1.8.0_181\jre\lib\javaws.jar;D:\develop\jdk1.8.0_181\jre\lib\jce.jar;D:\develop\jdk1.8.0_181\jre\lib\jfr.jar;D:\develop\jdk1.8.0_181\jre\lib\jfxswt.jar;D:\develop\jdk1.8.0_181\jre\lib\jsse.jar;D:\develop\jdk1.8.0_181\jre\lib\management-agent.jar;D:\develop\jdk1.8.0_181\jre\lib\plugin.jar;D:\develop\jdk1.8.0_181\jre\lib\resources.jar;D:\develop\jdk1.8.0_181\jre\lib\rt.jar;E:\a黑马javaweb视频\code\ScalaDSJ32\akka-worker\target\classes;D:\develop\apache-maven-3.3.9-bin\repository_maven\org\scala-lang\scala-library\2.11.8\scala-library-2.11.8.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\typesafe\akka\akka-actor_2.11\2.3.14\akka-actor_2.11-2.3.14.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\typesafe\config\1.2.1\config-1.2.1.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\typesafe\akka\akka-remote_2.11\2.3.14\akka-remote_2.11-2.3.14.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\io\netty\netty\3.8.0.Final\netty-3.8.0.Final.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\com\google\protobuf\protobuf-java\2.5.0\protobuf-java-2.5.0.jar;D:\develop\apache-maven-3.3.9-bin\repository_maven\org\uncommons\maths\uncommons-maths\1.2.2a\uncommons-maths-1.2.2a.jar akka.Worker
[INFO] [11/04/2019 01:27:41.493] [main] [Remoting] Starting remoting
[INFO] [11/04/2019 01:27:42.150] [main] [Remoting] Remoting started; listening on addresses :[akka.tcp://actorSystem@127.0.0.1:9999]
[INFO] [11/04/2019 01:27:42.151] [main] [Remoting] Remoting now listens on addresses: [akka.tcp://actorSystem@127.0.0.1:9999]
WorkerActor 接收到的消息是:setup
WorkerActor 接收到回应消息是 Sucess!
<properties>
<maven.compiler.source>1.8</maven.compiler.source>
<maven.compiler.target>1.8</maven.compiler.target>
<encoding>UTF-8</encoding>
<scala.version>2.11.8</scala.version>
<scala.compat.version>2.11</scala.compat.version>
</properties>
<dependencies>
<dependency>
<groupId>org.scala-lang</groupId>
<artifactId>scala-library</artifactId>
<version>${scala.version}</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-actor_2.11</artifactId>
<version>2.3.14</version>
</dependency>
<dependency>
<groupId>com.typesafe.akka</groupId>
<artifactId>akka-remote_2.11</artifactId>
<version>2.3.14</version>
</dependency>
</dependencies>
<build>
<sourceDirectory>src/main/scala</sourceDirectory>
<testSourceDirectory>src/test/scala</testSourceDirectory>
<plugins>
<plugin>
<groupId>net.alchim31.maven</groupId>
<artifactId>scala-maven-plugin</artifactId>
<version>3.2.2</version>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>testCompile</goal>
</goals>
<configuration>
<args>
<arg>-dependencyfile</arg>
<arg>${project.build.directory}/.scala_dependencies</arg>
</args>
</configuration>
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-shade-plugin</artifactId>
<version>2.4.3</version>
<executions>
<execution>
<phase>package</phase>
<goals>
<goal>shade</goal>
</goals>
<configuration>
<filters>
<filter>
<artifact>*:*</artifact>
<excludes>
<exclude>META-INF/*.SF</exclude>
<exclude>META-INF/*.DSA</exclude>
<exclude>META-INF/*.RSA</exclude>
</excludes>
</filter>
</filters>
<transformers>
<transformer implementation="org.apache.maven.plugins.shade.resource.AppendingTransformer">
<resource>reference.conf</resource>
</transformer>
<transformer implementation="org.apache.maven.plugins.shade.resource.ManifestResourceTransformer">
<mainClass></mainClass>
</transformer>
</transformers>
</configuration>
</execution>
</executions>
</plugin>
</plugins>
</build>