extending-akka.md 5.8 KB
Newer Older
颯沓如流星's avatar
颯沓如流星 已提交
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187
# Akka 扩展

如果你想向 Akka 添加特性,有一个非常优雅但功能强大的机制来实现这一点。它被称为 Akka 扩展(`Extensions`),由两个基本组件组成:`Extension``ExtensionId`

每个`ActorSystem`只加载一次扩展,由 Akka 管理。你可以选择按需加载扩展,也可以通过 Akka 配置在`ActorSystem`创建时加载扩展。有关如何实现这一点的详细信息,请参阅下面的“从配置中加载”部分。

- **警告**:由于扩展是一种钩住 Akka 本身的方法,因此扩展的实现者需要确保其扩展的线程安全。

## 建立扩展

现在,让我们创建一个示例扩展,用于计算某些事情的发生次数。

首先,我们定义`Extension`应该做什么:

```java
import akka.actor.*;
import java.util.concurrent.atomic.AtomicLong;

static class CountExtensionImpl implements Extension {
  // Since this Extension is a shared instance
  // per ActorSystem we need to be threadsafe
  private final AtomicLong counter = new AtomicLong(0);

  // This is the operation this Extension provides
  public long increment() {
    return counter.incrementAndGet();
  }
}
```

然后,我们需要为扩展创建一个`ExtensionId`,这样我们就可以获取它。

```java
import akka.actor.*;
import java.util.concurrent.atomic.AtomicLong;

static class CountExtension extends AbstractExtensionId<CountExtensionImpl>
    implements ExtensionIdProvider {
  // This will be the identifier of our CountExtension
  public static final CountExtension CountExtensionProvider = new CountExtension();

  private CountExtension() {}

  // The lookup method is required by ExtensionIdProvider,
  // so we return ourselves here, this allows us
  // to configure our extension to be loaded when
  // the ActorSystem starts up
  public CountExtension lookup() {
    return CountExtension.CountExtensionProvider; // The public static final
  }

  // This method will be called by Akka
  // to instantiate our Extension
  public CountExtensionImpl createExtension(ExtendedActorSystem system) {
    return new CountExtensionImpl();
  }
}
```

现在我们需要做的就是实际使用它:

```java
// typically you would use static import of the
// CountExtension.CountExtensionProvider field
CountExtension.CountExtensionProvider.get(system).increment();
```

或者从 Akka Actor 的内部:

```java
static class MyActor extends AbstractActor {
  @Override
  public Receive createReceive() {
    return receiveBuilder()
        .matchAny(
            msg -> {
              // typically you would use static import of the
              // CountExtension.CountExtensionProvider field
              CountExtension.CountExtensionProvider.get(getContext().getSystem()).increment();
            })
        .build();
  }
}
```

就这些了!

## 从配置中加载

为了能够从 Akka 的配置中加载扩展,必须在提供给`ActorSystem`的配置的`akka.extensions`部分中添加`ExtensionId``ExtensionIdProvider`实现的 FQCN。

```yml
akka {
  extensions = ["docs.extension.ExtensionDocTest.CountExtension"]
}
```

## 适用性

一切都是可能的!顺便问一下,你知道 Akka Typed Actor、序列化和其他特性是作为 Akka 扩展实现的吗?

### 应用程序特定设置

该「[配置](https://doc.akka.io/docs/akka/current/general/configuration.html)」可用于特定于应用程序的设置。一个好的实践是将这些设置放在扩展中。

配置示例:

```yml
myapp {
  db {
    uri = "mongodb://example1.com:27017,example2.com:27017"
  }
  circuit-breaker {
    timeout = 30 seconds
  }
}
```

`Extension`示例:

```java
import akka.actor.Extension;
import akka.actor.AbstractExtensionId;
import akka.actor.ExtensionIdProvider;
import akka.actor.ActorSystem;
import akka.actor.ExtendedActorSystem;
import com.typesafe.config.Config;
import java.util.concurrent.TimeUnit;
import java.time.Duration;

static class SettingsImpl implements Extension {

  public final String DB_URI;
  public final Duration CIRCUIT_BREAKER_TIMEOUT;

  public SettingsImpl(Config config) {
    DB_URI = config.getString("myapp.db.uri");
    CIRCUIT_BREAKER_TIMEOUT =
        Duration.ofMillis(
            config.getDuration("myapp.circuit-breaker.timeout", TimeUnit.MILLISECONDS));
  }
}

static class Settings extends AbstractExtensionId<SettingsImpl> implements ExtensionIdProvider {
  public static final Settings SettingsProvider = new Settings();

  private Settings() {}

  public Settings lookup() {
    return Settings.SettingsProvider;
  }

  public SettingsImpl createExtension(ExtendedActorSystem system) {
    return new SettingsImpl(system.settings().config());
  }
}
```

使用它:

```java
static class MyActor extends AbstractActor {
  // typically you would use static import of the Settings.SettingsProvider field
  final SettingsImpl settings = Settings.SettingsProvider.get(getContext().getSystem());
  Connection connection = connect(settings.DB_URI, settings.CIRCUIT_BREAKER_TIMEOUT);
}
```

## 库扩展

第三方库可以通过将其附加到其`reference.conf`中的`akka.library-extensions`来注册它的扩展,以便在 Actor 系统启动时自动加载。

```java
akka.library-extensions += "docs.extension.ExampleExtension"
```

由于无法有选择地删除此类扩展,因此应小心使用,并且仅在用户不希望禁用此类扩展或不支持禁用此类子功能的情况下使用。这一点很重要的一个例子是在测试中。

- **警告**:永远不要分配`akka.library-extensions= ["Extension"]`,因为这样会破坏库扩展机制并使行为依赖于类路径顺序。


----------

**英文原文链接**[Akka Extensions](https://doc.akka.io/docs/akka/current/extending-akka.html).


----------
颯沓如流星's avatar
颯沓如流星 已提交
188
———— ☆☆☆ —— [返回 -> Akka 中文指南 <- 目录](https://codechina.csdn.net/monokai/akka-guide/blob/master/README.md) —— ☆☆☆ ————