如何在微服务之间最好地共享和存储类似枚举的数据?

2022-09-04 22:15:41

情况:具有以下特征的微服务体系结构:

  • 微服务是用Java编写的,带有Spring Boot和Hibernate
  • 种选项服务,通过 REST 接口提供有关选项s 的信息
  • 选项数据可以按如下方式建模,例如通过枚举(代表性)
    • 具有一些与之关联的状态等属性。将来不太可能添加更多属于 Option 的属性,这些属性必须与它直接绑定。enabled
    • 新的选项s 和选项类型必须很少添加。它们永远不会被删除(如果需要,只是禁用)。
    • 选项应具有可引用的唯一标识符
    • 如果可能的话,不应该有UNKOWN选项
enum OptionType {
   TYPE_A,
   TYPE_B,
   TYPE_C;
}

enum Option {
    TYPE_A_X1(TYPE_A),
    TYPE_A_X2(TYPE_A),

    TYPE_B_Z1(TYPE_B, false),
    TYPE_B_Z2(TYPE_B);

    TYPE_C_U1(TYPE_C);
    TYPE_C_U2(TYPE_C);
    TYPE_C_U3(TYPE_C);

    private final OptionType type;
    private final boolean enabled;

    Option(OptionType type){
        this.type = type;
        this.enabled = true;
    }

    Option(OptionType type, boolean enabled){
        this.type = type;
        this.enabled = enabled;
    }
}

  • 其他微服务(当前为 3 个)需要能够访问 Option 数据。他们需要知道哪个选项存在,并以某种方式引用选项,例如通过其名称或标识符
    • 其中一个服务(示例服务)需要向外部世界提供 Option 数据类型作为其自己的 REST 接口中的筛选器设置。JSON 中的筛选器对象将如下所示:
{
  "typeA": "TYPE_A_X1",
  "typeB": "TYPE_B_Z2",
  "typeC": [ "TYPE_C_U1", "TYPE_C_U2"]
  // more filter settings
}

在我看来,在微服务之间存储和共享此选项数据的不同方法:

  • 选项服务选项数据存储在其自己的数据库中。如果我将数据从数据库读取到我的休眠实体中,则选项只是从那里开始的任何地方。String

    • 优点:
      • 易于重命名,添加和删除选项
    • 缺点:
      • 在代码中使用 Option 时没有类型安全性,例如,当取消包含 Option 的响应时
      • example-service 无法在其 OpenAPI 文档中轻松提供 Option 数据(仅 s)String
      • 微服务需要从选项服务中查询和缓存选项数据
  • 选项数据仅存在于例如上面建模的源代码中,并通过lib在不同服务之间共享。enum

    • 优点:
      • 在需要选项s 的任何地方键入安全。在反序列化包含 Options 数据的响应时非常有用,但对于生成 OpenAPI 文档也非常有用
      • 微服务仍可以通过其名称引用其数据库中的 Option,因为它是唯一的
    • 缺点:
      • 编辑选项的名称很困难
      • 无法删除选项
      • 如果添加了新的 Option/OptionType,则依赖于该库的服务更新其库版本的顺序很重要。由于我们无法将响应反序列化为未知选项类型。
  • 还有混合数据库和枚举解决方案的可能性,这带来了一个很大的缺点,即必须维护两个事实来源。

在微服务之间存储和共享 Option 数据的最佳方式是什么?什么是最佳实践?


答案 1

虽然枚举在大多数编程语言中非常有用,但它们可能成为服务间通信的噩梦。如果在微服务终结点中使用枚举,它将成为 API 规范的一部分。在一端更改它们会导致另一端的序列化/反序列化问题。因此,您应该只在 API 中使用枚举,如果

  • 您绝对确定它们永远不会改变(例如工作日)或
  • 您是所有参与服务的代码的所有者,并且能够协调大爆炸重新部署。

在这些情况下,您可以在服务之间创建共享库。

如果这不适用,请让您的生活更轻松,并将大多数类似枚举的数据(例如货币代码,国家/地区代码)视为容易更改的简单字符串值。如果要对这些值进行集中管理,为什么不为其创建一个充当主节点的微服务呢?这就是微服务架构的用途。提供其他服务可以不时查询的一些终结点。您甚至不需要持久性层。


答案 2

另一个选项 :使用第一个选项,但使用专门的类。

  • 这允许类型安全,
  • 但不要求在编译时知道枚举。
  • 不过,如果您需要检查实例的有效性,则需要一个已缓存允许列表的服务。OptionOption

示例代码:

import com.fasterxml.jackson.annotation.JsonValue;

public class Option {
    @JsonValue
    private String value;
        
    Option(String value) {
        this.value = value;
    }
}