3

使用事件驱动架构实现Hashnode博客系统

 2 years ago
source link: https://www.jdon.com/61986
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.
neoserver,ios ssh client

使用事件驱动架构实现Hashnode博客系统
Hashnode 有许多事件驱动的用例,在这篇文章中使用的一个用例是发布一篇文章。如果用户发布帖子,则会启动多个服务:

  • 将此帖子转换为音频帖子
  • 将此帖子备份到用户的 GitHub
  • 向所有订阅者发送时事通讯
  • 添加用户活动
  • 更新文章圈子

什么是事件驱动架构
首先,让我们看看EDA到底是什么,没有它我们会面临哪些问题。

事件驱动架构 (EDA) 通过引入一个称为事件的新实体将服务彼此分离。

事件是一些对象(您可以将其想象为一个简单的 JSON 文档),它将被发送到负责将事件发送给多个消费者的协调器(事件总线)。

对于在 hashnode 的发布,我们可以想象这个事件,例如:

{
"uuid": "eaeb7e90-160d-40a6-841a-74a9bf89210f",
"publication": "1234",
"postId": "5678",
"userIdWhoCalledEvent": "91011",
"hasScheduledDate": false,
"isTeamPub": false
}

架构组件
EDA 解耦系统,为此,我们需要有几个组件。这里的主要组件是:

v2-069d69494d25f83b57c4f50b1b220432_720w.jpg

  • 事件生产者:发送事件的系统 -> 例如 REST API
  • 事件路由器:编排所有事件并启动所需服务的系统
  • 事件消费者:消费事件的服务
  • 事件:消费者正在处理的实际事件

为什么我们需要事件驱动架构?
现在我们知道了事件驱动架构的组成部分。但是,我们需要迁移到 EDA 所面临的问题是什么? 让我们看一下为什么要在 hashnode 上实现适当的 EDA 的前 3 个原因。

性能和后台任务
用户在使用 hashnode 时应该有惊人的体验。其中一个方面当然是性能。这种情况下的性能是一个端点的执行时间。

例如,单击“发布帖子”按钮与您的帖子实际发布之间的时间。

为了理解这一点,了解同步和异步处理之间的区别至关重要。同步处理通常是典型的 REST API。用户单击按钮Publish Post,只要 API 正在处理(进行 DB 调用、标记为 HTML 等),浏览器就会显示加载指示器。

v2-cfe3a228a80eff876ac4bf7cf6262003_720w.jpg



异步处理通常也称为后台处理。用户可以再次与您的应用程序交互,并且不会被阻止继续在您的网站上工作。所有其他任务将在后台处理。 如果我们要添加所有可选服务,例如

  • GitHub 备份

对于我们的同步端点,实际帖子发布并且用户看到它需要很长时间。但是,如果我们只做用户需要的最关键的部分,即发布帖子,并将所有其他任务置于后台,我们将拥有更好的用户体验。 通过引入 EDA,我们允许系统异步处理这些任务,并且完全独立于实际的面向用户的 API。

解耦
第二个主要原因是解耦脱钩。 衡量两个例程或模块的紧密程度 解耦意味着系统和团队可以彼此独立工作,而无需了解更多的接口或 API 规范。 耦合不仅仅是指软件模块,它还可以指许多不同的东西。在此处查看一些示例:

类型例子
技术:Java 与 C++
Temporal:同步与异步
地点:IP、DNS

哈希节点当前状态耦合的一个非常糟糕的情况是事件生产者,即 REST API 需要知道在什么情况下调用哪些服务。

例如,如果在团队博客中发布了一篇文章,并且该博客启用了 GitHub 备份,则 API 需要调用 GitHub 备份服务。

虽然这很简单,但首先,它不能很好地扩展,因为有更多的服务要调用,而且团队中没有更多的开发人员应该拥有他们的服务。 在耦合架构中,它看起来像这样:

v2-4e73880c60c4fae5bb6d08e00c5ac9e2_720w.jpg



虽然这对于一两个服务来说看起来很容易,但随着功能的增加,这将变得越来越复杂。一段时间后,出现很多子模块相互依赖或在某些情况下必须重试的情况。这应该是考虑解耦架构的重点。



使用适当的 EDA,通过让事件的消费者(例如 GitHub 备份服务)订阅某些事件,这会容易得多。让我们看一个例子

v2-fa2d0d9855a23bed13f08f602bffd2d4_720w.jpg



对于所有帖子,徽章分配将开始,因为涉及更多的业务逻辑。

GitHub 备份取决于用户是否在其博客中启用了它。 如果您查看第一个事件,则标志gitHubBackup为false。这意味着该服务不会监听该事件,也不会开始实际备份帖子。

第二个博客已启用gitHubBackup,因此启动了备份服务。 虽然这看起来非常微不足道,但它正确地将事件的生产者和消费者解耦了。作为消费者服务,您可以简单地检查哪些属性可用并为它们创建规则。目标是一个端点简单地触发一个事件postPublished,传递一组预定义的数据,EventBus 和事件消费者订阅这个事件并处理其余的事情。发布者不需要知道任何事情。

扩展性
第三个原因是可扩展性。 
每个在高速增长环境中工作的人都知道这不一定是正确的。 通过解耦系统并实现共享事件类型和添加新事件的适当工作流,实现只监听某些事件类型的服务将变得更加容易。这对于出色的开发体验非常重要。

低耦合 -> 更多开发人员 -> 更好的 DevXP -> 将交付更多

但是通过解耦架构,较小的团队或开发人员可以真正拥有服务,而无需在 6 个不同的地方更改代码。 如果有人想基于某个事件开发服务,那么他们本身并不负责更改整个 API 端点。

好的,我们明白了,EDA 是有道理的。请记住,一切都有取舍。通过引入这种架构,您需要了解如何跨许多不同系统跟踪事件,以便能够了解正在发生的事情。

Hashnode 的当前状态 为了更好地理解 Hashnode 的观点,我想让您了解一下我们当前的架构。

Hashnode 已经并且仍然有巨大的增长。随着巨大的增长和许多新功能,技术债务将自动引入。那也行。但承认这一点并努力应对这些挑战也很重要。

hashnode 的主要 API 是一个运行在虚拟机上的 JavaScript Express Server。 我们已经在使用某种“EDA”,但目前它与实际的 REST API 高度耦合。

对于事件处理,我们使用 Express 的事件功能。

// controlles/post.controller.js
exports.createPost = async function (req, res) {
if (post.partOfPublication && post.publication) {
ee.emit("publication.post.published", {
post: post,
title: post.title,
url: post.url,
user: req.user,
isTeamPub,
});
}
};

在事件方面,我们有一个globalEventListener监听请求:

ee.on('publication.post.published', async function (eventData) {
if (eventData.post.audioBlogEnabled) {
backup();
}
});

虽然这不是很糟糕,但我们天生就有紧密的耦合。

在实际的 API 中,我们检查帖子是否是发表时,检查它是否附加了附件。
如果有,我们会发出事件,在事件监听publication.post.published器中,我们执行该函数内的所有业务逻辑。
在消费者中,我们需要检查是否设置了某些标志,例如音频博客功能并根据它们采取行动。

引入EDA消息总线以后,API 只要发布一个具有预定义类型的事件,即PostPublishedEvent,并告诉 EventBus:“嘿,我发布了一个 ID 为 123 的帖子”。而已!其他一切都会得到照顾。

banq注:其实

post.controller.js中
if (post.partOfPublication && post.publication) {

这个if语句属于业务规则,应该放入领域模型中,发出的事件属于领域事件

 


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK