我们遇到的情况是我们在相当昂贵的API上调用函数。让我们称之为API.ExpensiveCall()
在Web应用程序中经常调用此ExpensiveCall()
。虽然一个用户不会注意到它,但是当你有5个左右的同时用户时它会变得明显。
我们希望缓存这些结果。但由于API.ExpensiveCall()
本质上是我们的黑盒子,我们无法使缓存无效,知道何时刷新。
所以我们提出的解决方案是创建一个Windows服务,它每隔十秒钟调用一次API.ExpensiveCall()
,然后将结果保存到Web应用程序已经使用的本地数据库中。
这样,如果网站上有1个用户或20个以上的用户,那么ExpensiveCall()
每10秒只会被调用一次。它是API.ExpensiveCall()
连接到的外部系统上非常受控制的负载。
问题
我们的项目经理不同意这一点。出于某种原因,他认为每10秒进行一次定时更新是一个坏主意,因为在他看来,外部系统负担过重。
但是如果我们不对它做任何事情,并且保持原样,没有任何缓存,它不仅会降低Web应用程序的性能,而且肯定会导致每秒超过一个ExpensiveCall在外部系统上。并且该数量将根据Web应用程序上的用户数量而增加。
我想问你这种缓存方式真的是这么糟糕吗?您是否听说过使用这种缓存方法的其他系统?如果它是一个如此糟糕的主意,当它是一个黑盒子时,有没有其他更好的方法来缓存系统的结果?
修改
您的回答似乎表明我应该使用ASP.Net的内存缓存机制的超时功能。
我喜欢超时的想法。我现在看到的唯一(小)问题是,当超时到期并且是时候调用ExpensiveCall()时,它将是一个阻塞调用。而不是查询本地表,通过在单独的进程中不断刷新来保持最新状态。这是我对投票理念有吸引力的事情。虽然我必须承认每10秒钟进行一次投票确实感觉很奇怪,这就是为什么我会对它进行调查。
答案 0 :(得分:4)
您的项目经理可能是正确的:当没有连续五分钟的请求时,您仍然会进行轮询,导致对昂贵的功能进行30次不必要的调用。
通常解决这些问题的方式如下:每当您需要来自昂贵呼叫的数据时,您都会检查缓存。如果不存在,则调用昂贵的函数并将数据存储在缓存中,添加时间戳。如果是,则检查数据的年龄(因此是时间戳)。如果它超过10秒,则无论如何都要调用昂贵的功能,并更新缓存。否则,请使用缓存的数据。 ASP.NET的内置缓存API允许您以相当小的工作量完成所有这些工作 - 只需将缓存过期时间设置为10秒,您就应该很好。
通过这种方式,您将获得两全其美 - 您的数据永远不会超过10秒,但您仍然可以避免持续轮询。
答案 1 :(得分:3)
如果“一个用户不明显”,那么你应该让第一次调用方法缓存结果,然后每隔X秒使缓存失效。这样,每X秒只能调用一次,这会影响性能。
我同意每10秒点击一次外部服务听起来最好是不冷却,最坏的情况是拒绝服务攻击(取决于通话的重量和提供商的容量)。
答案 2 :(得分:2)
使用时间跨度(例如10秒)使缓存无效没有任何根本性的错误。
如果从业务角度来看,用户看到过时10秒的数据的后果是可以接受的,那么这是一个非常合理的解决方案。您可能不希望实际使缓存过期,直到您在10秒钟后收到第一个请求(假设对单个用户的影响不是很大...如果处理对该用户的请求的延迟是明显的,将缓存预先过期是好的。
答案 3 :(得分:1)
听起来这可能是单例操作(例如,所有请求都将使用相同的操作结果)。如果是这种情况,它听起来像是可以在HttpRuntime.Cache
中缓存的内容。
var mutex = new Mutex();
public IEnumerable<MyData> GetMyData()
{
mutex.WaitOne();
var cache = HttpRuntime.Cache;
var data = cache["MyData"] as IEnumerable<MyData>;
if (data == null)
{
data = API.ExpensiveCall();
cache.Add("MyData", data, null,
DateTime.Now.AddSeconds(60), Cache.NoSlidingExpiration, CacheItemPriority.High, null);
}
mutex.ReleaseMutex();
return data;
}
从这个意义上说,你调用GetMyData
对应用程序缓存进行检查,如果数据不存在(或已经过期,并且已被删除),我们进行昂贵的调用,缓存结果并返回数据。
答案 4 :(得分:1)
查看my response to this question - 它描述了一种确保标准缓存中新数据的方法。似乎它可能直接解决你的情况。