Akka扩展
注:本节未经校验,如有问题欢迎提issue
如果想要为Akka添加特性,有一个非常优美而且强大的工具,称为 Akka 扩展。它由两部分组成: Extension 和 ExtensionId.
Extensions 在每个 ActorSystem 中只会加载一次, 并被Akka所管理。 你可以选择按需加载你的Extension或是在 ActorSystem 创建时通过Akka配置来加载。 关于这些细节,见下文 “从配置中加载” 的部分.
警告
由于扩展是hook到Akka自身的,所以扩展的实现者需要保证自己扩展的线程安全性。
构建一个扩展
现在我们来创建一个扩展示例,它的功能是对某件事发生的次数进行统计。
首先定义 Extension 的功能:
1. import akka.actor.Extension
3. class CountExtensionImpl extends Extension {
4. //Since this Extension is a shared instance
5. // per ActorSystem we need to be threadsafe
6. private val counter = new AtomicLong(0)
8. //This is the operation this Extension provides
9. def increment() = counter.incrementAndGet()
10. }
然后需要为扩展指定一个 ExtensionId,这样我们可以获取它的实例.
1. import akka.actor.ActorSystem
2. import akka.actor.ExtensionId
3. import akka.actor.ExtensionIdProvider
4. import akka.actor.ExtendedActorSystem
6. object CountExtension
7. extends ExtensionId[CountExtensionImpl]
8. with ExtensionIdProvider {
9. //The lookup method is required by ExtensionIdProvider,
10. // so we return ourselves here, this allows us
11. // to configure our extension to be loaded when
12. // the ActorSystem starts up
13. override def lookup = CountExtension
15. //This method will be called by Akka
16. // to instantiate our Extension
17. override def createExtension(system: ExtendedActorSystem) = new CountExtensionImpl
19. /**
20. * Java API: retrieve the Count extension for the given system.
21. */
22. override def get(system: ActorSystem): CountExtensionImpl = super.get(system)
23. }
好了!然后我们就可以使用它了:
1. CountExtension(system).increment
或者在Akka Actor中使用:
1. class MyActor extends Actor {
2. def receive = {
3. case someMessage =>
4. CountExtension(context.system).increment()
5. }
6. }
你也可以将扩展藏在 trait 里:
1. trait Counting { self: Actor =>
2. def increment() = CountExtension(context.system).increment()
3. }
4. class MyCounterActor extends Actor with Counting {
5. def receive = {
6. case someMessage => increment()
7. }
8. }
这样就搞定了!
从配置中加载
为了能够从Akka配置中加载扩展,你必须在为ActorSystem提供的配置文件中的 akka.extensions 部分加上 ExtensionId 或 ExtensionIdProvider实现类的完整路径。
1. akka {
2. extensions = ["docs.extension.CountExtension"]
3. }
实用性
充分发挥你的想象力,天空才是极限! 顺便提一下,你知道 Akka的Typed Actor, Serialization和其它一些特性都是以Akka扩展的形式实现的吗?
应用特定设置
可以用 Configuration 来指定应用特有的设置。将这些设置放在一个扩展里是一个好习惯。
配置示例:
1. myapp {
2. db {
3. uri = "mongodb://example1.com:27017,example2.com:27017"
4. }
5. circuit-breaker {
6. timeout = 30 seconds
7. }
8. }
Extension的实现:
1. import akka.actor.ActorSystem
2. import akka.actor.Extension
3. import akka.actor.ExtensionId
4. import akka.actor.ExtensionIdProvider
5. import akka.actor.ExtendedActorSystem
6. import scala.concurrent.duration.Duration
7. import com.typesafe.config.Config
8. import java.util.concurrent.TimeUnit
10. class SettingsImpl(config: Config) extends Extension {
11. val DbUri: String = config.getString("myapp.db.uri")
12. val CircuitBreakerTimeout: Duration =
13. Duration(config.getMilliseconds("myapp.circuit-breaker.timeout"),
14. TimeUnit.MILLISECONDS)
15. }
16. object Settings extends ExtensionId[SettingsImpl] with ExtensionIdProvider {
18. override def lookup = Settings
20. override def createExtension(system: ExtendedActorSystem) =
21. new SettingsImpl(system.settings.config)
23. /**
24. * Java API: retrieve the Settings extension for the given system.
25. */
26. override def get(system: ActorSystem): SettingsImpl = super.get(system)
27. }
使用它:
1. class MyActor extends Actor {
2. val settings = Settings(context.system)
3. val connection = connect(settings.DbUri, settings.CircuitBreakerTimeout)
