事件商店3.0 - 吞吐量/性能

问题描述:

我一直JOliver的事件存储3.0作为试验项目中的一个潜在的组成部分,并一直试图通过测量事件存储事件的吞吐量。事件商店3.0 - 吞吐量/性能

我开始使用一个简单的线束,它基本上遍历了一个for循环,创建一个新的流并将一个包含GUID ID和一个字符串属性的非常简单的事件提交给MSSQL2K8 R2 DB。调度员本质上是没有操作的。

这种方法成功地实现〜3K操作/秒在与DB的8路HP G6 DL380在一个单独的32路G7 DL580运行。测试机器没有资源限制,在我的情况下阻塞看起来是极限。

有没有人有测量事件存储的吞吐量和什么样的人物的经验已经实现?我希望获得至少1个数量级的吞吐量,以使其成为可行的选择。

我同意阻止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时间框架中更多。

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

+1

感谢Jonathan的回复。澄清;)每个提交是一个新的EventSource,所以我每秒提交3K个不同的EventSources。收集网络跳并没有改善,但是一个有效的点。就交易而言,我并不明确参与交易,但这可能与不使用交易不同。我使用JSON进行序列化,但由于我们不受CPU限制,我不认为这会限制我们。我已经发布了测试工具到GitHub(https://github.com/MattCollinge/EventStore-Performance-Tests.git)。 – MattC

优秀的问题马特(+1),我看到奥利弗先生本人回答为答案(+1)!

我想抛出一种稍微不同的方法,我自己在玩,以帮助解决您遇到的每秒3000次提交瓶颈问题。

CQRS模式,大多数使用JOliver的EventStore的人似乎都试图遵循CQRS模式,允许大量的“扩展”子模式。通常排队的第一个人是Event提交自己,这是你看到的一个瓶颈。“Queue off”表示从实际提交中卸载并将它们插入一些写入优化的非阻塞I/O进程或“队列”。

我不严谨的解释是:

命令广播 - >命令处理程序 - >事件广播 - >事件处理程序 - >事件商店

实际上有两个向外扩展点这里在这些模式:在命令处理程序事件处理程序。如上所述,大多数情况下从扩展事件处理程序部分开始,或者将您的情况提交到EventStore库,因为这通常是由于需要将其保存在某处(例如Microsoft SQL Server数据库)而导致的最大瓶颈。

我自己正在使用几个不同的提供程序来测试最佳性能来“排队”这些提交。 CouchDB和.NET的AppFabric Cache(它具有很好的GetAndLock()功能)。 [OT]我非常喜欢AppFabric的持久缓存功能,它可以让您创建冗余缓存服务器,在多台机器上备份您的区域 - 因此,只要至少有一台服务器启动并运行,缓存就会保持运行状态。[/ OT]

所以,想象你的事件处理程序不直接写入提交到EventStore。相反,你有一个处理程序将它们插入到一个“队列”系统中,比如Windows Azure Queue,CouchDB,Memcache,AppFabric Cache等。关键是选择一个只有很少或没有块的系统来排队事件,但是这是持久的内置冗余(Memcache是​​我最不喜欢的冗余选项)。你必须有这种冗余,如果服务器掉线,你仍然有事件排队。

要最终从此“排队事件”提交,有几个选项。我喜欢Windows Azure的队列模式,因为许多“工作人员”可以不断地在队列中寻找工作。但它不一定是Windows Azure - 我使用在后台线程中运行的“队列”和“辅助角色”模仿本地代码中的Azure队列模式。它非常好地缩放。假设你有10名工作人员不断查看任何用户更新事件(我通常为每个事件类型编写一个单一的辅助角色,在监控每种类型的统计信息时使伸缩更容易),不断查看此“队列”。将两个事件插入队列中,前两名工作人员立即每次接收一条消息,并将它们(提交他们)直接插入到EventStore中 - 多线程,就像乔纳森在他的答案中提到的那样。您使用该模式的瓶颈将是您选择的任何数据库/事件存储支持。假设你的EventStore使用的是MSSQL,瓶颈仍然是3000 RPS。这很好,因为当这些RPS下降到20000突发之后的50 RPS时,系统就会“赶上”。这是CQRS允许的自然模式:“最终一致性”。

我说有其他的向外扩展模式是CQRS模式原生的。另一个,正如我上面提到的,是命令处理程序(或命令事件)。这也是我所做的一件事,特别是如果你有一个非常丰富的域名,就像我的一个客户那样(在每个命令上有几十个处理器密集的验证检查)。在这种情况下,我实际上会排队执行自己的命令,并由一些工作角色在后台处理。这也给你一个很好的横向扩展模式,因为现在你的整个后端,包括事件的EvetnStore提交,都可以进行线程化。

很显然,这样做的缺点是您会放松一些实时验证检查。我通过在构建域时通常将验证分为两类来解决这个问题。一个是Ajax或者域中的实时“轻量级”验证(类似于Pre-Command检查)。其他则是严重失效的验证检查,只能在域中完成,但不能用于实时检查。然后,您需要在Domain模型中针对失败进行编码。意思是,如果某件事失败,则总是编码出路,通常以通知电子邮件的形式返回给用户,说明出现了问题。由于该用户不再被该队列中的命令阻止,因此如果该命令失败,则需要通知他们。

而你需要进入'后端'的验证检查将进入你的查询或“只读”数据库,riiiight?不要进入EventStore检查唯一的电子邮件地址。您将针对您的前端查询的高可用性只读数据存储库进行验证。嘿,只有一个CouchDB文档专用于系统中所有电子邮件地址的列表,作为CQRS的查询部分。

CQRS只是建议......如果您确实需要实时检查繁重的验证方法,那么您可以在此前建立一个Query(只读)存储库,并在PreCommand阶段加快验证速度它被插入到队列中。很多的灵活性。我甚至会争辩说,验证诸如空的用户名和空电子邮件等事情甚至不是域问题,而是UI负责(减轻了在域中进行实时验证的需要)。我已经构建了一些项目,我在MVC/MVVM ViewModels上进行了非常丰富的UI验证。当然,我的域有非常严格的验证,以确保它在处理之前有效。但是,将平庸的输入验证检查或我称之为“轻量级”验证的内容移到ViewModel图层中,可以向最终用户提供近乎即时的反馈,而无需进入我的域。 (有一些技巧可以保持与您的域同步)。

因此,总之,可能要在提交这些事件之前仔细研究这些事件。正如乔纳森在他的回答中提到的,这很适合EventStore的多线程功能。

+1

有趣的答案。感谢您撰写! –

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