【发布时间】:2014-03-07 10:26:48
【问题描述】:
我有一个 Windows 服务,它必须在 2 个网络共享的大约 20 个文件中写入大约 20kB 的数据。
Total Commander 写入文件的时间:小于 0.1s。
用我的应用程序编写文件的时间:大约 10 秒。
怎么了?是的,文件会不断地从两个共享中读取,但这应该不是问题,因为:
public void WriteData(string text, string fileName, bool forceBackup = false) {
foreach (var dir in Locations) {
var path = string.Format(@"{0}\{1}", dir, fileName);
FileStream stream = null;
try {
stream = new FileStream(
path,
FileMode.Create,
FileAccess.Write,
FileShare.Read
);
using (StreamWriter writer = new StreamWriter(stream)) {
stream = null;
writer.Write(text);
}
}
catch (Exception) { } // irrelevant now, tested it doesn't throw exceptions anyway
finally {
if (stream != null) stream.Dispose();
}
File.SetLastWriteTime(path, DateTime.Now);
}
}
上面的代码适用于本地文件。将所有数据写入 RAM 驱动器需要一毫秒。我的网络共享也位于 RAM 驱动器上。
重要的是 - 使用 Total Commander 将这些文件复制到完全相同的位置也只需几毫秒。我的意思是 - 在满负荷下复制到网络共享。
没有共享冲突,应用程序在单个线程中写入文件。从 Total Commander 写入这些文件时没有问题,在不使用网络共享的情况下使用我的应用程序写入这些文件没有问题。
没有共享冲突,因为在写入时 - 这些文件只能由 Web 服务器读取,并明确设置了 FileAccess.Read 和 FileShare.ReadWrite。
不,我不能在不使用网络共享的情况下写入这些文件,因为我的服务所做的是在两台服务器之间进行同步。不,我不能使用 DFSR,因为文件更新得太频繁了(每秒 2 次)。不,我不能使用 2 个单独的服务来更新两台机器上的文件,因为当我的服务的每个实例都可以停止而不停止两台服务器上的数据更新时,它会取消故障保护功能。
详细地说,在我的生产环境中,有 2 个该服务实例,其中一个正在更新文件,另一个持续监控活动的是否完成其工作。当检测到故障时,他们会切换角色。这一切都是实时发生的,并且具有魅力。有一个大故障:写入文件的延迟超长。
如果您想知道File.SetLastWriteTime() 代表什么 - 这是针对 Windows (.NET) 错误的解决方法,即文件上次写入时间未通过单独的 create / write 正确更新。当然,如果第一个实例按时更新文件,那么正确的修改时间对于另一个实例的检测至关重要。
另外:我收到报告说有时会从这些文件中读取一些垃圾。但它很少发生。我还没有确认这个错误。
但主要问题是 - 什么需要这么长时间?我的测试服务器和目标服务器之间有快速的 1GBit 链接,生产服务器之间有 10GBit 的链接。 Ping 低于 1ms。这不是网络问题。
经过更多测试后,我发现缓冲区大小和文件选项与写入时间无关。我发现网络 ping 非常重要。我无法在我的开发机器上测试代码,因为 ping 太大。
无论如何 - 如果所有文件都创建一次,然后在不重新创建流的情况下更新,则可以优化代码以将运行速度提高 80%。当用于本地共享时,它也非常快。无论如何 - 测试代码很快,同一台服务器上的生产代码慢了 50 倍。但是有一点点不同 - 生产文件会不断地被 Web 服务器读取,而测试文件则不会。
仍然 - 未达到目标。我需要在与 10GBit/s 以太网链接的 2 台服务器上每秒更新 20 x 1kb 文件。达到 200 毫秒的延迟是可以接受的,但它仅适用于测试文件,共享的真实文件每次更新仍然超过 6000 毫秒。
顺便说一句,当可靠性至关重要时,此处不能选择打开文件。该服务应该能够无缝地将所有更新切换到另一个实例,以防任何文件被删除,或者发生任何网络、数据库或磁盘错误。让文件保持打开状态可能会导致共享违规、内存泄漏和其他灾难。正确处理不断打开的文件也会非常复杂,这会使代码更难调试。
也许还有另一种方法可以在服务器之间共享数据?也许一个 zip 文件可以用来上传文件,然后另一个服务会解压缩文件?我确信压缩和解压缩 1kB 的数据不会花费 1 秒的时间!
【问题讨论】:
-
虽然我认为它不能解决问题,但您可以通过使用
File.WriteAllText(path, text)来缩短代码很多。 (值得尝试看看它是否也让它更快,但我不希望它。) -
请不要在问题标题中包含有关所用语言的信息,除非没有它就没有意义。标记用于此目的。
-
@Jon Skeet:我不能,我需要共享文件以供阅读。我不能用 File.WriteAllText 指定文件共享模式,可以吗?
-
啊,不。我错过了。即使
StreamWriter构造函数也不允许该选项。请注意,使用using语句而不是try/catch/finally会更简单:) -
创建一个微小的复制代码。这缩小了范围。像1)打开流,2)写一个字节。就是这样。
标签: c# .net networking