ps:本文主要参考《akka入门与实践》
学习akka之前最好先了解一下reactive架构,actor模型和erlang语言的设计思想
《大数据时代的软件架构范式:Reactive架构及Akka实践》
《面向软件错误构建可靠的分布式系统》
补充erlang论文读后笔记
Akka中返回的Future是scala.concurrent.Future;在java8中可以将其转换为CompletableFuture。
CompletableFuture
- .thenAccept():对返回结果执行代码
- .thenApply():对返回结果进行转换
- .thenCompose():对返回结果进行异步转换,结果扁平化,得到结果后再进行另外一个异步调用,使得结果只在一个Future中;
- .handle():失败情况下执行代码
- .exceptionally():在失败中恢复,失败之后转换,返回一个成功的结果
层次结构:
实现:
在actor中,重写supervisorStrategy,返回一个新的策略OneForOneStrategy;
java8中用DeciderBuilder.match来实现模式匹配,匹配抛出的异常,并指定后续操作;
重启过程:
调用preRestart()->原来的actor停止(这个时候原来的状态就消失了)->重新建一个actor实例,运行新建的actor的构造函数->调用postRestart(),重启完成;
为了防止重启过程中丢失信息,最好的方式是通过Props把初始化信息传递给actor的构造函数;
很多时候,我们不希望actor在初始化的过程中发生错误,而是会给actor发送一条初始化信息,然后再actor运行的过程中处理状态的变化,可以在preStart()中向actor自己发送一条connect消息来达到这个效果:
public void preStart(){self.tell(new Connect(),null)}
在actor的构造函数或preStart方法中调度执行这个消息:
System.scheduler().scheduleOnce(
Duration.create(1000,TimeUnit.MILLISECONDS),self(),CheckConnected,system.dispacher(),null);
Actor根据不同的状态来改变行为的机制:
1.条件语句
2.热交换
context.become(PartialFunction behavior)改变成beahavior这个函数要做的事、unbecome()将actor的行为改回默认行为
3.FSM
- 比热交换要重
- 定义状态、定义状态容器(队列),定义行为
- akka.actor.AbstractFSM(S,D)
- When\matchEvent\event指定方法
Router:akka中用于负载均衡和路由的抽象;
创建:传入一个actor group或者由router来创建一个actor pool
路由策略:P121
广播消息:向一个router group/pool中所有actor发送消息:Router.tell(new akka.routing.Broadcast(msg));
监督router group/pool中的对象:
1.pool方式:router自己创建的actor,这些路由对象时router的子节点,可以在创建router的时候,调用withSupervisorStrategy方法来指定监督策略 so,推荐pool的方式
获取Dispatcher:
四种Dispatcher:
为actor配置dispatcher
可以把需要大量计算、运行时间较长的任务分离到单独的Dispatcher,确保在糟糕的情况下仍然能够有资源去运行其他任务;
创建ClusterController Actor,在actor的preStart和postStart方法订阅集群事件。
例子
—订阅集群事件,对不可用节点下线
启动节点
public class Main{
public static void mian(String[] args){
ActorSystem system = ActorSystem.create(".conf中system名字");
ActorRef clusterController = system.actorOf(
Props.create(ClusterController.class),"clusterController");
}
}
退出集群
试图使用kill进程来关闭及诶点的话,akka会把这个节点标记为不可达,然后输出一段错误信息。
优雅地退出集群:cluster.leave(self().path().address());–可以写一个api,对特殊情况下线机器节点
集群成员的状态
底层有一个逻辑上leader节点,负责协调状态的变化。所有节点的状态改变通过MermberEvent在集群间发送。
失败检查
在配置文件中,指定akka.cluster.seed-nodes指定种子节点,当一个节点加入集群时,该节点会尝试连接第一个种子节点,如果连接成功,新节点就会发布其地址。种子节点会负责通过gossip协议将新节点的地址最终通知整个集群,如果连接第一个种子节点失败,新节点就会尝试连接第二个种子节点。只要成功连接任何一个种子节点,那么任何节点加入或离开集群时,我们都不需要对配置进行修改。
当部署到生产环境时,应该至少定影两个拥有固定ip地址的种子节点,并且保证任何时候都至少有一个种子节点可用。如果节点尝试加入集群时,所有已种子节点都不可用,那么就无法加入集群。
akka.extensions = ["akka.contrib.pattern.ClusterReceptionistExtension"]
actor的路径包含两部分:源:akka://ActorSystem;路径:/user/actor;
如果我们向ActorSelection发送消息而其对应的Actor不存在,消息就会丢失.
判断actor存在
使用akka.actor.Identify
Identify msg = new Indentify(messageId);
Future identify = (Future)Patterns.ask(actor,msg,timeout);
如果actor存在,会接收到ActorIdentify(messagedId,Some(actorRef))作为相应,如不存在的话,会收到ActorIdentify((path,client),None);
邮箱基于队列,Props中有一个withMailbox的方法,可以在创建actor的时候调用该方法,为actor分配邮箱:
ActorRef clusterController = system.actorOf(Props.create(MyActor.class).withMailbox("default-mailbox"));
阻塞邮箱:邮箱已满时,再向邮箱发送消息会导致线程等待直至邮箱腾出空间;
非阻塞邮箱:邮箱满时,之后的消息会被丢弃;mailbox-type = “akka.dispatch.NonBlockingBoundedMailbox”
提高邮箱中消息的优先级
补充akka配置说明
akka {
prio-dispatcher {
mailbox-type = "PriorityMailbox"
}
actor {
provider = "cluster"
deployment {
/xxx/yyy{
router = balancing-pool
nr-of-instances = 5
pool-dispatcher {
executor = "fork-join-executor"
# allocate exactly N threads for this pool
fork-join-executor {
# Min number of threads to cap factor-based parallelism number to
parallelism-min = 8
# The parallelism factor is used to determine thread pool size using the
# following formula: ceil(available processors * factor). Resulting size
# is then bounded by the parallelism-min and parallelism-max values.
parallelism-factor = 3.0
# Max number of threads to cap factor-based parallelism number to
parallelism-max = 64
# Setting to "FIFO" to use queue like peeking mode which "poll" or "LIFO" to use stack
# like peeking mode which "pop".
task-peeking-mode = "FIFO"
# Throughput defines the maximum number of messages to be
# processed per actor before the thread jumps to the next actor.
# Set to 1 for as fair as possible.
throughput = 1
}
}
cluster {
enabled = on
allow-local-routees = on
use-roles = ["server"]
}
}
}
}
remote {
log-remote-lifecycle-events = off
netty.tcp {
hostname = "127.0.0.1"
port = 2551
}
}
cluster {
seed-nodes = [
"akka.tcp://xxx@ip:port",
"akka.tcp://xxx@ip:port"
]
roles = ["client", "server"]
auto-down-unreachable-after = 60s
seed-node-timeout = 60s
}
# sigar native library extract location during tests
# akka.cluster.metrics.native-library-extract-folder=${user.dir}/target/native
}
//akka.cluster.shutdown-after-unsuccessful-join-seed-nodes = 20s
//akka.coordinated-shutdown.terminate-actor-system = on
akka.cluster.jmx.multi-mbeans-in-same-jvm = on
高级:
日志:slf4j
信道eventBus:发布、订阅、分类器
Agent:java的AtomicInteger,绑定diapacher:get set方法
akka persistance:actor事件回放、状态保存