akka 学习,scala语言(1)

先分享一下akka的官方文档
https://doc.akka.io/docs/akka/current/dispatchers.html

Q&A
1- context 的become 和 unbecome
我们知道Actor 的功能是在重写receive函数实现的,那么become 和 unbecome 只用来切换receive函数这个从写的函数对象的。
注意become只是把函数当做了对象压入了堆栈,所以如果你一直become,必然会出现堆栈的溢出

简单写了一个sample:

package akka_future
import akka.actor._


case class sign(times:Int)

class TestActor extends Actor with ActorLogging {
  override def receive: Receive = Case1

  def Case1:Receive ={
    case sign(x) =>
      log.info(s"Case1: receive sign[$x]")
      context.become(Case2)
  }

  def Case2:Receive = {
    case sign(x) =>
      log.info(s"Case2: receive sign[$x]")
      context.become(Case1)
  }

}

object BecomeActor extends App {

  val system = ActorSystem("Become")
  val test = system.actorOf(Props(new TestActor), "TestBecome")

  for {
    x  <- 1 to 20
  }{
      test ! sign(x)
  }

}

看一下源码,(trait ActorContext extends ActorRefFactory,接口定义在ActorContext中)

  /**
   * Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
   * Replaces the current behavior on the top of the behavior stack.
   *
   * *Warning*: This method is not thread-safe and must not be accessed from threads other
   * than the ordinary actor message processing thread, such as [[java.util.concurrent.CompletionStage]] and [[scala.concurrent.Future]] callbacks.
   */
   
	def become(behavior: Actor.Receive): Unit = become(behavior, discardOld = true)
	info
	==== 该方法是线程不安全的,不能再其他创建新的线程中调用。
	==== 该方法调用了重载函数,且多了一个参数
	==== become中的函数对象是存放在堆中的
	
  /**
   * Changes the Actor's behavior to become the new 'Receive' (PartialFunction[Any, Unit]) handler.
   * This method acts upon the behavior stack as follows:
   *
   *  - if `discardOld = true` it will replace the top element (i.e. the current behavior)
   *  - if `discardOld = false` it will keep the current behavior and push the given one atop
   *
   * The default of replacing the current behavior on the stack has been chosen to avoid memory
   * leaks in case client code is written without consulting this documentation first (i.e.
   * always pushing new behaviors and never issuing an `unbecome()`)
   *
   * *Warning*: This method is not thread-safe and must not be accessed from threads other
   * than the ordinary actor message processing thread, such as [[java.util.concurrent.CompletionStage]] and [[scala.concurrent.Future]] callbacks.
   */
	def become(behavior: Actor.Receive, discardOld: Boolean): Unit
在重载函数中,多了一个个参数 discardOld, 按照注释 true 为 替换堆顶元素, false为放到堆顶且不移除原来的元素 


以上的内容来自于trait,那么我们看一下实际调用处的地方

private var behaviorStack: List[Actor.Receive] = emptyBehaviorStack

def become(behavior: Actor.Receive, discardOld: Boolean = true): Unit =
behaviorStack = behavior :: (if (discardOld && behaviorStack.nonEmpty) behaviorStack.tail else behaviorStack)

behaviorStack本质上就是一个scala的List,我们相当于把函数对象插入到了这个list里面,这里的函数对象类似于C中的函数指针。

经过测试,把元素插入tail就是替换到List的第一个元素

def listTest():Unit = {
val l1 = List(1,2,3)

val t3 = 0 :: l1.tail
println(s"t3 : ${t3.toString}")
}

打印
t3 : List(0, 2, 3)

Q&A
2.actorOf 用来创建actor,actorSelection用来查找actor,之前还有一个actorFor的方法,目前官方文档已经废弃了这个方法。

先看个sample 生成一个名为A的actor,再去查找并发消息

object A {
  def props(name:String) = Props(new A(name))
}

class A(name:String) extends Actor with ActorLogging {
  override def receive: Receive = Case1

  def Case1:Receive ={
    case sign(x) =>
      log.info(s"$name: receive sign[$x]")
  }

}

object CreateActor extends App{
    val system = ActorSystem("Create")

    val a:ActorRef = system.actorOf(A.props("frankie"),"frankie")

    val _a:ActorSelection = system.actorSelection("/user/frankie")

    println(s"a path  ${a.path}")
    println(s"a path  ${_a.pathString}")

  a ! sign(0)
  a ! sign(1)
}
  

打印:
a path akka://Create/user/frankie
a path /user/frankie
[INFO] [07/17/2019 21:30:07.380] [Create-akka.actor.default-dispatcher-3] [akka://Create/user/frankie] frankie: receive sign[0]
[INFO] [07/17/2019 21:30:07.381] [Create-akka.actor.default-dispatcher-3] [akka://Create/user/frankie] frankie: receive sign[1]

话不多说看源码
ActorRefFactory(trait)

/**

  • Create new actor as child of this context with the given name, which must
  • not be null, empty or start with “$”. If the given name is already in use,
  • an InvalidActorNameException is thrown.
  • See [[akka.actor.Props]] for details on how to obtain a Props object.
  • @throws akka.actor.InvalidActorNameException if the given name is
  • invalid or already in use
  • @throws akka.ConfigurationException if deployment, dispatcher
  • or mailbox configuration is wrong
  • @throws UnsupportedOperationException if invoked on an ActorSystem that
  • uses a custom user guardian
    */
    def actorOf(props: Props, name: String): ActorRef

abstract class ActorSystem extends ActorRefFactory 真正的实现是在ActorSystem里面实现的

看一下实现代码:

val guardianProps: Option[Props],

def actorOf(props: Props, name: String): ActorRef =
if (guardianProps.isEmpty) guardian.underlying.attachChild(props, name, systemService = false)
else throw new UnsupportedOperationException(
  s"cannot create top-level actor [$name] from the outside on ActorSystem with custom user guardian")

private[akka] class LocalActorRef private[akka] (
_system: ActorSystemImpl,
_props: Props,
_dispatcher: MessageDispatcher,
_mailboxType: MailboxType,
_supervisor: InternalActorRef,
override val path: ActorPath)
extends ActorRefWithCell with LocalRef {

这个函数我看了一下,后面基本上是初始化了整个actor,邮箱啊,路径啊,父子关系啊等,很复杂不好一时搞明白(已经在另一个线程中工作了)
guardian.underlying.attachChild(props, name, systemService = false)

看一下官网上的内容

In order to make system.actorOf both synchronous and non-blocking while keeping the return type ActorRef (and the semantics that the returned ref is fully functional),
special handling takes place for this case. Behind the scenes, a hollow kind of actor reference is constructed, which is sent to the system’s guardian actor who actually
creates the actor and its context and puts those inside the reference. Until that has happened, messages sent to the ActorRef will be queued locally, and only upon
swapping the real filling in will they be transferred into the real mailbox.

就是说下面为了不阻塞这个方法,返回了一个空的引用,只有另一个进程中完全创建好这个这个actor,他才能真正的工作,信箱才可以收信。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值