首页 > 应用开发 > IT综合 > 在线教育知识付费系统源码一键更新五网合一
这篇文章描述了我前段时间在一家以前的公司工作时发生的一个真实情况。这里的主要目标是解释问题和解决方案。我想我可以分享它,以便它也可以帮助将来最终可能遇到同样问题的人。
事不宜迟,让我们开始吧。我正在开发一种创新的知识付费系统源码,需要开发的用例之一是用户有可能知道另一个给定实体正在使用或引用哪些实体(任何类型),这些实体有什么样的关系它们之间,以及其他功能。
完整源码:zx.xcxyms.top
我们的源码系统是使用具有越来越多服务的微服务架构构建的,所以这不是一件容易的事,因为我们遵循Database Per Service模式,一方面它很好,因为它允许我们解耦我们的服务并使用更适合每个数据库的技术(SQL 与 NoSQL),但另一方面,它产生了连接信息的问题,这些信息持久存在于分离的数据库中。简而言之,我们需要创建一种可扩展的、通用的和抽象的信息查询方式,而不管实体类型如何。
问题
我们的领域模型由几个知识付费业务概念组成,但为了简化,让我们使用“Person”和“Movie”等简单概念对问题进行类比。
让我们假设一部电影是由它的标题和发行日期组成的,而一个人是由它的名字和出生日期组成的。关于关系,知识节点可以由一个或多个人执导,一个人可以在一个知识节点或多个知识节点中提现。

图 1. 架构模型
假设我们为每个实体提供服务,people-api并且movies-api通过 API 网关公开。该people-api服务负责通过 REST API 为“Person”实体提供 CRUD 功能,并且该movies-api服务还负责通过 REST API 为“电影”实体提供 CRUD 功能。图 2 中的下图描述了当前架构。

图 2. 初始化架构
一个可能的解决方案是通过 API 网关和聚合响应进行同步的服务间通信,但这会有点难以编码,并且会增加我们服务的耦合,降低内聚性,并且每个服务都必须知道其他 API 合约,损害了拥有微服务架构的全部意义。在这种特殊情况下,它会起作用,是的,但是如果我们要处理数百甚至数千个微服务呢?如果我们有数百个业务概念,它们之间有数百万种不同的关系,并且必须实现每一个可能的查询组合呢?你明白了。
记住前面的问题,我们可以让事情变得更复杂一些。
假设我们在域模型中添加了一个新的“目录”实体。例如,目录可以是电影的集合,它可以有名称和类别。我们的新域模型最终将如图 3 所示。

图 3. 更新的源码模型
为了反映我们的领域模型,我们需要创建一个新的微服务来负责处理我们的新实体。这个新的微服务将被命名为catalogue-api.

然后,我们的架构将变成如图 4 所示的样子。
在这种情况下,用户现在可能想知道哪些目录引用了给定的人。但是,等一下!目录不直接引用一个人。相反,它们参考了知识节点,并且是那些参考人的知识节点。我们现在如何才能实现我们的目标?
解决方案
经过几次长时间的会议和讨论,我们决定通过做一些概念证明来调查我们的问题的一些可能的解决方案,并最终提出了一个可扩展的解决方案,并且不会影响我们以前的解决方案。
我们开始考虑我们的业务域模型,就好像它是一个图,其中节点是实体,边是它们的关系。按照这种想法,图 5 中的下一张图是我们的实体如何建模的示例。

图 5. 图表示例
保持这种思路,我们提出了使用Neo4j数据库提供新服务的想法,该数据库将保留我们所有的实体及其关系。然后,该服务将能够通过利用密码查询语言以任何所需的关系长度遍历知识图,以更友好、更简单的方式进行所有这些复杂的查询。
但是还有一个问题我们还没有弄清楚如何解决:
我们如何聚合所有数据库中持久化的所有数据并将其持久化到一个单一的数据库中,并且仍然保持持续的一致性?
使用发件箱模式更改数据捕获
这个问题的解决方案是使用带有发件箱模式的CDC(变更数据捕获)技术实现异步数据复制机制。我们通过向所有写入事务添加一个新语句来实现这一点,该语句负责生成一个发件箱事件,该事件最终将被Kafka源连接器捕获并放入一个主题中,任何有兴趣了解状态的人正在使用该主题已经改变了,特别是我们的知识库。
根据我们的初始架构,如图 2 所示,然后我们可以将 Kafka 和这个新服务添加到我们的系统中。该服务将被命名knowledge-base并有两个应用程序:knowledge-base-consumer使用发件箱事件,以及knowledge-base-api提供 REST API 来查询实体及其关系。
构建示例项目
下图描述了最终的架构,这正是我在这个小示例项目中实现的以支持本文。KrakenD被用作 API 网关,所有服务都是使用Quarkus框架在Kotlin中编写的。并且每个都有一个MongoDB数据库,而有一个Neo4j数据库。people-apimovies-apiknowledge-base

图 6. 最终架构
每当movies-api或people-api想要对其数据库执行写入操作时,它们还会发出一个带有相应更改的发件箱事件,然后将其放入正在由knowledge-base-consumer. 这种方法保证所有对源数据库的写入最终都会复制到目标数据库(最终一致性)。
如果 Kafka 宕机,更改仍会保留在源数据库的事务日志中,因此没有什么害处,因为一旦 Kafka 恢复正常,连接器将读取事务日志并使用丢失的最新事件填充主题。另一方面,如果消费者宕机,事件仍然会到达 Kafka,因此一旦消费者恢复,它将消费这些事件,从而使整体解决方案具有弹性和容错性。
下面的图 7 有一个小图来解释这种情况下的发件箱模式。
使用 CDC(更改数据捕获)的发件箱模式

图 7. 使用 CDC(变更数据捕获)的发件箱模式
测试项目
在 和 上执行一些 CRUD 操作后people-api,movies-api我们可以看一下在knowledge-base-api的 Neo4j 数据库中持久化的内容的示例,如图 8 所示。
节点和关系示例

图 8. 节点和关系示例
来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/70017807/viewspace-2894228/ ,如需转载,请注明出处,否则将追究法律责任。