我有一个 Web 应用程序,可以控制哪些 Web 应用程序从我们的负载均衡器获取服务流量。 Web 应用程序在每个单独的服务器上运行。
它在 ASP.NET 应用程序状态的对象中跟踪每个应用程序的“输入或输出”状态,并且只要状态发生更改,该对象就会序列化到磁盘上的文件。当 Web 应用程序启动时,状态将从文件中反序列化。
虽然网站本身每秒只收到几个请求,并且很少访问文件,但我发现由于某种原因,在尝试读取或写入文件时很容易发生冲突。这种机制需要非常可靠,因为我们有一个自动化系统,可以定期对服务器进行滚动部署。
在有人对上述任何内容的审慎性提出任何评论之前,请允许我简单地说,解释其背后的推理将使这篇文章比现在长得多,所以我想避免搬山。
也就是说,我用来控制文件访问的代码如下所示:
internal static Mutex _lock = null;
/// <summary>Executes the specified <see cref="Func{FileStream, Object}" /> delegate on
/// the filesystem copy of the <see cref="ServerState" />.
/// The work done on the file is wrapped in a lock statement to ensure there are no
/// locking collisions caused by attempting to save and load the file simultaneously
/// from separate requests.
/// </summary>
/// <param name="action">The logic to be executed on the
/// <see cref="ServerState" /> file.</param>
/// <returns>An object containing any result data returned by <param name="func" />.
///</returns>
private static Boolean InvokeOnFile(Func<FileStream, Object> func, out Object result)
{
var l = new Logger();
if (ServerState._lock.WaitOne(1500, false))
{
l.LogInformation( "Got lock to read/write file-based server state."
, (Int32)VipEvent.GotStateLock);
var fileStream = File.Open( ServerState.PATH, FileMode.OpenOrCreate
, FileAccess.ReadWrite, FileShare.None);
result = func.Invoke(fileStream);
fileStream.Close();
fileStream.Dispose();
fileStream = null;
ServerState._lock.ReleaseMutex();
l.LogInformation( "Released state file lock."
, (Int32)VipEvent.ReleasedStateLock);
return true;
}
else
{
l.LogWarning( "Could not get a lock to access the file-based server state."
, (Int32)VipEvent.CouldNotGetStateLock);
result = null;
return false;
}
}
This usually有效,但有时我无法访问互斥体(我在日志中看到“无法获取锁定”事件)。我无法在本地重现此问题 - 它只发生在我的生产服务器(Win Server 2k3/IIS 6)上。如果我删除超时,应用程序将无限期挂起(竞争条件??),包括后续请求。
当我确实收到错误时,查看事件日志会告诉我互斥锁已由前一个请求实现并释放before错误已被记录。
互斥体在 Application_Start 事件中实例化。当它在声明中静态实例化时,我得到相同的结果。
借口,借口:线程/锁定不是我的强项,因为我通常不必担心它。
关于为什么它随机无法收到信号有什么建议吗?
Update:
我已经添加了正确的错误处理(多么尴尬!),但我仍然遇到相同的错误 - 并且根据记录,未处理的异常从来都不是问题。
只有一个进程会访问该文件 - 我不使用网络花园作为该应用程序的网络池,并且没有其他应用程序使用该文件。我能想到的唯一例外是当应用程序池回收时,并且在创建新的 WP 时旧的 WP 仍然处于打开状态 - 但我可以通过观察任务管理器看出问题是在只有一个工作进程时发生的。
@mmr:使用 Monitor 与使用 Mutex 有什么不同?根据 MSDN 文档,看起来它实际上在做同样的事情 - 如果我无法使用我的互斥体获得锁,那么它does只需返回 false 即可优雅地失败。
另一件需要注意的事情是:我遇到的问题似乎完全是随机的 - 如果一个请求失败,那么下一个请求可能会正常工作。似乎也没有任何模式(至少没有其他模式)。
更新2:
该锁不用于任何其他调用。唯一一次在 InvokeOnFile 方法外部引用 _lock 是在实例化它时。
调用的 Func 要么从文件读取并反序列化为对象,要么序列化对象并将其写入文件。这两个操作都不是在单独的线程上完成的。
ServerState.PATH 是一个静态只读字段,我预计它不会导致任何并发问题。
我还想重申我之前的观点,即我无法在本地(在卡西尼号)中重现这一点。
得到教训:
- 使用正确的错误处理(废话!)
- 使用正确的工具来完成工作(并对该工具的作用/作用有基本的了解)。正如 sambo 指出的那样,使用 Mutex 显然会产生大量开销,这会导致我的应用程序出现问题,而 Monitor 是专门为 .NET 设计的。