AKKA简介

  1. AKKA介绍:
    1. AKKA是java虚拟机JVM平台上构建高并发、分布式和容错应用工具包和运行时。可以理解为Akka是编写并发程序的框架
    2. AKKA是scala语言写成,同时提供了scala和java的接口
    3. AKKA主要解决的问题是:可以轻松的写出高效稳定的并发程序,程序员不在 过多的考虑县城,锁,和资源竞争等细节
  2. 主要解决什么问题
    1. 处理并发问题保证共享数据的一致性和正确性,因为程序是多线程时,多线程同事对一个数据进行修改,如果不加锁等同步条件,实际上大并发就会阻塞在这段代码中,对程序效率有很大影响
    2. 若是用单线程处理,不会有数据一致性问题,但是系统的性能又不能保证
    3. Actor模型的出现解决了这个问题,简化并发问题,提升程序性能,是一种并发问题的解决方案。
    4. Akka Actor的模型    
      1. AKKA处理并发的方法基于Actor模型
      2. 在基于actor的系统里,所有的事物是actor,就好像在面向对象设计里面所有的事物都是对象一样
      3. Actor模型是作为一个并发模型设计和架构的,actor于actor之间只能通过消息通信
      4. Actor于Actor之间只能用消息通信,消息是有顺序的
      5. ActorSystem 的职责是负责创建并管理起创建的Actor,在一个JVM进行中有一个即可
      6. Actor模型是对并发模型进行了高的抽象
      7. Actor模型是异步,非阻塞,高性能的事件驱动编程模型
      8. Actor模型是轻量级时间处理,1GB内存可容纳百万级别个Actor。因此处理大并发性能很高
    5. 模型工作机制说明

      Actor模型说明

      1.ActorSystem创建Actor

      2.ActorRef:可以理解成是Actor的代理或者引用,消息是通过ActoRef来发送的,而不能通过Actor直接发送消息,通过哪个ActorRef发消息就说明把该消息发送给哪个Actor

      3.消息发送 到Dispatch Message(消息分发器,可以理解为一个线程池),它得到消息后会将消息进行分发到对应的MailBox(可以理解为一个消息队列,FIFO)

      4.Actor可以通过receive方法来获取消息,然后进行处理

 

      Actor间消息传递机制

      1.每个消息就是一个message对象,message继承了Runnable,因为Message就是线程类

      2.从Actor模型工作机制看上去很麻烦,但是程序员编程时只需要编写Actor就可以了,其他的交给Actor模型完成即可

      3.A Actor要给B Actor发送消息,那么 A Actor要先拿到B Actor的代理对象ActorRef才能发送对象

      测试用例:

      

package analyse

import akka.actor.{Actor, ActorRef, ActorSystem, Props}
import com.typesafe.config.ConfigFactory
class YellowClikenServer extends Actor  {
  override def receive: Receive = {
    case "start" => println("start 小黄鸡客服开始工作了...")
    // 如果接收到了服务端的发来的消息,即 ClientMessage
    case ClientMessage(mes) => {
      println("客户咨询的问题是:" + mes)
      mes match {
        // 使用 match case 匹配(模糊匹配)
        case "价格" => sender() ! ServerMessage("20000 RMB")
        case "地址" => sender() ! ServerMessage("北京市")
        case "技术" => sender() ! ServerMessage("大数据")
        case _ => sender() ! ServerMessage("你说的啥子:)")
      }
    }
  }
}

// 主程序入口
object YellowChickenServerApp extends App {
  val host = "127.0.0.1" // 服务端ip地址
  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)
  // 创建 ActorSystem
  val serverActorSystem = ActorSystem("Server", config)
  // 创建 YellowChickenServer 的 Actor 和 ActorRef
  val yellowChickenServerActorRef: ActorRef = serverActorSystem.actorOf(Props[YellowClikenServer], "YellowChickenServer-01")

  // 启动服务端
  yellowChickenServerActorRef ! "start"
}
package analyse

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

import scala.io.StdIn

class CustomerActor(serverHost: String, serverPort: Int) extends Actor {
  // 定义一个 YellowChickenServerRef
  var serverActorRef: ActorSelection = _

  // 在 Actor 中有一个方法 preStart 方法,它会在 Actor 运行前执行
  // 在 Akka 开发中,通常将初始化的工作,放在 preStart 方法中
  override def preStart(): Unit = {
    this.serverActorRef = context.actorSelection(s"akka.tcp://Server@${serverHost}:${serverPort}/user/YellowChickenServer-01")
    println("this.serverActorRefer=" + this.serverActorRef)
  }

  override def receive: Receive = {
    case "start" => println("start 客户端运行,可以咨询问题")
    case mes: String => {
      // 发给服务端
      // serverActorRef ! mes // 不应该发送字符串,应该包装一把,应该发送一个(样例)对象(即协议)
      serverActorRef ! ClientMessage(mes) // 此时发送的是一个对象,该样例类默认实现了序列化 和 apply 方法
    }
    // 如果接受到了服务器端的消息
    case ServerMessage(mes) => {
      println(s"收到小黄鸡客服(Server)消息:$mes")
    }
  }
}

// 主程序入口
object CustomerActorApp extends App {
  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 clientActorSystem = ActorSystem("Client", config)
  // 创建 CustomerActor 的 Actor 和 ActorRef
  val clientActorRef: ActorRef = clientActorSystem.actorOf(Props(new CustomerActor(serverHost, serverPort)), "CustomerActor-01")

  // 启动客户端
  clientActorRef ! "start"

  // 客户端发送消息
  while (true) {
    val mes = StdIn.readLine()
    clientActorRef ! mes
  }
}

 

   

posted on 2022-09-23 14:10  郝心人forever  阅读(2685)  评论(0编辑  收藏  举报