Disruptor 日志实现
Disruptor Journaling Implementation
在Martin Fowler's write-up of the LMAX-disruptor architecture中,他说:
The journaler's job is to store all the events in a durable form, so
that they can be replayed should anything go wrong. LMAX does not use
a database for this, just the file system. They stream the events onto
the disk.
我很好奇基于文件系统的事件日志的实现在实践中是什么样子的。 following answer 表示它被写入 "raw file",但我对可能为生产系统实现的实际细节感兴趣。它真的是一个包含不断附加的结构化日志的原始文本文件吗?还是某种二进制格式?系统的这个组件是否有任何关键的设计决策?
建议的日志记录器需要包含两条信息:收到的事件本身和某种标识符,用于跟踪您在日志中的位置,以便您可以在重播期间选择从该记录开始。
存储格式最终由您决定,但需要考虑以下因素:
重播可能不仅需要从系统崩溃中触发,还需要从您自己的代码中的错误中触发。对输入消息字节流的操作越少越好。对字节流的任何操作都会引入错误的机会,并使您的重放逻辑与 "drop bytes back into the input buffer." 对我来说这可能是最大的决定。
回放应该很快并且不包含业务逻辑。一种允许您的存储设备按顺序存储并且不需要来回跳跃的文件格式(例如带有索引的数据库所需的文件格式)将提高性能。环形缓冲区输入和存储层之间的层越多,速度就越慢。
磁盘上的预分配存储(您甚至可以使用 RAW 分区)将允许您从头到尾写入字节,而无需更新文件系统的目录元数据和可用空间跟踪区域.这应该可以简化并提高性能。只要此预分配足以将所有数据保存在检查点之间,您就可以了。随着时间的推移,随着存储设备的改进,这不再是一个令人担忧的问题。
journaller 只是主应用程序的另一个消费者 ring-buffer。消息通过网络读取,添加 header(接收到的时间戳等),然后送入 ring-buffer.
共有三个消费者:
- 应用程序处理程序(调用业务逻辑)
- 复制发件人(将邮件复制到辅助服务器)
- 日志处理程序
应用程序处理程序在 journaller 完成时被门控,并且来自次要的 ack,确保收到的消息在次要的 ring-buffer 和本地系统 page-cache 中,然后再处理应用程序消息.
日志记录器非常愚蠢 - 消息以有线格式附加到 fixed-length 日志文件。该文件是预先 pre-allocated 并使用各种 file-system 挂载选项来最小化 write-latency。最后,我们发现 XFS 是最好的 file-system 选项,但前提是没有同时读取正在写入的日志文件的读者。否则 XFS 代码中可能会有讨厌的锁定效果。
如果您对我们如何得出这些结论感兴趣,我将所有这些都写得非常详细:
https://epickrram.blogspot.co.uk/2015/05/improving-journalling-latency.html
https://epickrram.blogspot.co.uk/2015/07/seek-write-vs-pwrite.html
https://epickrram.blogspot.co.uk/2015/12/journalling-revisited.html
在Martin Fowler's write-up of the LMAX-disruptor architecture中,他说:
The journaler's job is to store all the events in a durable form, so that they can be replayed should anything go wrong. LMAX does not use a database for this, just the file system. They stream the events onto the disk.
我很好奇基于文件系统的事件日志的实现在实践中是什么样子的。 following answer 表示它被写入 "raw file",但我对可能为生产系统实现的实际细节感兴趣。它真的是一个包含不断附加的结构化日志的原始文本文件吗?还是某种二进制格式?系统的这个组件是否有任何关键的设计决策?
建议的日志记录器需要包含两条信息:收到的事件本身和某种标识符,用于跟踪您在日志中的位置,以便您可以在重播期间选择从该记录开始。
存储格式最终由您决定,但需要考虑以下因素:
重播可能不仅需要从系统崩溃中触发,还需要从您自己的代码中的错误中触发。对输入消息字节流的操作越少越好。对字节流的任何操作都会引入错误的机会,并使您的重放逻辑与 "drop bytes back into the input buffer." 对我来说这可能是最大的决定。
回放应该很快并且不包含业务逻辑。一种允许您的存储设备按顺序存储并且不需要来回跳跃的文件格式(例如带有索引的数据库所需的文件格式)将提高性能。环形缓冲区输入和存储层之间的层越多,速度就越慢。
磁盘上的预分配存储(您甚至可以使用 RAW 分区)将允许您从头到尾写入字节,而无需更新文件系统的目录元数据和可用空间跟踪区域.这应该可以简化并提高性能。只要此预分配足以将所有数据保存在检查点之间,您就可以了。随着时间的推移,随着存储设备的改进,这不再是一个令人担忧的问题。
journaller 只是主应用程序的另一个消费者 ring-buffer。消息通过网络读取,添加 header(接收到的时间戳等),然后送入 ring-buffer.
共有三个消费者:
- 应用程序处理程序(调用业务逻辑)
- 复制发件人(将邮件复制到辅助服务器)
- 日志处理程序
应用程序处理程序在 journaller 完成时被门控,并且来自次要的 ack,确保收到的消息在次要的 ring-buffer 和本地系统 page-cache 中,然后再处理应用程序消息.
日志记录器非常愚蠢 - 消息以有线格式附加到 fixed-length 日志文件。该文件是预先 pre-allocated 并使用各种 file-system 挂载选项来最小化 write-latency。最后,我们发现 XFS 是最好的 file-system 选项,但前提是没有同时读取正在写入的日志文件的读者。否则 XFS 代码中可能会有讨厌的锁定效果。
如果您对我们如何得出这些结论感兴趣,我将所有这些都写得非常详细:
https://epickrram.blogspot.co.uk/2015/05/improving-journalling-latency.html
https://epickrram.blogspot.co.uk/2015/07/seek-write-vs-pwrite.html
https://epickrram.blogspot.co.uk/2015/12/journalling-revisited.html