最近在 ASP.NET Core 10 项目中遇到的问题,出现问题的 C# 代码如下:
var cacheOptions = new HybridCacheEntryOptions
{
LocalCacheExpiration = TimeSpan.FromMinutes(1),
Flags = HybridCacheEntryFlags.DisableDistributedCache
};
return await hybridCache.GetOrCreateAsync(
cacheKey,
async token => await GetRuleListByCheckee<TCheckee>(),
cacheOptions);
因为序列化失败,才发现 HybridCache 在使用内存缓存(in-memory cache)也进行了序列化操作
MessagePack.MessagePackSerializationException: Failed to serialize ...
---> MessagePack.FormatterNotRegisteredException: ...
at MessagePack.FormatterResolverExtensions.Throw(Type t, IFormatterResolver resolver)
at MessagePack.Formatters.ListFormatter`1.Serialize(MessagePackWriter& writer, List`1 value, MessagePackSerializerOptions options)
at MessagePack.MessagePackSerializer.Serialize[T](MessagePackWriter& writer, T value, MessagePackSerializerOptions options)
--- End of inner exception stack trace ---
at MessagePack.MessagePackSerializer.Serialize[T](MessagePackWriter& writer, T value, MessagePackSerializerOptions options)
at MessagePack.MessagePackSerializer.Serialize[T](IBufferWriter`1 writer, T value, MessagePackSerializerOptions options, CancellationToken cancellationToken)
at Cnblogs.Cache.Hybrid.Extensions.HybridCacheMessagePackSerializer`1.Serialize(T value, IBufferWriter`1 target)
at Microsoft.Extensions.Caching.Hybrid.Internal.DefaultHybridCache.TrySerialize[T](T value, BufferChunk& buffer, IHybridCacheSerializer`1& serializer)
at Microsoft.Extensions.Caching.Hybrid.Internal.DefaultHybridCache.StampedeState`2.BackgroundFetchAsync()
at Microsoft.Extensions.Caching.Hybrid.Internal.DefaultHybridCache.StampedeState`2.<UnwrapReservedAsync>g__AwaitedAsync|18_0(ILogger log, Task`1 task)
如何让 HybridCache 不进行这个序列化操作?
通过 HybridCache 的开源代码 DefaultHybridCache.StampedeStateT.cs#L330 知道了,满足以下条件才不进行序列化
// If we're writing this value *anywhere*, we're going to need to serialize; this is obvious
// in the case of L2, but we also need it for L1, because MemoryCache might be enforcing
// SizeLimit (we can't know - it is an abstraction), and for *that* we need to know the item size.
// Likewise, if we're writing to a MutableCacheItem, we'll be serializing *anyway* for the payload.
//
// Rephrasing that: the only scenario in which we *do not* need to serialize is if:
// - it is an ImmutableCacheItem (so we don't need bytes for the CacheItem, L1)
// - we're not writing to L2
CacheItem cacheItem = CacheItem;
bool skipSerialize = cacheItem is ImmutableCacheItem<T> && (activeFlags & FlagsDisableL1AndL2Write) == FlagsDisableL1AndL2Write;