在 .NET 中跨进程边界有效地流式传输数据
Efficiently streaming data across process boundaries in .NET
我已经断断续续地开发内部开发工具几个星期了,但我 运行 遇到了一个丑陋的绊脚石,我还没有找到一个好的解决方案.我希望有人可以就在 .NET 中使用现有框架的最佳方式提供一些想法或指导。
背景:该工具的用途是将多个不同类型的日志文件(Windows事件日志、IIS、SQL跟踪等)加载到同一个数据库table 以便将它们一起分类和检查。我个人的目标是简化整个过程,这样我们只进行一次传递,而不会将整个日志缓存在内存或磁盘中。当日志文件达到数百 MB 或 GB 范围时,这一点很重要。快速的性能很好,但是缓慢且不引人注目(允许您同时处理其他事情)比 运行ning 更快但在进程中独占系统要好,所以我专注于最小化 RAM 和磁盘使用.
到目前为止,我已经迭代了几种不同的设计,试图将其归结为简单的东西。我希望日志解析器的核心——必须与任何外部库或文件交互以实际读取数据的部分——尽可能简单并符合标准接口,以便添加对新格式的支持尽可能简单。目前,解析方法 returns 和 IEnumerable<Item>
其中 Item
是自定义结构,我使用 yield return
来最小化缓冲量。
但是,我们很快 运行 陷入了一些丑陋的限制:提供的库(通常由 Microsoft 提供)来处理这些文件格式。最大和最丑陋的问题:其中一个库仅适用于 64 位。另一个(Microsoft.SqlServer.Management.Trace TraceFile for SSMS logs)仅适用于 32 位。众所周知,您不能混合搭配 32 位和 64 位代码。由于本练习的重点是拥有一个可以处理任何格式的实用程序,因此我们需要一个单独的 child 进程(在本例中处理 32-bit-only 部分)。
最终结果是,我需要 64 位主进程启动一个 32 位 child,为其提供解析日志文件所需的信息,并以某种方式将数据流回不需要将整个内容缓冲到内存或磁盘的方式。起初我尝试使用 stdout,但随着数据量的增加而崩溃。我试过使用 WCF,但它确实不是为了处理 "service" 作为 "client" 的 child 而设计的,而且很难得到他们从他们想要的工作方式向后同步,而且我不知道我是否真的能让他们正确地传输数据。我不想使用一种机制来打开不安全的网络端口,或者如果某人 运行 多个实例可能会意外串扰(我希望该场景正常工作——每个 64 位主进程都会产生运行 它自己的 child)。理想情况下,我希望 32 位 运行ning 解析器的核心 child 看起来与 64 位 运行ning 解析器的核心相同 parent,但我不知道是否有可能继续使用 yield return
,即使有一些包装器可以帮助管理 IPC。 .NET 中是否有任何现有框架可以使这变得相对容易?
WCF 确实有 P2P 模式,但是如果你所有的进程都是本地机器,你最好使用 IPC,例如 named pipes,因为后者 运行 in 内核模式并且没有前者的消息开销。
如果失败,您可以尝试 COM,它在 32 位和 64 位进程之间通信应该没有问题。 - Tell me more
以防万一有人遇到这个问题,我会 post 我们最终确定的解决方案。关键是重新定义进程间 WCF 服务接口,使其不同于进程内 IEnumerable
接口。我们没有尝试 yield return
跨进程边界,而是在中间插入了一个使用枚举器的代理层,因此我们可以一遍又一遍地调用 "give me an item" 方法。这可能比真正的流解决方案有更多的性能开销,因为每个项目都有一个方法调用,但它似乎确实完成了工作,而且它不会泄漏或消耗内存。
我们确实遵循了 Micky 关于使用命名管道的建议,但仍在 WCF 中。我们还使用命名信号量来协调这两个进程,因此在 "child service" 完成启动之前我们不会尝试进行服务调用。
我已经断断续续地开发内部开发工具几个星期了,但我 运行 遇到了一个丑陋的绊脚石,我还没有找到一个好的解决方案.我希望有人可以就在 .NET 中使用现有框架的最佳方式提供一些想法或指导。
背景:该工具的用途是将多个不同类型的日志文件(Windows事件日志、IIS、SQL跟踪等)加载到同一个数据库table 以便将它们一起分类和检查。我个人的目标是简化整个过程,这样我们只进行一次传递,而不会将整个日志缓存在内存或磁盘中。当日志文件达到数百 MB 或 GB 范围时,这一点很重要。快速的性能很好,但是缓慢且不引人注目(允许您同时处理其他事情)比 运行ning 更快但在进程中独占系统要好,所以我专注于最小化 RAM 和磁盘使用.
到目前为止,我已经迭代了几种不同的设计,试图将其归结为简单的东西。我希望日志解析器的核心——必须与任何外部库或文件交互以实际读取数据的部分——尽可能简单并符合标准接口,以便添加对新格式的支持尽可能简单。目前,解析方法 returns 和 IEnumerable<Item>
其中 Item
是自定义结构,我使用 yield return
来最小化缓冲量。
但是,我们很快 运行 陷入了一些丑陋的限制:提供的库(通常由 Microsoft 提供)来处理这些文件格式。最大和最丑陋的问题:其中一个库仅适用于 64 位。另一个(Microsoft.SqlServer.Management.Trace TraceFile for SSMS logs)仅适用于 32 位。众所周知,您不能混合搭配 32 位和 64 位代码。由于本练习的重点是拥有一个可以处理任何格式的实用程序,因此我们需要一个单独的 child 进程(在本例中处理 32-bit-only 部分)。
最终结果是,我需要 64 位主进程启动一个 32 位 child,为其提供解析日志文件所需的信息,并以某种方式将数据流回不需要将整个内容缓冲到内存或磁盘的方式。起初我尝试使用 stdout,但随着数据量的增加而崩溃。我试过使用 WCF,但它确实不是为了处理 "service" 作为 "client" 的 child 而设计的,而且很难得到他们从他们想要的工作方式向后同步,而且我不知道我是否真的能让他们正确地传输数据。我不想使用一种机制来打开不安全的网络端口,或者如果某人 运行 多个实例可能会意外串扰(我希望该场景正常工作——每个 64 位主进程都会产生运行 它自己的 child)。理想情况下,我希望 32 位 运行ning 解析器的核心 child 看起来与 64 位 运行ning 解析器的核心相同 parent,但我不知道是否有可能继续使用 yield return
,即使有一些包装器可以帮助管理 IPC。 .NET 中是否有任何现有框架可以使这变得相对容易?
WCF 确实有 P2P 模式,但是如果你所有的进程都是本地机器,你最好使用 IPC,例如 named pipes,因为后者 运行 in 内核模式并且没有前者的消息开销。
如果失败,您可以尝试 COM,它在 32 位和 64 位进程之间通信应该没有问题。 - Tell me more
以防万一有人遇到这个问题,我会 post 我们最终确定的解决方案。关键是重新定义进程间 WCF 服务接口,使其不同于进程内 IEnumerable
接口。我们没有尝试 yield return
跨进程边界,而是在中间插入了一个使用枚举器的代理层,因此我们可以一遍又一遍地调用 "give me an item" 方法。这可能比真正的流解决方案有更多的性能开销,因为每个项目都有一个方法调用,但它似乎确实完成了工作,而且它不会泄漏或消耗内存。
我们确实遵循了 Micky 关于使用命名管道的建议,但仍在 WCF 中。我们还使用命名信号量来协调这两个进程,因此在 "child service" 完成启动之前我们不会尝试进行服务调用。