# 二进制兼容规则

Akka 维护并验证模块版本之间的向后二进制兼容性。

在本文档的其余部分中，每当提到“二进制兼容性”时，都意味着“向后二进制兼容性”，而不是向前兼容性。

这意味着，只要你的构建不启用`inliner`（仅限 Scala 的限制），新 JAR 就是旧 JAR 的一个替代品（但并非相反）。

## 二进制兼容性规则的解释

二进制兼容性保持在：

- **次要版本**和**补丁版本**请注意，“次要”的含义已经转变为对 Akka 2.4.0 更严格的含义，有关详细信息，请阅读「[版本控制方案的变化]()」。

以下两者之间**不**保持二进制兼容性：

- **主要版本**
- 任何版本的"可能改变"模块，请参阅「[模块标记为“可能改变”](https://codechina.csdn.net/monokai/akka-guide/blob/master/articles/additional/may-change.md)」
- 下面解释了一些值得注意的例外情况

具体示例（请阅读「[版本控制方案的变化]()」，了解“2.4 之前”和“2.4 之后”的区别）：

```yml
# [epoch.major.minor] era
OK:  2.2.0 --> 2.2.1 --> ... --> 2.2.x
NO:  2.2.y --x 2.3.y
OK:  2.3.0 --> 2.3.1 --> ... --> 2.3.x
OK:  2.3.x --> 2.4.x (special case, migration to new versioning scheme)
# [major.minor.patch] era
OK:  2.4.0 --> 2.5.x
OK:  2.5.0 --> 2.6.x
NO:  2.x.y --x 3.x.y
OK:  3.0.0 --> 3.0.1 --> ... --> 3.0.n
OK:  3.0.n --> 3.1.0 --> ... --> 3.1.n
OK:  3.1.n --> 3.2.0 ...
     ...
```

### 不保留二进制兼容性的情况

如果在 Akka 或 Akka 的短暂依赖中报告了安全漏洞，并且在不破坏二进制兼容性的情况下无法解决，则修复安全问题更为重要。在这种情况下，在发布次要版本时可能不会保留二进制兼容性。这种例外情况会在发布公告中注明。

一些模块被排除在二进制兼容性保证之外，例如：

- `*-testkit`模块，因为这些模块仅用于测试，通常重新编译并按需运行。
- `*-tck`模块——因为它们可能想要添加新的测试（或强制配置某些东西），以便发现 TCK 应该测试的现有实现中可能出现的故障。这里的兼容性并不能保证，但是它试图使升级过程尽可能顺利。
- 所有被标记为「[可能改变](https://codechina.csdn.net/monokai/akka-guide/blob/master/articles/additional/may-change.md)」模板，根据定义，这些模块会受到快速迭代和变化的影响。

### 何时将完全删除不推荐使用的方法

一旦一个方法被废弃，那么准则是，它将至少保留一个完整的次要版本发布。例如，如果在版本 2.5.2 中弃用它，那么它将一直保留到 2.5 的其余部分，以及整个 2.6。

这是一个指导原则，因为在极少数情况下，经过仔细考虑，可能会出现异常，并且之前删除了该方法。

## 版本控制方案的变化，自 2.4 以来兼容性更强

自从 Akka 2.4.0 发布以来，一个新的版本控制方案已经生效。

历史上，Akka 一直遵循 Java 或 Scala 的版本控制风格，因为第一个数字意味着`epoch`，第二个意味着`major`，第三个意味着`minor`，因此：`epoch.major.minor`样式的版本控制方案一直沿用到 2.3.x。

目前，自 Akka 2.4.0 以来，新的版本控制应用于语义版本控制，这更接近于许多人所期望的，版本号被解译为`major.minor.patch`。这也意味着 Akka 2.5.x 与 2.4 系列版本是二进制兼容的，除了被标记为"可能改变"的 API。

除此之外，Akka 2.4.x 已被制成与 2.3.x 系列兼容的二进制文件，因此没有理由保留在 Akka 2.3.x 上，因为升级是完全兼容的（此后许多问题已得到解决）。

## 不允许混合版本控制

在 Akka 项目下一起发布的模块将一起升级。例如，将 Akka Actor 2.4.2 与 Akka Cluster 2.4.5 混合是不合法的，尽管两者是二进制兼容的。

这是因为模块可能会假设跨模块边界的内部变更，例如集群中的某些特性可能需要在 Actor 中进行内部变更，但是它不是公共 API，因此这种变更被认为是安全的。

- **注释**：我们建议在构建文件中保留一个`akkaVersion`变量，并将其重新用于所有包含的模块，因此升级时，只需在这一个位置更改它即可。

## “可能改变”的含义

模块描述和文档中使用了"可能改变"，以表示它们包含的 API 可能会在没有任何事先警告的情况下发生更改，并且不包含在二进制兼容性承诺中。可以通过阅读「[模块标记为“可能改变”](https://codechina.csdn.net/monokai/akka-guide/blob/master/articles/additional/may-change.md)」了解更详细的信息。

## API 稳定性注解和注释

Akka 向最终用户提供了非常强的二进制兼容性承诺。但是，Akka 的某些部分被排除在这些规则之外，例如，内部或已知的不断发展的 API 可能被标记为这样，并作为整体稳定模块的一部分进行装运。一般来说，任何破坏都是通过反预测和方法添加来避免和处理的，但是某些已知尚未完全冻结（或完全内部）的 API 被标记为这样，并且随时可能更改（即使尽最大努力保持它们的兼容性）。

### INTERNAL API 和 @InternalAPI 标记

当浏览源代码和/或寻找可调用的方法时，尤其是从 Java 中，它没有像 Scala 那样具有丰富的访问保护系统，有时你可能会发现用`/** INTERNAL API */`注释或`@akka.annotation.InternalApi`注解的方法或类。

这些类没有兼容性保证。它们可能会在小版本中更改甚至消失，用户代码不应该调用它们。

Akka 在其内部广泛使用的 Scala `private[akka]`模式的细节：在 Scala 中充当“只能从给定包访问”的方法或类在原始 Java 字节码中被编译为`public (!) `。Scala 理解的访问限制作为元数据存储在类文件中。因此，这样的方法被安全地禁止从 Scala 访问，但是 Java 用户不会被`javac`编译器警告这一事实。请注意这一点，不要调用内部 API，因为它们可能在没有任何警告的情况下发生更改。

### @DoNotInherit 和 @ApiMayChange 注解

除了特殊的内部 API 注解之外，Akka 中还存在两个注解，并专门处理以下用例：

- `@ApiMayChange`，它标志着已知尚未完全稳定的 API。
- `@DoNotInherit`，它标志着 API 是在封闭世界的假设下设计的，因此不能扩展到 Akka 之外，否则这样的代码将面临二进制不兼容的风险。例如，可以使用此注解标记接口，虽然类型是公共的，但并不意味着由用户代码进行扩展。这允许向这些接口添加新方法，而不会冒破坏客户端代码的风险。此类 API 的示例是`FlowOps`特性或 Akka HTTP 域模型。

请注意，在必须更改 API 时总是采取最努力的方法，并且尽可能避免破坏，但是这些注解允许试验、收集反馈并稳定我们可以构建的最佳 API。

## 二进制兼容性检查工具链

Akka 使用了 LightBend 维护的「[迁移管理器](https://github.com/lightbend/mima)」，简称`MiMa`，用于强制实现二进制兼容性，并将其保持在承诺的位置。

所有`Pull`请求都必须通过 MiMa 验证（这是自动发生的），如果检测到失败，例如，如果更改发生在内部 API 中，则可以进行手动异常覆盖。

## 跨 Scala 版本的序列化兼容性

Scala 不维护跨主要版本的序列化兼容性。这意味着，如果使用 Java 序列化，则如果用 Scala 的不同版本序列化，则不能保证对象可以被反序列化。

内部的 Akka Protobuf 序列化程序可以通过`enable-additional-serialization-bindings`显式启用，也可以通过`akka.actor.allow-java-serialization = off`隐式启用（从安全的角度来看，这是可取的），这都不会受到序列化兼容性问题的影响。





----------

**英文原文链接**：[Binary Compatibility Rules](https://doc.akka.io/docs/akka/current/common/binary-compatibility-rules.html).



----------
———— ☆☆☆ —— [返回 -> Akka 中文指南 <- 目录](https://codechina.csdn.net/monokai/akka-guide/blob/master/README.md) —— ☆☆☆ ————