3

我有大约 50 个网站,在 5 个 Web 服务器之间进行负载平衡。它们都使用企业库缓存,并访问相同的缓存数据库。缓存数据库中的项目每隔几个小时刷新一次,使用 ICacheItemRefreshAction 实现。

我想通过将刷新代码放在关键部分中来保证只有一个网站刷新缓存。

  • 如果网站在单个服务器上的单个应用程序池中运行,我可以使用lock()

  • 如果网站在单个服务器上的单独应用程序池中运行,我可以使用Mutex

但是,这些不能确保跨多个 Web 服务器的关键部分。

目前,我正在缓存数据库中创建一个新密钥以充当互斥锁。这通常会起作用,但我可以看到 2 个进程进入临界区的可能性很小。

public class TakeLongTimeToRefresh : ICacheItemRefreshAction
{
    #region ICacheItemRefreshAction Members

    public void Refresh(string removedKey, object expiredValue, CacheItemRemovedReason removalReason)
    {
        string lockKey = "lockKey";
        ICacheManager cm = CacheFactory.GetCacheManager();

        if (!cm.Contains(lockKey))
        {
            Debug.WriteLine("Entering critical section");
            // Add a lock-key which will never expire for synchronisation.
            // I can see a small window of opportunity for another process to enter
            // the critical section here...
            cm.Add(lockKey, lockKey, 
                   CacheItemPriority.NotRemovable, null, 
                   new NeverExpired());

            object newValue = SomeLengthyWebserviceCall();
            cm.Remove(removedKey);
            Utilities.AddToCache(removedKey, newValue);

            cm.Remove("lockkey");
        }
    }
}

有没有办法保证关键部分以确保我不会两次调用网络服务?

编辑我应该补充一点,我不能使用共享文件,因为部署策略会阻止它。

StackOverflow 参考资料:

4

2 回答 2

4

您必须涉及所有人共有的一些外部锁定获取。例如,SQL 中的表 t 具有一行和一个锁字段,您将在其中获取锁:

set transaction isolation serializable;
update t set lock = 1 where lock = 0;

检查受影响的行,如果它的 1 有锁,则通过将锁更新为 0 来释放它。这实质上是在 SQLServer 的行锁上捎带,如果两个同时启动,只有一个会在 S 锁后获得 U 锁,另一个会阻塞并随后返回 0 行受影响(因为第一个事务将其翻转为 1)。

于 2011-03-18T15:48:01.703 回答
1

我建议您将创建/返回锁句柄的逻辑移动到数据库并将它们组合起来,这将保证它始终是一个拥有锁的进程。

所以数据库可能有一个存储过程,你要求一个锁,它要么返回空结果(不成功),要么创建一条记录并返回它。

于 2011-03-18T15:49:20.707 回答