对于AKKA中actor的一点个人理解

对于AKKA中actor的一点个人理解

  • Actor是什么
  • Actor和线程的区别
  • Actor是如何执行的
  • 不要阻塞一个Actor
  • 慎用Actor的ask方法
  • thoughtput的一点思考

Actor是什么

曾经看到知乎上有人提过actor就是cpu上的时间片,这种说法非常贴切。Actor之间通过消息进行通讯,一切都是异步的。可以说Actor就像现实生活中的一群人,他们各司其职,互相通过消息进行交流,一个actor收到另外一个actor发来的消息后会按照消息的内容去执行指定的任务,接着再将新任务传递下去或者将执行结果返回给消息发送方。Actor这种模型很好地解决了传统java并发带来的各种问题。

Actor和线程的区别

Actor规避了传统多线程中锁的问题,在Actor中是没有共享变量的,一切都是无状态的,尽管我们可以在actor中去调用一个新的线程去进行一些异步操作,但是这并不符合Actor本身的理念,并且会破坏Actor的整体设计。要记住一点,Actor是单线程运行的,一个Actor同时只能处理一条消息,我们可以通过增加Actor的数量来提高系统并行处理的能力。

Actor是如何执行的

AKKA中使用dispatcher对actor进行执行,当一个actor启动之后会将自身绑定到一个dispatcher上,我们可以在系统配置中定义自己的dispathcer。Dispatcher本身其实是一个线程池,默认的dispatcher是一个fork-join-executor,读者可以参考下表来了解不通的dispatcher。

Dispathcer 适用场景
默认Dispatcher 适合大多数场景,默认实现
PinnedDispatcher 适合高优先级actor,为actor使用独立的线程
BalancingDispatcher 使用该dispatcher的actor将共享一个邮箱,只适合相同类型actor使用,2.3版本之后被BalancingPool替代

一个简单的dispatcher配置

default-dispatcher {
   executor = "fork-join-executor"//默认executor
   fork-join-executor {
     parallelism-min = 8
     parallelism-factor = 2.0
     parallelism-max = 8//最大活跃线程数
   }
 }

不要阻塞一个Actor

Actor的一条重要准则就是尽量不要去阻塞一个Actor,因为Actor本身为单线程处理消息,一旦被阻塞会导致消息积压、dispatcher资源被大量占据等问题,在笔者目前的项目中我们一般使用future去对IO阻塞类的操作进行处理。另外还有一条思路就是为存在阻塞的操作创建多个独立的actor,并将这些actor绑定到一个独立的dispatcher,将阻塞actor与常规actor进行隔离,避免影响到其他actor的执行

通过配置独立dispatcher来隔离Actor

//首先在配置文件中定义一个新的dispatcher
 crawler-dispatcher{
  type=Dispatcher
  executor = "thread-pool-executor"
  thread-pool-executor{
    core-pool-size-min = 4
    core-pool-size-max = 64
  }
  throughput=5
}

//调用以下withDispatcher方法绑定actor到指定dispatcher
 val actor = context.actorOf(Props[MyActor].withDispatcher("crawler-dispatcher"))

慎用Actor的ask方法

方法 区别 执行方式
tell fire and forget,发送后立刻返回 直接发送
ask 发送后会等待一段时间,并返回一个future 创建一个中间代理再发送

上表展示了tell和ask的区别,通过阅读akka源码可以发现,在使用ask的时候actor会生成一个临时的代理Actor再去发送消息,如果滥用ask会对系统的性能造成很大的影响,需要注意

//akka中调用ask创建的临时actorRef
val a = PromiseActorRef(ref.provider, timeout, targetName = actorRef, message.getClass.getName, sender)
        actorRef.tell(message, a)
        a.result.future

thoughtput的一点思考

在上面我们讲到了一些dispatcher的区别和使用方法,其中还有一个参数叫做thoughtput ,在akka官方文档中是这么描述的,
# 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.
阅读可知,这个参数可以让我们对actor在获取线程之后进行处理的消息数量进行设置,设置为1则为公平的模式,如果设置的很大,则当前actor可以一直占据该线程直到消费完指定数目的消息后才会让出线程。如果我们的cpu上下文切换过多可以考虑将该值设置得大一点,如果消息数量本身很少,设置过大的值会造成actor一直占据着线程空等消息,这会影响其他actor的执行,具体的设置还需要大家自行摸索。

default-dispatcher {
   executor = "fork-join-executor"
   fork-join-executor {
     parallelism-min = 8
     parallelism-factor = 2.0
     parallelism-max = 8
   }
   thoughtput = 5//可以设置actor让出线程前处理消息的数目,可以进行设置降低cpu上下文切换次数
 }

你可能感兴趣的:(akka,scala)