Gary(CQRS专家)发言:
一个聚合决定了写模型中的一致性边界,这个边界和系统持久存储数据的一致性相关。流程管理器管理不同聚合(可能在不同的限界上下文中)之间的关系,并确保聚合最终彼此一致。
注册过程的失败可能对系统产生不利后果:聚合可能彼此不同步,这可能导致系统中出现不可预测的行为,或者一些进程可能最终成为僵尸进程,继续运行并使用资源,但永远不会完成。团队确定了以下与RegistrationProcessManager流程管理器相关的特定故障场景。流程管理器也许会:
崩溃或者无法在发送任何命令之前和接收事件之后持久化其状态。这样消息处理器可能无法将事件标记为完成,因此在超时之后,将事件放回Topic订阅并重新处理。
在发送任何命令之前和持久化其状态之后崩溃。这将使系统处于不一致的状态,因为流程管理器保存了其新的状态,但没有发送预期的命令。原始事件被放回Topic订阅并重新处理。
未能标记事件已被处理。流程管理器将第二次处理该事件,因此在超时之后,系统将把该事件重新放到服务总线Topic订阅中。
在等待它所期望的特定事件时超时。流程管理器无法继续处理并达到预期的最终状态。
接收到一个流程管理器处于特定状态时不期望接收的事件。这可能表明在其他地方存在问题,这意味着流程管理器继续工作是不安全的。
这些设想可归纳为两个具体的问题:
RegistrationProcessManager成功地处理了一个事件,但是没有将其标记为完成。在事件自动返回到Azure服务总线Topic订阅之后,RegistrationProcessManager将再次处理该事件。
RegistrationProcessManager成功地处理一个事件,并将其标记为完成,但随后未能发送命令。
使系统能弹性的重新处理事件如果流程管理器本身的行为是幂等的,那么如果它第二次接收并处理一个事件,则不会导致系统中的不一致。使流程管理器的行为具有幂等性,可以避免前三种故障条件中固有的问题。崩溃之后,您可以简单地重新启动流程管理器,并第二次重新处理传入的事件。
您可以让流程管理器发送的所有命令都是幂等的,来替代让流程管理器幂等。重新启动流程管理器可能会导致第二次发送命令,但如果这些命令是幂等的,则不会对流程或系统产生不利影响。要使此方法生效,您仍然需要修改流程管理器,以确保它至少发送一次所有命令。如果命令是幂等的,那么多次发送它们并不重要,但是如果根本不发送就很重要。
在V1版本中,大多数消息处理要么已经是幂等的,要么系统检测到重复的消息并将它们发送到dead-letter队列。例外情况是OrderPlaced事件和SeatsReserved事件,因此团队修改了系统V3版本处理这两个事件的方式,以解决这个问题。
确保始终发送命令需要事务行为来确保当RegistrationProcessManager类保存其状态时,系统始终会发送命令。这要求团队实现一个伪事务,因为将Azure服务总线和SQL数据库表一起放到分布式事务中既不可取也不可行。
团队为V3版本所采用的解决方案是确保系统持久保存RegistrationProcessManager生成的所有命令,同时持久保存RegistrationProcessManager实例的状态。然后,系统尝试发送命令,并在成功发送之后将它们从存储中删除。每当从存储中加载RegistrationProcessManager实例时,系统还检查未发送的消息。
性能优化在这个阶段,我们使用Visual Studio运行性能和压力测试,以分析响应时间并确定瓶颈。团队使用Visual Studio Load Test来模拟访问应用程序的不同用户数量,并在代码中添加了额外的跟踪,以记录时间信息,以便进行详细分析。团队在Azure中创建了性能测试环境,在Azure VM角色实例中运行测试控制器和测试代理。这使我们能够通过使用测试代理模拟不同数量的虚拟用户来测试Contoso会议管理系统在不同负载下的执行情况。
作为这项工作的结果,团队对系统进行了许多更改,以优化其性能。
Gary(CQRS专家)发言:
尽管在旅程中,团队在项目结束时进行了性能测试和优化工作,但通常在你想做的时候就做这个工作是有意义的,这可以解决可伸缩性问题并尽快加固代码。如果您正在构建自己的基础设施,并且需要能够处理高吞吐量,则尤其如此。