在事件驱动的微服务架构中处理更改


9

我正在做一个研究项目,正在研究处理事件驱动的微服务体系结构中的更改的选项。

因此,假设我们有一个应用程序,其中有四个不同的服务。这些服务中的每一个都有自己的数据库来存储本地数据。

在此设置中,这四个服务使用事件总线相互通信。因此,当服务中发生某些事情时,它会发布一个事件。所有对该事件感兴趣的其他服务都将以自己的方式对其进行处理。

在那种情况下,架构中的不同服务需要就这些事件(属性等)的内容订立“合同”。因此,服务对这些事件具有“松散耦合的依赖性”

我的问题是: 我们如何应对这些事件的变化?

因此,假设服务A在应用程序中注册了新用户。因此,它发送一个“ UserRegistered”事件,服务B拾取该事件并对其进行处理,但是服务C小组的一些开发人员决定他们也需要注册用户的性别,因此该事件被更改,属性性别被添加到“ UserRegistered”事件。

我们如何确保服务B仍可以在不重新部署的情况下使用该额外属性来拾取同一事件?

还有其他方法可以解决此问题,然后对这些事件进行版本控制吗?


您的消息是什么格式,或者可以设计吗?一些消息格式允许使用可选属性。根据阅读器的实现,您可以添加可选属性,而无需更新所有阅读器。
汤玛斯·欧文斯

我可以自由选择用于邮件的格式。我认为使用JSON是最好的方法。这些不同的服务以不同的语言构建很重要。这就是为什么需要诸如XML或JSON之类的通用格式的原因。
CGeense

Answers:


1

事件与变化无关。他们是关于什么时候改变。

我可以创建一个与更改内容完全脱钩的事件系统。这样,我从事件中学到的就是对象已更新。如果我什至不关心对象已更新,那么我将告诉任何知道如何与该对象对话的对象,询问它发生了什么变化。

那不能解决传达这些变化的问题。它只是阻止它成为事件系统的一部分。

解决数据不同版本问题的一种方法的一个示例是让观察者创建并向观察对象传递一个集合。被观察的对象将使用其最新数据填充集合,并且当控件返回时,您(观察者)将获得所需的东西。如果还有其他您不关心的东西(因为您从未听说过),则只需忽略它。

有很多其他方法可以给猫咪贴皮,但这正是我在这种情况下所做的工作。


这会不会大大增加服务之间的流量?使用问题中的示例,一个UserRegistered事件,如果有一个事件不包含有关用户的信息,则将有1条发布的消息发送到总线,然后有{对感兴趣的服务的数量}个对用户服务的请求或发布的消息去公交车。然后,将返回各种大小的{感兴趣的服务数量}条消息。尽管我确实认为这可能是纸上比较干净的设计,但是如果要考虑性能,则它会在任何非平凡的系统中崩溃,尤其是在网络上。
Thomas Owens

@ThomasOwens与事件一起发送数据意味着如果我有N个观察者,我将发送N条消息。仅发送事件意味着我发送了3N条消息,其中只有1条具有数据包。即使通过网络也可以很好地扩展。唯一的重大缺点是它会使您的延迟增加三倍。并不是说您无法找到针对特定情况的最佳解决方案。我要说明的是事件系统和数据版本不必耦合。
candied_orange

1
此事件总线的整体思想是将不同的服务解耦。通过使用一块中间件,我们可以确保这些服务彼此不认识,并且可以在不知道彼此存在的情况下存在并进行通信。如果我们从这些事件中删除状态并让服务直接相互连接,则我们正在耦合这些服务。这样,我们永远无法重新部署一项服务,而不必重新部署其余服务
CGeense

1
这里的重点是事件系统是否需要可扩展的数据。如果您不更改以前建立的名称或结构,则JSON或XML可以很好地解决问题。我对集合也做了同样的事情。事件系统不应该关心性别。如果它正在发送数据,则应将其传递出去,而另一端的某个对象将关心性别或不关心性别。
candied_orange

1

诸如NServiceBus之类的框架通过将事件版本控制与多态消息分发一起使用来处理此问题。

例如,服务A的版本1可能将事件发布为IUserRegistered_v1。当Service A 1.1版需要包含其他字段时,它可能会声明接口IUserRegistered_v1_1,该接口将从IUserRegistered_v1继承并声明一些其他字段。

当服务A发布IUserRegistered_v1_1事件时,NServiceBus会将消息分派到处理IUserRegistered_v1或IUserRegistered_v1_1的所有终结点。


0

增量改进

对模型的简单更改是,当侦听器注册为观察者时,它们将包含他们希望了解的数据元素的列表或其他结构。如果从服务返回的数据很简单,则可以使用此方法,但是如果您有大量的分层数据,则实现起来会非常复杂。

坚如磐石

如果您真的想要一种健壮的方法来进行此服务设计,则该服务应保留对其存储的数据所做的更改的历史记录。本质上,您永远不会更新数据库中的记录,而是在每个记录代表更改的地方添加新记录。这些新记录中的每一个都与标识该操作的事件ID相关联。均匀记录与有关更改的所有相关信息(谁,什么,什么时候等)一起存储。这还具有一些其他好处,这些好处不在此答案的范围内,但本文将讨论CAP定理

进行更改后,您将创建事件记录并将所有新数据添加到数据库中。然后,您将一个事件发布给包含(最少)事件ID的侦听器。然后,侦听器可以请求与该ID关联的数据,并获取与该ID关联的数据的版本。这样,每个侦听器都可以获取所需的任何内容,而无需将其他不同的侦听器的需求耦合在一起。我建议您将最常用数据字段的子集添加到事件消息中,以便侦听器可以过滤掉他们不感兴趣的事件。这可以减少过程的闲聊,并且某些侦听器可能永远不需要调用回来。这也可以保护您免受时间问题。如果您只是回拨服务并根据密钥获取数据,在获取事件和为其获取数据之间可能还会发生其他更改。这可能对所有侦听器都无关紧要,但是如果您需要了解所有更改,则可能会造成重大问题。如果您确实希望将其更改为11,则上述增量设计改进与该方法兼容。

对于您需要做的事情,这可能有些过头了,但是根据我的经验,如果您没有一种精确的方法来查看记录随着时间​​的变化,您或使用数据的人最终会想要它。


-1

@CandiedOrange在评论他自己有关可扩展数据格式(如xml)的答案时提出了一个正确的观点。

只要添加数据就没有关系。但是,请为较旧的事件/非必填字段提供合理的默认值。

您只需要更新关心的服务-在这种情况下就是-性别。xml / json解析器应该能够忽略其他服务的额外数据。当然,这取决于您对解析器和事件数据格式的选择。

我不同意没有相关数据的事件。对于事件源,事件应定义发生了什么变化。接收到事件后,其他服务就不必从事件源中检索数据。


我的观点是,它需要处理各种变化。考虑广播事件的服务。并且该事件包含一个过时的属性,应将其删除。即使这些其他服务未使用该属性,也会因为他们期望它而破坏它们。因此,我在martinfowler.com上阅读了有关消费者驱动合同的文章: martinfowler.com/articles/consumerDrivenContracts.html 在应用此原理时。每个提供者(事件)都知道他的期望。利用该信息,他可以验证他是否破坏了任何消费者。
CGeense
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.