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
/
最顶层的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的引用、路径及地址
几个特殊的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
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
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中的