我觉得自己已经碰到了一堵砖墙而又无法再深入了解,可能一直在思考它。
我正在创建一个缓存处理类,它从数据库中获取一些数据,检查它的时间,然后如果它已经过时它将从第三方API请求新数据。
但我坚持并发。第三方API不喜欢太多的重复调用,因此在Get()
被同时调用两次并且都试图更新数据的不幸事件中,这是一个不幸的结果,我想删除它可能性。
但我也希望能够处理不同的路径,同时阻止其他路径尝试更新,而一条路径已经在获取数据。
public class CacheHandler : ICacheHandler
{
private IRequester Requester;
private ICache Cache;
public CacheHandler()
{
Requester = new RequesterHttp();
Cache = new CacheDB();
}
public T Get<T>(string path)
{
// Get data from cache
var cacheRes = Cache.Get(path);
// If not null, deserialize to object and compare if its time to update from third party or to return cached version
if (cacheRes != null)
{
// Deserialize xml from db
Response<T> deserialized = Xml.Deserialize<Response<T>>(cacheRes);
// Compare xml from db cached untill with current time
int compare = DateTime.Compare(DateTime.UtcNow, deserialized.CachedUntil);
// If cacheduntill is later than current time, return cached info
if (compare <= 0)
return deserialized.Result;
}
// If no cache, or its time to update current cache - Get third party xml
var reqRes = Requester.Get(path);
// Save it to cache
Cache.Set(path, reqRes);
// Desearialize
Response<T> deserialized2 = Xml.Deserialize<Response<T>>(reqRes);
// Return it to user
return deserialized2.Result;
}
}
但我的问题怎么样?
我想我可以将它全部合并到CacheHandler
并对数据库执行悲观并发,但这会破坏将来能够扩展的想法。
答案 0 :(得分:0)
在最基本的级别,您可以将此调用包装在一个锁中,以防止您描述的竞争条件:
public class CacheHandler : ICacheHandler
{
private IRequester Requester;
private ICache Cache;
private object syncLock = new object();
public CacheHandler()
{
Requester = new RequesterHttp();
Cache = new CacheDB();
}
public T Get<T>(string path)
{
lock(syncLock)
{
// Get data from cache
var cacheRes = Cache.Get(path);
// If not null, deserialize to object and compare if its time to update from third party or to return cached version
if (cacheRes != null)
{
// Deserialize xml from db
Response<T> deserialized = Xml.Deserialize<Response<T>>(cacheRes);
// Compare xml from db cached untill with current time
int compare = DateTime.Compare(DateTime.UtcNow, deserialized.CachedUntil);
// If cacheduntill is later than current time, return cached info
if (compare <= 0)
return deserialized.Result;
}
// If no cache, or its time to update current cache - Get third party xml
var reqRes = Requester.Get(path);
// Save it to cache
Cache.Set(path, reqRes);
// Desearialize
Response<T> deserialized2 = Xml.Deserialize<Response<T>>(reqRes);
// Return it to user
return deserialized2.Result;
}
}
}
然而,从多种意义上说,这不是速度的最佳策略。理想情况下,您可能希望将此缓存数据存储在内存中,然后异步将其保存到数据库中。这样,您的碰撞窗口会变小(尽管不会完全消失)。
我没有看到围绕该方法的锁的方法,但如果你利用内存缓存,你将锁定一个更短的时间,而不是整个缓慢的从数据库往返(相比之下)。