使用分布式和可扩展的架构时,通常需要最终的一致性。
以图形方式,如何处理这种最终的一致性?
用户习惯点击保存,即时查看结果......最终的一致性是不可能的。
如何处理此类场景的GUI?
请注意,该问题适用于桌面应用程序和Web应用程序。
PS:我正在使用微软平台,但我想这个问题适用于任何技术......
答案 0 :(得分:7)
Task Based UI很适合这个模型。您可以从UI创建和执行任务。您还可以使用任务状态监视器来在任务执行时向用户显示。
另一种选择是使用来自客户端的某种池。您从客户端发送命令和池,直到命令完成并且新数据可用。在某些情况下,从用户按下保存到他将看到新记录时,您会有延迟,但在大多数情况下,它应该几乎是同步的。
另一个(好的?)选项是假设/设计不失败的命令。这并非易事,但您可以在客户端上拥有缓存,并将命令中的数据添加到该缓存,并在执行命令之前将其显示给用户。如果命令因某些意外情况而失败,那么只需设计一个好的“我们很抱歉”的消息,误导用户几秒钟。
您还可以合并上述方法。
通常,最终的一致性更多是业务/域名问题,您应该让域专家处理它。
答案 1 :(得分:3)
有两种方式:
我更喜欢第一种选择。
答案 2 :(得分:2)
正如有人已经提到的那样,基于任务的UI非常适合这种情况,而我要做的就是采用一种技术,为命令传播“花时间”。
例如,假设我们在列表屏幕上,用户可以在其中执行各种操作,其中之一是将新项目添加到列表中。选择添加项目后,您可以显示“您下次要做什么?”可以有“添加另一个项目”,“执行此任务”,“执行其他任务”,“返回列表”。
当他们点击某个选项时,数据可能会被刷新。
此外,如果您使用的是基于任务的UI,则可以分析任务执行的模式,并使用这些“您希望接下来做什么”屏幕来简化UI。类似于亚马逊的“其他人也购买了这些物品”。
答案 3 :(得分:2)
我认为其他答案通常将CQRS与最终的一致性混合在一起。基于任务的UI非常适合CQRS,但它不能解决最终一致的读取模型的问题。
首先,我想质疑你的陈述:
用户习惯点击保存,即时查看结果......最终的一致性是不可能的。
你是做什么的?为什么不能立即看到结果?我认为这里的问题是你的结果定义。
任何操作的结果都是该操作已执行。有很多方法可以展示这个!这取决于您想要完成的动作类型。例子:
发送电子邮件:如果用户输入了正确的电子邮件地址,则几乎可以保证该操作将成功完成。为了防止意外故障,可以使用持久队列,因为这种操作不需要同步完成。所以你只需说"发送电子邮件"。通常,当您要求重置密码时,会看到此类响应。
更新用户配置文件中的一些信息:在客户端验证新数据后,很可能命令也会成功,因为唯一可能发生的事情是数据库错误(如果使用数据库)。同样,通过使用持久队列可以减轻这种情况。在这种情况下,您只需以相同的形式显示更新的字段。 SPA的良好做法是在客户端拥有一个全面的数据存储,就像Redux一样。在这种情况下,您可以通过发送命令并更新客户端存储来安全地更新服务器,这将导致UI显示最新数据。 免责声明:有些答案将此技术称为"欺骗用户",但我不同意此定义。
如果您有容易出错的命令,则可以使用其他答案(如Websockets或服务器端事件)中已描述的技术来回复错误。这需要相当多的额外工作。您还可以发送命令并等待回复或同步执行命令。有人会说"这不是CQRS"但这只是另一个受到挑战的教条。确保命令已完成执行并结合前一点(客户端数据存储)将是一个很好的解决方案。
我不确定是否有任何100%防弹技术允许您始终显示读取模型中的非陈旧数据。我认为这违反了CQRS的原则。即使使用实时事件,您也只能获得表明您已编写模型的事件已更新。尽管如此,你的预测可能会失败,对此的反应是另一回事。
但是,我不会在这个问题上集中精力。事实是,经过充分测试的预测和几乎可以保证的命令将非常有效。对于90%的情况下的错误处理,只需要一些手动或半手动过程就可以从这些错误中恢复。对于最后10%,你可以结合泛型"错误"从服务器推送的消息说"抱歉,您的操作XXX未能执行"最重要的行动可能会有一些创造性的过程,但实际上这些情况非常罕见。
答案 4 :(得分:1)
如前所述,可以告诉用户请求(命令)已已确认(已成功已发布强>)。如果发生某些故障,系统应通过以下方式将此信息传达给请求者:
,例如,邮件客户/服务:
我认为告知用户最近失败的好方法是在他浏览应用程序时向他提供错误面板。可能需要用户手势才能解除该警报等。
例如:
答案 5 :(得分:1)
我不会欺骗用户或阻止他做出其他一些动作。在读取端确认之后,我宁愿将数据流传输到UI。让我们考虑这两种情况:
用户保存数据并期望结果。建立与服务器的连接。在读取方面对它们进行确认后,它们将流向UI并且UI正在更新。
用户保存数据并刷新网页。在重新加载时,正从数据存储中提取数据并建立用于流的连接。如果读取方在此期间没有更新数据存储,那么仍然有一个打开的流,并且在数据到达读取端之后应该更新UI。
为什么从读取端流式传输而不是直接从写入端流式传输?简单来说,这将确认已达到阅读方面。 从技术方面来说,可以使用Server-Sent Events。
缺点:
读取方仍未立即反映结果。但至少在大多数情况下,用户将能够继续他的工作,而不会被UI阻止。
答案 6 :(得分:0)
有几种方法可以处理最终的一致性。从用户的动作到后端刷新,所有这些实际上都是要占用的时间。
用户读取:给定的用户只能从他们写入的同一数据库节点中读取。其他用户从复制的节点读取。优点:界面足够快,并且应用程序保持同步。缺点:您的服务体系结构必须跟踪用户并将其路由到特定的数据库节点。
禁用用户界面,直到操作完成,然后刷新它。 Java Server Faces就是一个典型的例子。可以使用加载微调器创建一个模式,以覆盖UI,直到刷新完成。优点:用户界面与应用程序状态保持同步。缺点:大多数操作都会创建被阻止的用户界面。用户对受限的UI感到非常沮丧,并且会抱怨应用程序运行缓慢。
确认,立即感谢用户的提交。然后,让他们知道操作(是否完成)(电子邮件,SMS,应用内通知)。优点:很快就可以了。缺点:UI滞后于系统,直到刷新。即使有通知,用户也可能会困惑,因为他们看不到更新。它还需要整合各种通信渠道。用户不会立即看到他们的更改。如果操作失败,他们可能不知道为时已晚。
伪造乐观地认为该操作将完成。向用户显示最终的用户界面(验证,评论,信用卡确认等),并允许他们像成功一样继续进行操作。如果出现故障,请立即将其显示为上下文错误:撤消否决旁的警报,带有失败评论的帖子中的应用内警报,信用卡被拒绝的电子邮件。优点:UI感觉更快。缺点:UI暂时与应用程序状态不同步,您必须解决该问题。一种情况:您可能会伪造带有临时ID的内容。但是,在创建内容之后,直到刷新之前,临时ID都是错误的。第二种情况,您可能需要在操作之后直到刷新之前将所有状态更改存储在UI上。然后,您需要一些解析程序来应用自发出操作以来的所有本地状态更改。这是不平凡的决议。
Web套接字将UI订阅到事件流,以便在后端完成操作后将其推送到前端。是单向还是双向流?优点:用户界面感觉很快,并且与应用程序状态同步。缺点:一致的浏览器支持,需要流事件的后端源以及套接字服务器的可伸缩性。