很好的问题马特(+1),我看到奥利弗先生自己回答为答案(+1)!
我想提出一种我自己正在使用的稍微不同的方法,以帮助解决您看到的每秒 3,000 次提交的瓶颈。
大多数使用 JOliver 的 EventStore 的人似乎都在尝试遵循 CQRS 模式,它允许许多“横向扩展”子模式。人们通常排队的第一个是事件提交本身,您会看到其中的瓶颈。“排队”意味着从实际提交中卸载并将它们插入到一些写优化的非阻塞 I/O 进程中,或者“队列”。
我的松散解释是:
命令广播 -> 命令处理程序 -> 事件广播 -> 事件处理程序 -> 事件存储
在这些模式中实际上有两个横向扩展点:命令处理程序和事件处理程序。如上所述,大多数从扩展事件处理程序部分开始,或者在您的情况下将提交扩展到 EventStore 库,因为这通常是最大的瓶颈,因为需要将其保存在某个地方(例如 Microsoft SQL Server 数据库)。
我自己正在使用一些不同的提供者来测试“排队”这些提交的最佳性能。CouchDB 和 .NET 的 AppFabric 缓存(具有出色的 GetAndLock() 功能)。[OT]我真的很喜欢 AppFabric 的持久缓存功能,它允许您创建冗余缓存服务器,在多台机器上备份您的区域 - 因此,只要至少有 1 台服务器启动并运行,您的缓存就会保持活动状态。[/OT]
因此,假设您的事件处理程序不直接将提交写入 EventStore。相反,您有一个处理程序将它们插入“队列”系统,例如 Windows Azure 队列、CouchDB、Memcache、AppFabric 缓存等。关键是选择一个几乎没有块的系统来排队事件,但是有些内置冗余(Memcache 是我最不喜欢的冗余选项)。你必须有这种冗余,以防万一服务器掉线,你仍然有事件排队。
要最终从这个“排队事件”提交,有几个选项。为此,我喜欢 Windows Azure 的队列模式,因为您可以让许多“工作人员”不断地在队列中寻找工作。但它不一定是 Windows Azure——我在本地代码中模仿了 Azure 的队列模式,使用在后台线程中运行的“队列”和“工作角色”。它的扩展性非常好。
假设您有 10 名工作人员不断地查看任何用户更新事件的“队列”(我通常为每个事件类型编写一个工作人员角色,以便在您监控每种类型的统计信息时更容易扩展)。两个事件被插入到队列中,前两个工作人员立即各自获取一条消息,并同时将它们(提交)直接插入到您的 EventStore 中 - 多线程,正如乔纳森在他的回答中提到的那样。该模式的瓶颈将是您选择的任何数据库/事件存储支持。假设您的 EventStore 正在使用 MSSQL,而瓶颈仍然是 3,000 RPS。这很好,因为系统的构建是为了在这些 RPS 下降到 20,000 次爆发后的 50 RPS 时“赶上”。这是 CQRS 允许的自然模式:“最终一致性”。
我说过 CQRS 模式还有其他横向扩展模式。正如我上面提到的,另一个是命令处理程序(或命令事件)。这也是我做过的一个,特别是如果您像我的一个客户那样拥有非常丰富的域域(每个命令上的数十个处理器密集型验证检查)。在这种情况下,我实际上会将命令本身排入队列,由一些工作角色在后台处理。这也为您提供了一个很好的横向扩展模式,因为现在您的整个后端,包括事件的 EvetnStore 提交,都可以线程化。
显然,这样做的缺点是您会丢失一些实时验证检查。在构建我的域时,我通常通过将验证分为两类来解决这个问题。一种是 Ajax 或域中的实时“轻量级”验证(有点像命令前检查)。其他的是硬故障验证检查,仅在域中完成,但不能用于实时检查。然后,您需要在域模型中为失败编码。意思是,如果出现问题,总是为出路编写代码,通常以通知电子邮件的形式返回给用户出现问题。因为用户不再被这个排队的命令阻塞,所以如果命令失败,他们需要得到通知。
您需要进入“后端”的验证检查将进入您的查询或“只读”数据库,对吗?不要进入 EventStore 来检查,比如说,一个唯一的电子邮件地址。您将针对前端查询的高可用性只读数据存储进行验证。哎呀,有一个 CouchDB 文档专用于系统中所有电子邮件地址的列表,作为 CQRS 的查询部分。
CQRS 只是建议......如果您真的需要实时检查繁重的验证方法,那么您可以围绕它构建一个查询(只读)存储,并加快验证 - 在 PreCommand 阶段,在它被插入之前队列。很大的灵活性。我什至会争辩说,验证空用户名和空电子邮件之类的东西甚至不是域问题,而是 UI 责任(减轻了在域中进行实时验证的需要)。我已经构建了一些项目,我在 MVC/MVVM ViewModel 上进行了非常丰富的 UI 验证。当然我的域有非常严格的验证,以确保它在处理之前是有效的。但是将平庸的输入验证检查,或者我所说的“轻量级”验证,向上移动到 ViewModel 层可以向最终用户提供近乎即时的反馈,没有进入我的领域。(也有一些技巧可以使其与您的域保持同步)。
因此,总而言之,可能会考虑在提交这些事件之前将它们排入队列。正如乔纳森在他的回答中提到的那样,这与 EventStore 的多线程特性非常吻合。