我正在使用ServiceStack.Redis实现来缓存通过Web API接口提供的事件。应将这些事件插入缓存中并在一段时间后自动删除(例如3天):
private readonly IRedisTypedClient<CachedMonitoringEvent> _eventsCache;
public EventMonitorCache([NotNull]IRedisTypedClient<CachedMonitoringEvent> eventsCache)
{
_eventsCache = eventsCache;
}
public void Dispose()
{
//Release connections again
_eventsCache.Dispose();
}
public void AddOrUpdate(MonitoringEvent monitoringEvent)
{
if (monitoringEvent == null)
return;
try
{
var cacheExpiresAt = DateTime.Now.Add(CacheExpirationDuration);
CachedMonitoringEvent cachedEvent;
string eventKey = CachedMonitoringEvent.CreateUrnId(monitoringEvent);
if (_eventsCache.ContainsKey(eventKey))
{
cachedEvent = _eventsCache[eventKey];
cachedEvent.SetExpiresAt(cacheExpiresAt);
cachedEvent.MonitoringEvent = monitoringEvent;
}
else
cachedEvent = new CachedMonitoringEvent(monitoringEvent, cacheExpiresAt);
_eventsCache.SetEntry(eventKey, cachedEvent, CacheExpirationDuration);
}
catch (Exception ex)
{
Log.Error("Error while caching MonitoringEvent", ex);
}
}
public List<MonitoringEvent> GetAll()
{
IList<CachedMonitoringEvent> allEvents = _eventsCache.GetAll();
return allEvents
.Where(e => e.MonitoringEvent != null)
.Select(e => e.MonitoringEvent)
.ToList();
}
StructureMap 3注册表如下所示:
public class RedisRegistry : Registry
{
private readonly static RedisConfiguration RedisConfiguration = Config.Feeder.Redis;
public RedisRegistry()
{
For<IRedisClientsManager>().Singleton().Use(BuildRedisClientsManager());
For<IRedisTypedClient<CachedMonitoringEvent>>()
.AddInstances(i => i.ConstructedBy(c => c.GetInstance<IRedisClientsManager>()
.GetClient().GetTypedClient<CachedMonitoringEvent>()));
}
private static IRedisClientsManager BuildRedisClientsManager()
{
return new PooledRedisClientManager(RedisConfiguration.Host + ":" + RedisConfiguration.Port);
}
}
第一种情况是检索所有缓存事件(数百个)并通过ODataV3和ODataV4将其传送到Excel PowerTools以进行可视化。这按预期工作:
public class MonitoringEventsODataV3Controller : EntitySetController<MonitoringEvent, string>
{
private readonly IEventMonitorCache _eventMonitorCache;
public MonitoringEventsODataV3Controller([NotNull]IEventMonitorCache eventMonitorCache)
{
_eventMonitorCache = eventMonitorCache;
}
[ODataRoute("MonitoringEvents")]
[EnableQuery(AllowedQueryOptions = AllowedQueryOptions.All)]
public override IQueryable<MonitoringEvent> Get()
{
var allEvents = _eventMonitorCache.GetAll();
return allEvents.AsQueryable();
}
}
但我正在努力的是Excel PowerQuery所做的OData过滤。我知道我还没有进行任何服务器端过滤,但目前并不重要。当我过滤任何属性并单击刷新时,PowerQuery同时发送多个请求(我最多看到三个)。我相信它首先获取整个数据集,然后使用过滤器执行以下请求。这导致ServiceStack.Redis的各种例外:
An exception of type 'ServiceStack.Redis.RedisResponseException' occurred in ServiceStack.Redis.dll but was not handled in user code
使用其他信息:
Additional information: Unknown reply on multi-request: 117246333|company|osdmonitoringpreinst|2014-12-22|113917, sPort: 54980, LastCommand:
或者
Additional information: Invalid termination, sPort: 54980, LastCommand:
或者
Additional information: Unknown reply on multi-request: 57, sPort: 54980, LastCommand:
或者
Additional information: Type definitions should start with a '{', expecting serialized type 'CachedMonitoringEvent', got string starting with: u259447|company|osdmonitoringpreinst|2014-12-18|1
所有这些例外都发生在_eventsCache.GetAll()
。
必须有一些我缺少的东西。我确信Redis能够在同一套上“同时”处理大量请求,但显然我做错了。 :)
顺便说一下:Redis 2.8.12正在Windows Server 2008计算机上运行(2012年很快)。
感谢您的任何建议!
答案 0 :(得分:1)
错误消息表示在多个线程中使用RedisClient的非线程安全实例,因为它正在获取对它不期望/发送的请求的响应。
为了确保您正确使用我只会传入线程安全IRedisClientsManager
单例,例如:
public EventMonitorCache([NotNull]IRedisClientsManager redisManager)
{
this.redisManager = redisManager;
}
然后在您的方法中明确地解析和处理redis客户端,例如:
public void AddOrUpdate(MonitoringEvent monitoringEvent)
{
if (monitoringEvent == null)
return;
try
{
using (var redis = this.redisManager.GetClient())
{
var _eventsCache = redis.As<CachedMonitoringEvent>();
var cacheExpiresAt = DateTime.Now.Add(CacheExpirationDuration);
CachedMonitoringEvent cachedEvent;
string eventKey = CachedMonitoringEvent.CreateUrnId(monitoringEvent);
if (_eventsCache.ContainsKey(eventKey))
{
cachedEvent = _eventsCache[eventKey];
cachedEvent.SetExpiresAt(cacheExpiresAt);
cachedEvent.MonitoringEvent = monitoringEvent;
}
else
cachedEvent = new CachedMonitoringEvent(monitoringEvent, cacheExpiresAt);
_eventsCache.SetEntry(eventKey, cachedEvent, CacheExpirationDuration);
}
}
catch (Exception ex)
{
Log.Error("Error while caching MonitoringEvent", ex);
}
}
在GetAll()中:
public List<MonitoringEvent> GetAll()
{
using (var redis = this.redisManager.GetClient())
{
var _eventsCache = redis.As<CachedMonitoringEvent>();
IList<CachedMonitoringEvent> allEvents = _eventsCache.GetAll();
return allEvents
.Where(e => e.MonitoringEvent != null)
.Select(e => e.MonitoringEvent)
.ToList();
}
}
无论您的EventMonitorCache
依赖关系的生命周期是什么,这都将起作用,例如:由于EventMonitorCache
不再保留在redis服务器连接上,因此保持单身是安全的。