微服务和noSQL——在微服务架构中丰富数据的最佳实践

7
我希望计划一个解决方案来管理我的体系结构中的丰富数据。 更明确地说,我有数十个微服务。 例如-国家、建筑物、楼层、工人。 所有这些都在单独的NoSql数据存储上运行。
当我从工人服务获取数据时,我还想呈现楼层名称(工人正在工作的楼层)、建筑物名称和国家名称。
解决方案1。 客户端将查询所有微服务。 问题-多个请求并使客户端了解结构。 我知道多个请求不应该困扰我,但我认为在一个单一的调用中返回描述实体的json更好。
解决方案2。 创建一个编排,从多个服务检索数据。 问题-如果数据(例如实体名称)未存储在DB的同一文档中,则很难按这些字段进行排序和过滤。
解决方案3。 在保存实体(例如工人)之前,调用所有其他服务并填充相关数据(建筑物名称、国家名称)。 问题-当建筑物名称更改时,它不会反映在工人服务中。
解决方案4。 (这是我能想到的最好的解决方案)。 创建一个订阅代理的进程,并接收所有实体更改。 对于每个实体,它更新所有相关实体。 当实体更改时,例如建筑物名称更改,它会更新所有保存建筑物名称的文档。 问题: 每个服务都必须知道可以更新什么。 当发生尾随更新时,它不应再次更新代理(递归更新),因此这可能会使微服务复杂化。
解决方案5。 保持一切规范化。在ElasticSearch中过滤和排序。 问题:在ES中保持规范化数据的性能太昂贵了。
4个回答

2
我看到 Netflix 做的一件事情(我很喜欢),就是为这样的东西创建中间服务。所以也许可以创建一个新的中间服务,调用其他服务来收集所有数据,然后创建带有国家、建筑、楼层和工人的统一输出。
甚至可以更进一步,尝试提供作为输入的哪些资源要包含在输出中的方案。
所以我想这非常符合您的解决方案 2。我注意到您提到对于解决方案 2,存在与数据库排序/过滤相关的问题。我认为,如果您使用 NoSQL,那么必须有一个理由,并且往往是出于性能的原因。我认为,如果做错了,那么确实会有问题,但如果所有可搜索的适当字段都被正确地键入和索引(如 @Roman Susi 在他的 1 和 2 号要点中提到的),那么我不认为这会成为一个问题。是的,这个服务只会像其他服务和数据存储的总和一样快,所以它们必须快速。
现在保留各个微服务,继续让客户端调用一个服务,并将合并数据的复杂性封装到这个新服务中。
这是我在视频中看到的(https://www.youtube.com/watch?v=StCrm572aEs)... 这是一个很长但非常有启发性的视频。

1

很难在解决方案N级别上提供建议,但是以下建议可以避免某些问题:

  1. 为实体使用全局唯一标识符。例如,通过分配键值某种URI。

  2. 全局ID还简化了更新,因为您可以跟踪实际更改的内容,名称或实体。(实体与全局URI具有一对一关系)

  3. CAP定理说您只能从CAP中选择两个。您想要CA架构吗?还是CP?或者可能是AP?这将强烈影响您分发数据的方式。

  4. 对于“排序和过滤”,有MapReduce方法,可以分布式地处理这些事情的负载。

  5. 仔细考虑归一化/去规范化的平衡。如果您的服务在URI上运行,那么您可以拥有一个将URI转换为标签(名称、描述等)的服务,但您不需要在每个地方保留冗余信息并进行更新。不要进行初步优化,而是尽可能长时间地保持数据规范化。这样,工作人员甚至可能不需要建筑物名称,而只需要其全局ID。微服务从另一个微服务查找元数据。

  6. 换句话说,在关注点分离的一部分中,最小化共享服务之间的键数。

  7. 专注于底层模型,而不是JSON的转换。在您的系统中正确建模数据比节省JSON调用更有价值。

关于NoSQL,可以看一下Riak数据库:它具有可调节的CAP属性,如果没有记错的话。即使您不将其用作此类数据库,阅读其文档也可能有助于为分布式微服务系统设计合适的体系结构。(当然,这仅适用于本质上是并行系统的情况)

谢谢。您能详细说明一下mapReduce方法吗?目前在规范化的方法中,我需要对我的elasticSearch进行多次查询。 - Bick
你在谈论每个服务的NoSQL存储。MapReduce是将计算分布在这些存储之间(如果负载很大且数据量很大)。ES是集中式解决方案,如果它符合您的需求,为什么要去中心化和使用NoSQL?一切都可以放在一个地方(关系型数据库,NoSQL或其他)+适当复制。不知道操作规模很难说。 - Roman Susi
我本不想提及,但一开始我就认为RDF / SPARQL解决方案也可能适合您的需求,因为您无需特别麻烦地从服务中联合数据:多个来源可以在一个SPARQL查询中组合。ES用于索引文本数据。 - Roman Susi

1
首先,感谢您的提问。这与文档数据库的主要问题类似:如何从另一个集合中按字段对集合进行排序?我有自己的答案,所以我会尝试评论您所有的解决方案:
解决方案1:如果客户端想独立处理国家/建筑物/楼层,那么这很好。但是,它并没有解决您在解决方案2中提到的问题-按建筑物对10k个工人进行排序会很慢。
解决方案2:如果客户端只想要一个列表,而不知道如何从多个部分组合它,则与解决方案1类似。
解决方案3:正如您所说,由于数据不一致而无法接受。
解决方案4:大部分时间都可以工作。但是:
  • 大量数据重复。如果您有20个实体,您将拥有x20数据。
  • 复杂性高。20个实体-> 20个不同的程序来更新相关数据
  • 高内聚性。所有服务都必须相互了解。由于更新程序,数据模型更改将传播到每个服务
  • 可靠性值得怀疑。可以这样做,因此数据在故障后将是一致的,但这并不容易
解决方案5:有点回答 :-)

但是-你不需要所有的东西。将服务分离,为分离的实体提供服务,并在其上构建其他服务。

如果客户需要丰富的数据-构建返回丰富数据的服务,就像解决方案2一样。

如果客户想要显示带有过滤和排序的丰富数据列表-构建一个提供带有过滤和排序功能的丰富数据的服务!很可能,这种服务的实现将包含ES实例,其中包含来自较低级别服务的缓存和索引数据。重点在于ES不必包含所有内容或在每个服务之间共享-由您决定更好的平衡性能和基础设施资源。


1

这是一个链接数据可以帮助您的案例。

基本上,工人的Floor属性将是指向楼层本身的URI(链接)。任何其他链接数据也应表示为URI。

使用一些JSON-LD进行建模,它会像这样:

worker = {
  '@id': '/workers/87373',
  name: 'John',
  floor: {
    '@id': '/floors/123'
  }
}

floor = {
  '@id': '/floor/123',
  'level': 12,
  building: { '@id': '/buildings/87' }
}

building = {
  '@id': '/buildings/87',
  name: 'John's home',
  city: { '@id': '/cities/908' } 
}

这样客户端只需要将基本URL(如api.example.com)附加到@id并进行简单的GET调用即可。
为了减轻客户端的额外调用负担(如果是慢速移动设备),我们使用带有微服务的网关模式。 网关可以轻松扩展这些链接并增强返回对象。 它还可以并行进行多个调用。
因此,网关将进行GET /floor/123调用,并将楼层对象替换为回复中的对象。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接