Akka中,也是基于Actor来进行编程的。类似于之前学习过的Actor。但是Akka的Actor的编写、创建方法和之前有一些不一样。

API介绍

  • ActorSystem: 它负责创建和监督Actor
  1. 在Akka中,ActorSystem是一个重量级的结构,它需要分配多个线程.
  2. 在实际应用中, ActorSystem通常是一个单例对象, 可以使用它创建很多Actor.
  3. 直接使用context.system就可以获取到管理该Actor的ActorSystem的引用.
  • 实现Actor类
  1. 定义类或者单例对象继承Actor(注意:要导入akka.actor包下的Actor
  2. 实现receive方法,receive方法中直接处理消息即可,不需要添加loop和react方法调用. Akka会自动调用receive来接收消息.
  3. 【可选】还可以实现preStart()方法, 该方法在Actor对象构建后执行,在Actor生命周期中仅执行一次.
  • 加载Actor
  1. 要创建Akka的Actor,必须要先获取创建一个ActorSystem。需要给ActorSystem指定一个名称,并可以去加载一些配置项(后面会使用到)
  2. 调用ActorSystem.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

入门案例

需求

基于Akka创建两个Actor,Actor之间可以互相发送消息
image.png

实现步骤

  1. 创建Maven模块
  2. 创建并加载Actor
  3. 发送/接收消息

    创建Maven模块

    使用Akka需要导入Akka库,这里我们使用Maven来管理项目, 具体步骤如下:

  4. 创建Maven模块.

    1. 选中项目, 右键 -> new -> Module -> Maven -> Next ->
    2. GroupId: com.itheima
    3. ArtifactId: akka-demo
    4. next -> 设置"module name"值为"akka-demo" -> finish
  5. 打开pom.xml文件,导入akka Maven依赖和插件.

    1. //1. 直接把资料的pom.xml文件中的内容贴过来就行了.
    2. //2. 源码目录在: src/main/scala下
    3. //3. 测试代码目录在: src/test/scala下.
    4. //4. 上述的这两个文件夹默认是不存在的, 需要我们手动创建.
    5. //5. 创建出来后, 记得要修改两个文件夹的类型.
    6. 选中文件夹, 右键 -> Mark Directory as ->
    7. Source Roots //存放源代码.
    8. Test Source Roots //存放测试代码.

    导包

    ```sql

    1. <dependency>
    2. <groupId>org.scala-lang</groupId>
    3. <artifactId>scala-library</artifactId>
    4. <version>2.12.14</version>
    5. </dependency>
    6. <!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-remote -->
    7. <dependency>
    8. <groupId>com.typesafe.akka</groupId>
    9. <artifactId>akka-remote_2.12</artifactId>
    10. <version>2.6.19</version>
    11. </dependency>
    <dependency>
        <groupId>com.typesafe.akka</groupId>
        <artifactId>akka-stream_2.12</artifactId>
        <version>2.6.19</version>
    </dependency>

    <!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-actor -->
    <dependency>
        <groupId>com.typesafe.akka</groupId>
        <artifactId>akka-actor_2.12</artifactId>
        <version>2.6.19</version>
    </dependency>


    <!-- https://mvnrepository.com/artifact/com.typesafe.akka/akka-slf4j -->
    <dependency>
        <groupId>com.typesafe.akka</groupId>
        <artifactId>akka-slf4j_2.12</artifactId>
        <version>2.6.19</version>
    </dependency>
<a name="n8SDH"></a>
### 创建并加载Actor
到这, 我们已经把Maven项目创建起来了, 后续我们都会采用Maven来管理我们的项目. 接下来, 我们来实现:<br />创建并加载Actor, 这里, 我们要创建两个Actor:

- SenderActor:用来发送消息
- ReceiverActor:用来接收,回复消息

**具体步骤**

1. 在src/main/scala文件夹下创建包: 
1. 在该包下创建两个Actor(注意: 用object修饰的单例对象).
   - SenderActor: 表示发送消息的Actor对象.
   - ReceiverActor: 表示接收消息的Actor对象.
3. 在该包下创建单例对象Entrance, 并封装main方法, 表示整个程序的入口.
3. 把程序启动起来, 如果不报错, 说明代码是没有问题的.
```scala
object SenderActor extends Actor {
    /*
    细节: 
        在Actor并发编程模型中, 需要实现act方法, 想要持续接收消息, 可通过loop + react实现.
        在Akka编程模型中, 需要实现receive方法, 直接在receive方法中编写偏函数处理消息即可.
    */
    //重写receive()方法
    override def receive: Receive = {
        case x => println(x)
    }
} 

object ReceiverActor extends Actor{
    //重写receive()方法
    override def receive: Receive = {
        case x => println(x)
    }
}

object Entrance {    
    def main(args:Array[String]) = {
        //1. 实现一个Actor Trait, 其实就是创建两个Actor对象(上述步骤已经实现).

        //2. 创建ActorSystem
        //两个参数的意思分别是:ActorSystem的名字, 加载配置文件(此处先不设置)
        val actorSystem = ActorSystem("actorSystem",ConfigFactory.load())

        //3. 加载Actor
        //actorOf方法的两个参数意思是: 1. 具体的Actor对象. 2.该Actor对象的名字
        val senderActor = actorSystem.actorOf(Props(SenderActor), "senderActor")
        val receiverActor = actorSystem.actorOf(Props(ReceiverActor), "receiverActor")
    }
}

发送/接收消息

思路分析

  1. 使用样例类封装消息
    • SubmitTaskMessage——提交任务消息
    • SuccessSubmitTaskMessage——任务提交成功消息
  2. 使用!发送异步无返回消息.

参考代码

  • MessagePackage.scala文件中的代码 ```scala /**
    • 记录发送消息的 样例类.
    • @param msg 具体的要发送的信息. */ case class SubmitTaskMessage(msg:String)

/**

  • 记录 回执信息的 样例类.
  • @param msg 具体的回执信息. */ case class SuccessSubmitTaskMessage(msg:String) ```
  • Entrance.scala文件中的代码

    //程序主入口.
    object Entrance {
    def main(args: Array[String]): Unit = {
      //1. 创建ActorSystem, 用来管理所有用户自定义的Actor.
      val actorSystem = ActorSystem("actorSystem", ConfigFactory.load())
      //2. 通过ActorSystem, 来管理我们自定义的Actor(SenderActor, ReceiverActor)
      val senderActor = actorSystem.actorOf(Props(SenderActor), "senderActor")
      val receiverActor = actorSystem.actorOf(Props(ReceiverActor), "receiverActor") 
    
      //3. 由ActorSystem给 SenderActor发送一句话"start".
      senderActor ! "start"
    }
    }
    
  • SenderActor.scala文件中的代码

    object SenderActor extends Actor{
    override def receive: Receive = {
     //1. 接收Entrance发送过来的: start
      case "start" => {
        //2. 打印接收到的数据.
        println("SenderActor接收到: Entrance发送过来的 start 信息.")
    
        //3. 获取ReceiverActor的具体路径.
        //参数: 要获取的Actor的具体路径.
        //格式: akka://actorSystem的名字/user/要获取的Actor的名字.
        val receiverActor = context.actorSelection("akka://actorSystem/user/receiverActor")
    
        //4. 给ReceiverActor发送消息: 采用样例类SubmitTaskMessage
        receiverActor ! SubmitTaskMessage("我是SenderActor, 我在给你发消息!...")
      }
    
        //5. 接收ReceiverActor发送过来的回执信息.
      case SuccessSubmitTaskMessage(msg) => println(s"SenderActor接收到回执信息: ${msg} ")
    }
    }
    
  • ReceiverActor.scala文件中的代码 ```scala object ReceiverActor extends Actor { override def receive: Receive = { //1. 接收SenderActor发送过来的消息. case SubmitTaskMessage(msg) => {

    //2. 打印接收到的消息.
    println(s"ReceiverActor接收到: ${msg}")
    
    //3. 给出回执信息.
    sender ! SuccessSubmitTaskMessage("接收任务成功!. 我是ReceiverActor")
    

    } } }

**输出结果**
```scala
SenderActor接收到: Entrance发送过来的 start 信息.
ReceiverActor接收到: 我是SenderActor, 我在给你发消息!...
SenderActor接收到回执信息: 接收任务成功!. 我是ReceiverActor