2

actor容器

actor是State、Behavior、mailbox、children、supervisor strategy的容器

akka的特点

1、高效并发Actor,异步、非阻塞、事件驱动的编程模型,轻量级线程
2、错误容忍fault-tolerant,let-it-crash, self-heal and never stop,supervisor hierarchies
3、可伸缩

The Error Kernel pattern

重要的数据放在靠近root节点附近, 把风险分散到叶子节点
1.重启是递归的
2.叶子节点将会频繁重启
3.避免重启带有重要状态的actor

针对需要阻塞的场景:

1、设置单独的线程池处理阻塞的操作
2、使用future来阻塞,同时注意限制线程池的队列长度,不要耗尽系统资源

actor是什么时候终止?

1、即使通过restart也无法启动
2、被自己的supervisor关闭了
3、自己关闭
关闭的时候,会将剩余的消息发送到系统的dead letter信箱
然后由系统邮箱接管,所有发送给它的消息都转发到系统邮箱
当一个subordinate检测到failure,它自动挂起自己以及他的子孙,然后给supervisor发送failure信号。

处理failure的四种策略

supervisor可以选择

  • 保持subordinate的已有状态继续

  • 清除subordinate状态重启

  • 停掉subordinate

  • 自己也选择抛出异常自己failed掉
    preRestart是个钩子方法,默认是在重启前终止掉它的子孙

akka的顶层supervisor

clipboard.png

  • /
    最顶层的guardian, 使用的是SupervisorStrategy.stoppingStrategy,只要是碰到Exception,就会终止child。严格来讲它不是一个actor,因为每个actor都必须有supervisor。它是在bubble之外的,也叫做bubble-walker

  • /user
    通过使用 system.actorOf()创建的actor都是user guardian的子孙

ActorSystem system = ActorSystem.create("HelloAkka");
ActorRef master = system.actorOf(Props.create(HelloAkka.class),"master");

这样也意味着,一旦user guardian挂掉,所有actor将会被停止。
然后user创建的actor可以通过getContext().actorOf去创建它的子孙。

final ActorRef greeter = getContext().actorOf(Props.create(Greeter.class), "greeter");
  • /system
    引入该guardian是为了确保系统的一些使用actor实现的服务(比如日志)也关闭掉。所有的Exception(除了ActorInitializationException和ActorKilledException)都会重启,其他throwables都是会再抛出来然后shut down整个actor system

  • /deadLetters
    所有发到stopped或者不存在的actor的消息都会被路由到这个actor

  • /temp
    系统创建的短暂actor

  • /remote
    在这底下的所有actor都在远程

重启的过程(使用新actor替代旧actor)

1、suspend并递归suspend该child
2、调用旧actor的preRestart方法(默认是给children发送termination请求,然后调用postStop方法)
3、等待所有需要terminate的children停止,
4、创建新的actor
5、调用postRestart方法(默认调用preStart方法)
6、发送重启请求给children
7、resume该actor

actor的监视

  • ActorContext.watch/unwatch(targetActorRef)

  • supervisor接受到Terminated信息之后,抛出DeatchPactException

  • OneForOneStrategy,只针对failed的节点极其子孙重启

  • AllForOneStrategy,重启该节点的所有兄弟,适用于一个节点与相邻节点有复杂关系的,如果supervisor没有处理Terminated信息,则会抛出DeathPactException,然后重启它。stop在这种模式下不会自动terminate它的孩子

actor的引用、路径及地址

clipboard.png

几个特殊的actorRef

  • PromiseActorRef

  • DeadLetterActorRef

  • EmptyLocalActorRef

actor的查找

  • ActorSystem.actorSelection

// 创建一个actor system,名为mySystem
ActorSystem system = ActorSystem.create("mySystem");
// 创建一个名为service的顶级actor
system.actorOf(Props.create(LookupActor.class), "service");
// 使用绝对路径获取service
ActorSelection service = system.actorSelection("akka://mySystem/user/service");
  • context.actorSelection
    可以使用..来访问parent actor,即可以使用相对路径

context.actorSelection("../brother") ! msg
context.actorSelection("/user/serviceA") ! msg

clipboard.png

actorOf与actorSelection与actorFor

  • actorOf仅仅用来创建新的actor

  • actorSelection仅仅用来查找已存在actor

  • actorFor被废弃,用actorSelection替代

Peer-to-Peer的架构

akka remote采用p2p的架构,基于

  • 通信是对称的,A可以通信B,B也应该可以通信A

  • 系统也是有收有发
    HTTP或者是AKKA I/O适合采用client-server模式

akka的内存模型

  • actor send rule

  1. send of the message to an actor happens before the receive of that message by the same actor.

  • actor subsequent processing rule
    actor是按顺序处理消息的,一个消息没处理完,不会处理下一个。即actor的变量在内部对于处理中的消息都是可见的,无需volatile

消息投递方式

  • at-most-once delivery(actor采取的方式)
    消息被投递0或1次,即消息可能被丢失。最高效。

  • at-least-once delivery(akka persistence采用)
    消息至少投递1次,即消息可能重复但不会丢失。需要重拾,同时保持state直到被接收

  • exactly-once delivery
    消息只投递一次,即既不重复也不丢失。最昂贵的,需要过滤重复的消息。

ACK-RETRY protocol

  • 需要消息的ack

  • 如果没有在指定时间收到ack,需要重试机制

  • 接收者需要有检测/丢弃重复消息的机制

消息可靠性

为什么不保证可靠性,akka具有分布性,基于这种抽象,丢失是不可避免的。比如JVM的stackOverFlow、OOM,也比如mailbox满了,或者接收的actor挂掉了等等。

消息顺序性

A1给A2发送M1,M2,M3
A3给A2发送M4,M5,M6
那么对于A2来说,M1到达由于M2,M2到达由于M3;M4到达由于M5,M5到达由于M6,但消息可能丢失,而M123与M456可能是夹杂的。
上面仅仅对于用户消息来说的,但是针对failure消息,不保证。比如C发送M给parent,然后发送failure消息F,那么parent接受到的可能是FM或者MF。

config加载

  • 从resource目录加载

final ActorSystem system = ActorSystem.create("CreationSystem",ConfigFactory.load("usoft9/remotecreation"));
final ActorRef actor = system.actorOf(Props.create(CreationActor.class), "creationActor");
  • 合并

Config firstConfig = ConfigFactory.load("complex1.conf");
Config secondConfig = ConfigFactory.load("complex2.conf");
//a.withFallback(b)  a和b合并,如果有相同的key,以a为准
Config finalConfig = firstConfig.withFallback(secondConfig);
System.out.println(finalConfig.getString("complex-app.something")); //hello world
System.out.println("simple-lib.foo"); //这个配置项是在complex2.conf中的

codecraft
11.9k 声望2k 粉丝

当一个代码的工匠回首往事时,不因虚度年华而悔恨,也不因碌碌无为而羞愧,这样,当他老的时候,可以很自豪告诉世人,我曾经将代码注入生命去打造互联网的浪潮之巅,那是个很疯狂的时代,我在一波波的浪潮上留下...


引用和评论

0 条评论