目录

  • 一 Dispatcher 任务分发
  • 1.1 理解什么是Dispatcher
  • 1.2 Executor的分类
  • 1.3 基本使用
  • 1.4 其他类型的调度器


  • 在Akka中,Actor的消息通信和任务执行建立在一个完全透明的调度机制之上,它屏蔽了底层线程(池)的实现细节,几乎让开发者感觉不到它的存在。
  • 调度机制类似于一个消息中心控制器,所有消息、任务都由它负责分发和派送,在这个过程中,有很多因素会影响整个系统的效率和性能。

一 Dispatcher 任务分发

1.1 理解什么是Dispatcher

当前疫情情况下,快递小哥短缺,由于每个快递小哥都有大量的货要送,所以很可能会造成延迟送货,为了解决这个问题,快递公司可能会从两方面入手。

  • 让快递小哥们提高单次送货速度,尽可能多送货。
  • 临时增派人手,以便处理更多的送货任务。

在Akka中,Actor运行在一个线程池之上,每个线程(快递小哥)会处理多个Actor消息(商品),为了能保证Actor消息处理的及时性和线程的使用效率,我们需要对线程池做一些调配或协调的工作,这就是Dispatcher的意义。

akka使用demo_akka使用demo


在实际项目中,我们(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
}

akka使用demo_akka_02

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
        }

akka使用demo_学习_03

  • PinnedDispatcher会让每个Actor都有自己单独的线程池(虽然只有一个线程),这个线程池不会被其他Actor共享;
  • 普通的Dispatcher会让多个Actor绑定到一个线程池,当任务很多并且非常耗时的时候,会占用大量(甚至全部)的线程去执行它们,而新的任务会等待空闲的线程。