目录
- 一 Dispatcher 任务分发
- 1.1 理解什么是Dispatcher
- 1.2 Executor的分类
- 1.3 基本使用
- 1.4 其他类型的调度器
- 在Akka中,Actor的消息通信和任务执行建立在一个完全透明的调度机制之上,它屏蔽了底层线程(池)的实现细节,几乎让开发者感觉不到它的存在。
- 调度机制类似于一个消息中心控制器,所有消息、任务都由它负责分发和派送,在这个过程中,有很多因素会影响整个系统的效率和性能。
一 Dispatcher 任务分发
1.1 理解什么是Dispatcher
当前疫情情况下,快递小哥短缺,由于每个快递小哥都有大量的货要送,所以很可能会造成延迟送货,为了解决这个问题,快递公司可能会从两方面入手。
- 让快递小哥们提高单次送货速度,尽可能多送货。
- 临时增派人手,以便处理更多的送货任务。
在Akka中,Actor运行在一个线程池之上,每个线程(快递小哥)会处理多个Actor消息(商品),为了能保证Actor消息处理的及时性和线程的使用效率,我们需要对线程池做一些调配或协调的工作,这就是Dispatcher的意义。
在实际项目中,我们(Actor)可能需要处理各种不同类型的任务,有的任务耗时长,或者长时间阻塞,而有的任务则处理较快,不会造成阻塞,大部分时候,我们会将这两类任务做线程资源上的隔离,即分配不同的Dispatcher处理它们,此时可以避免由于某类任务耗尽线程而让其他请求等待的情况。
1.2 Executor的分类
- thread-pool-executor:基于普通的线程池,它有一个工作队列,当线程空闲时会从队列中获取任务并执行。
my- threadpool-dispatcher {
# dispatcher类型
type = Dispatcher
executor = "thread-pool-executor"
# 配置线程池
thread-pool-executor {
# 最小线程数
core-pool-size- min = 2
# 并发使用的最大线程数=处理器*因子
core-pool-size-factor = 3.0
#最大线程数
core-pool-size-max = 15
}
throughput = 1
}
- fork-join-executor:基于工作窃取的线程池,它的工作思路是把大的计算任务拆分成小的任务然后并行执行,最后合并结果。当某个线程的任务队列里没有任务时,会主动从其他线程的任务队列中“窃取”任务。一般认为,fork-join的性能更佳,这也是Akka的默认选项。
# 自定义线程名称,Actor中使用
my-forkjoin-dispatcher {
# 类型:有多种类型,不同的线程调度策略
type = Dispatcher
# 执行器
executor = "fork-join-executor"
# 配置fork-join线程池
fork-join-executor {
# 最小线程
parallelism- min = 3
# 并发因子 cpu核数*parallelism-factor
parallelism-factor = 3.0
# 最大线程数
parallelism-max = 15
}
# 公证
throughput = 1
}
1.3 基本使用
代码式
import akka.actor.AbstractActor;
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.cluster.ClusterEvent;
import akka.event.Logging;
import akka.event.LoggingAdapter;
/**
* @description:
* @author: shu
* @createDate: 2022/12/23 13:58
* @version: 1.0
*/
public class DispacherActor extends AbstractActor {
protected final LoggingAdapter log = Logging.getLogger(context().system(), this);
@Override
public Receive createReceive() {
return receiveBuilder()
.build();
}
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("sys");
ActorRef ref = system.actorOf(Props.create(DispacherActor.class).withDispatcher("my-forkjoin-dispatcher"), "actorDemo");
}
}
配置式
akka.actor.deployment {
/actorDemo {
dispatcher = my-forkjoin-dispatcher
}
}
1.4 其他类型的调度器
innedDispatcher是另外一种Dispatcher类型,它会为每个Actor提供只有一个线程的线程池,该线程池为Actor独有。当Actor处理比较耗时、阻塞的操作时,使用Pinned-Dispatcher将会非常有效。
import akka.actor.ActorRef;
import akka.actor.ActorSystem;
import akka.actor.Props;
import akka.actor.UntypedActor;
/**
* @description:
* @author: shu
* @createDate: 2022/12/23 14:08
* @version: 1.0
*/
class ActorPinnedDemo extends UntypedActor {
@Override
public void onReceive(Object msg) throws Exception {
System.out.println(getSelf()+"--->"+msg+""
+Thread.currentThread().getName());
Thread.sleep(5000);
}
public static void main(String[] args) {
ActorSystem system = ActorSystem.create("sys");
for(int i=0; i<20; i++) {
ActorRef ref = system.actorOf(Props.create(ActorPinnedDemo.class)
.withDispatcher("my-pinned-dispatcher"), "actorDemo"+i);
ref.tell("hello pinned", ActorRef.noSender());
}
}
}
my-pinned-dispatcher {
executor = "thread-pool-executor"
type = PinnedDispatcher
}
- PinnedDispatcher会让每个Actor都有自己单独的线程池(虽然只有一个线程),这个线程池不会被其他Actor共享;
- 普通的Dispatcher会让多个Actor绑定到一个线程池,当任务很多并且非常耗时的时候,会占用大量(甚至全部)的线程去执行它们,而新的任务会等待空闲的线程。