在 .NET 中跨进程边界高效地流式传输数据

Posted

技术标签:

【中文标题】在 .NET 中跨进程边界高效地流式传输数据【英文标题】:Efficiently streaming data across process boundaries in .NET 【发布时间】:2015-06-27 00:24:20 【问题描述】:

我已经在内部开发工具上工作了几个星期,但我遇到了一个丑陋的绊脚石,我还没有找到一个好的解决方案。我希望有人可以就在 .NET 中使用现有框架的最佳方式提供一些想法或指导。

背景:此工具的目的是将多个不同类型的日志文件(Windows 事件日志、IIS、SQL 跟踪等)加载到同一个数据库表中,以便可以一起对它们进行排序和检查。我个人的目标是简化整个事情,以便我们只进行一次传递,而不会将整个日志缓存在内存或磁盘中。当日志文件达到数百 MB 或进入 GB 范围时,这一点很重要。快速的性能是好的,但缓慢且不引人注目(允许您同时处理其他事情)比运行更快但在此过程中独占系统要好,因此我专注于最大限度地减少 RAM 和磁盘的使用。

到目前为止,我已经迭代了几种不同的设计,试图将其归结为简单的东西。我希望日志解析器的核心——必须与任何外部库或文件交互以实际读取数据的部分——尽可能简单并符合标准接口,以便添加对新格式的支持尽可能简单。目前,parse 方法返回一个IEnumerable<Item>,其中Item 是一个自定义结构,我使用yield return 来最小化缓冲量。

但是,我们很快就遇到了一些难看的限制:处理这些文件格式的库(通常由 Microsoft 提供)。最大和最丑陋的问题:其中一个库仅适用于 64 位。另一个(用于 SSMS 日志的 Microsoft.SqlServer.Management.Trace TraceFile)仅适用于 32 位。众所周知,您不能混合和匹配 32 位和 64 位代码。由于本练习的重点是拥有一个可以处理任何格式的实用程序,因此我们需要有一个单独的子进程(在本例中是处理 32 位部分)。

最终结果是我需要 64 位主进程来启动一个 32 位子进程,为它提供解析日志文件所需的信息,并以某种不需要的方式将数据流回将全部内容缓冲到内存或磁盘。起初我尝试使用标准输出,但随着大量数据的使用而崩溃。我尝试过使用 WCF,但它实际上并不是为了处理作为“客户端”的 的“服务”,而且很难让它们从他们想要的工作方式向后同步,另外我不知道我是否真的可以让它们正确地流式传输数据。我不想使用一种机制来打开不安全的网络端口,或者如果有人运行多个实例时可能会意外串扰(我希望该场景能够正常工作——每个 64 位主进程都会产生并运行自己的子进程)。理想情况下,我希望在 32 位子进程中运行的解析器核心看起来与在 64 位父进程中运行的解析器核心相同,但我不知道是否可以继续使用 yield return ,即使有一些包装器来帮助管理 IPC。 .NET 中是否有任何现有框架可以使这相对容易?

【问题讨论】:

【参考方案1】:

WCF 确实具有 P2P 模式,但是如果您的所有进程都是本地计算机,那么您最好使用 IPC,例如 命名管道,因为后者在 内核模式 下运行,并且没有前者的消息传递开销。

如果你可以尝试 COM,它在 32 位和 64 位进程之间交谈应该没有问题。 - Tell me more

【讨论】:

【参考方案2】:

如果有人偶然发现这一点,我将发布我们最终确定的解决方案。关键是重新定义进程间 WCF 服务接口,使其不同于进程内IEnumerable 接口。我们没有尝试yield return 跨越进程边界,而是在中间插入了一个使用枚举器的代理层,因此我们可以一遍又一遍地调用“给我一个项目”方法。这可能比真正的流式解决方案具有更多的性能开销,因为每个项目都有一个方法调用,但它似乎确实完成了工作,并且不会泄漏或消耗内存。

我们确实遵循了 Micky 的使用命名管道的建议,但仍在 WCF 中。我们还使用命名信号量来协调两个进程,因此在“子服务”完成启动之前,我们不会尝试进行服务调用。

【讨论】:

以上是关于在 .NET 中跨进程边界高效地流式传输数据的主要内容,如果未能解决你的问题,请参考以下文章

Android 基于共享内存实现跨进程大数据的高效传输

Android 基于共享内存实现跨进程大数据的高效传输

Android Binder跨进程与非跨进程的传输异同源码分析

——Parcelable接口的使用(跨进程,Intent传输)

一种高效的跨进程MVC架构

Windows 中跨进程的共享内存是不是一致?