ASP.Net 网站静态锁定文件写入/ IIS回收问题



我看到很多关于网站静态锁定以写入单个文件进行日志记录的讨论。

有些人说永远不要使用静态锁,而另一些人则说在这种情况下明确需要它。 反对者争辩说:如果 IIS 应用程序池被回收怎么办? 然后,静态锁将丢失,并且可能会发生文件写入错误。

我的具体问题:如果你的用户数量相当小(n<1000(,并且你的锁中有一行代码来执行文件写入,比如说500个字符<,这是一个天文数字上不可能关注的问题吗?

如果这是一个重大的问题,那么避免这种罕见的IIS回收静态锁定错误的最简单的改进途径是什么? 在这种情况下,简单的尝试/捕获写入甚至会"捕获"多个文件访问吗?

Use FileShare.ReadWrite and useAsync = false

通过使用此构造函数创建 FileStream 来访问文件(不要使用File.Open(,并指定以下参数:

var stream = new FileStream(path, 
FileMode.Append, 
FileAccess.Write, 
FileShare.ReadWrite, 
4096, 
false);

需要注意的重要论点是FileShare.ReadWriteuseAsync = false.

FileShare.ReadWrite:允许随后打开文件进行读取或写入。如果未指定此标志,则在关闭文件之前,任何打开文件进行读取或写入(通过此进程或其他进程(的请求都将失败。但是,即使指定了此标志,可能仍需要其他权限才能访问该文件。

useAsync:指定是使用异步 I/O 还是同步 I/O。但是,请注意,基础操作系统可能不支持异步 I/O,因此在指定 true 时,句柄可能会根据平台同步打开。异步打开时,BeginRead 和 BeginWrite 方法在大型读取或写入时性能更好,但对于小型读取或写入,它们可能要慢得多。如果应用程序设计为利用异步 I/O,请将 useAsync 参数设置为 true。正确使用异步 I/O 可以将应用程序速度提高多达 10 倍,但在不针对异步 I/O 重新设计应用程序的情况下使用它可能会将性能降低多达 10 倍。

通过使用这些参数,可以获得一个文件句柄,该句柄将允许其他进程并行访问该文件。同时,您的写入将是同步的,这将防止您的输出被另一个进程分成两半。仍然存在锁定,但它由底层操作系统处理,对您和任何竞争进程都是透明的。

添加锁

如果它让你感觉更好,你可以把它包在锁里:

lock (lockObject)
{
using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 4096, false))
{
var writer = new TextWriter(stream);
writer.Write(message);
}
}

请注意,lock可以保护您免受竞争线程的影响,但不能保护您免受竞争进程的影响。如果你有一个处理日志写入的工作线程(例如,使用队列和生产者/使用者模式(,你可能不需要它,它会增加不必要的开销。但是,如果您直接从 Web 工作线程写入日志,则确实需要它。

跨进程互斥锁

以上应该非常安全,即使在应用程序池回收期间也是如此。至少我从来没有遇到过任何问题。但。。如果您真的很偏执,并且您的日志记录是关键任务,则可以使用命名互斥锁来锁定跨越进程边界。

var mutex = new Mutex(false, "MyLoggingMutex");
try
{
mutex.WaitOne();
using (var stream = new FileStream(path, FileMode.Append, FileAccess.Write, FileShare.ReadWrite, 4096, false))
{
var writer = new TextWriter(stream);
writer.Write(message);
}
}
finally
{
mutex.ReleaseMutex();        
}

这种事情有相当多的开销,所以我可能不会使用它,除非日志记录是关键任务,例如,您将可审计数据转储到可能用于支持不可否认性的日志文件(例如,证明谁做了什么,这样你就不会被起诉,诸如此类(。但如果是这种情况,老实说,我会坚持使用数据库,这使得问题解决起来微不足道。

最新更新