10

我一直在尝试将 JOliver 的 Event Store 3.0 作为项目中的一个潜在组件,并一直在尝试通过 Event Store 测量事件的吞吐量。

我开始使用一个简单的工具,它基本上通过一个 for 循环迭代创建一个新的流,并将一个非常简单的事件提交到一个 MSSQL2K8 R2 DB,该事件包括一个 GUID id 和一个字符串属性。调度员本质上是一个无操作的人。

这种方法设法在 8 路 HP G6 DL380 上运行每秒约 3K 次操作,而 DB 在单独的 32 路 G7 DL580 上运行。测试机器不受资源限制,在我的情况下,阻塞看起来是限制。

有没有人有任何测量事件存储的吞吐量的经验以及已经取得了什么样的数字?我希望吞吐量至少提高 1 个数量级,以使其成为可行的选择。

4

3 回答 3

7

我同意阻塞 IO 将成为最大的瓶颈。我可以在基准测试中看到的一个问题是您正在针对单个流进行操作。您的域中有多少个聚合根,每秒有 3K+ 事件?EventStore 的主要设计是针对多个聚合的多线程操作,从而减少读取世界应用程序的争用和锁定。

另外,您使用的是什么序列化机制?JSON.NET?我还没有实现 Protocol Buffers,但是每个基准测试都表明 PB 在性能方面要快得多。对您的应用程序运行分析器以查看最大的瓶颈在哪里会很有趣。

我注意到的另一件事是,您在等式中引入了网络跃点,这会增加针对任何单个流的延迟(和阻塞时间)。如果您正在写入使用固态驱动器的本地 SQL 实例,我可以看到与运行磁驱动器且数据和日志文件位于同一盘片上的远程 SQL 实例相比,该数字要高得多。

最后,您的基准应用程序是使用 System.Transactions 还是默认为无事务?(EventStore 是安全的,无需使用 System.Transactions 或任何类型的 SQL 事务。)

现在,说了这么多,我毫不怀疑 EventStore 中的某些区域只要稍加注意就可以显着优化。事实上,我正在为 3.1 版本进行一些向后兼容的模式修订,以减少在单个提交操作期间在 SQL Server(以及一般的 RDBMS 引擎)中执行的写入次数。

在开始作为 3.x 基础的 2.x 重写时,我面临的最大设计问题之一是异步、非阻塞 IO 的想法。我们都知道 node.js 和其他非阻塞 Web 服务器比线程 Web 服务器高出一个数量级。然而,在调用者上引入的复杂性的可能性增加了,这是必须强烈考虑的事情,因为它是大多数程序和库操作方式的根本转变。如果并且当我们确实转向事件、非阻塞模型时,它会更多地出现在 4.x 时间框架内。

底线:发布您的基准,以便我们可以看到瓶颈在哪里。

于 2011-11-23T04:07:42.063 回答
6

很好的问题马特(+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 的多线程特性非常吻合。

于 2012-06-21T20:25:33.400 回答
0

我们使用 Erlang/Elixir 构建了一个用于大规模并发的小型样板,使用 Eventstore https://github.com/work-capital/elixir-cqrs-eventsourcing 。我们仍然需要优化数据库连接、池等……但是每个聚合有一个进程和多个数据库连接的想法符合您的需求。

于 2016-10-27T08:12:51.940 回答