.NET Core 8中IMemoryCache处理OTP的缓存问题排查求助
.NET Core 8中IMemoryCache处理OTP的问题排查与优化建议
问题描述
我们在基于.NET Core 8的应用中使用IMemoryCache处理OTP时遇到问题,具体出现在resetPasswordHandle和CheckPasswordOtpHandle操作的OTP验证环节。
问题详情
存OTP逻辑(resetPasswordHandle)
public async Task<AccountResponseBase> Handle(ForgetPasswordRequest req) { // 其他业务代码 var codeCacheKey = $"{CacheConsts.FORGOT_PASS_CACHE_KEY_PREFIX}:{req.email}"; _cache.Set(codeCacheKey, code, TimeSpan.FromMinutes(10)); }
验证OTP逻辑(CheckPasswordOtpHandle)
public async Task<PzJwtResponse> Handle(ForgetPasswordOtpRequest req) { // 其他业务代码 var cacheKey = $"{CacheConsts.FORGOT_PASS_CACHE_KEY_PREFIX}:{user.Email}"; if (!_cache.TryGetValue(cacheKey, out string cachedCode)) { return Failure(AccountFailureStatusTypes.CodeNotFoundOrExpired); } }
现象
偶尔会出现持续返回CodeNotFoundOrExpired错误的情况,即使OTP已正确存入缓存,该问题需重置IIS后才能恢复正常。
问题排查与优化建议
1. 缓存未被正确获取/提前过期且需重置IIS恢复的可能原因
- 进程回收导致缓存丢失:IIS应用池会因内存过高、空闲超时、定期回收等触发进程重启,
IMemoryCache是进程内缓存,进程重启后所有缓存数据会被清空,此时OTP无法被获取;重置IIS本质是重启应用进程,重新初始化缓存,因此恢复正常。 - 缓存键不一致:检查
req.email与user.Email的格式、大小写是否完全一致(比如邮箱是否带空格、大小写差异),键不匹配会直接导致无法找到缓存项。 - 内存压力触发缓存驱逐:当服务器内存不足时,
IMemoryCache会自动驱逐优先级较低的缓存项(默认优先级为Normal),即使未到过期时间,OTP也可能被提前清理。 - 缓存实例注入错误:若
IMemoryCache未注册为单例(错误注册为Scoped或Transient),不同请求会使用独立的缓存实例,存入的OTP无法在其他实例中被获取。
2. 提升缓存稳定性的建议
- 优先切换到分布式缓存(如Redis):
IMemoryCache仅适用于单实例部署场景,若应用是多实例(负载均衡)或需要跨进程共享缓存,必须使用分布式缓存。Redis支持过期时间配置、数据持久化,能彻底避免进程重启导致的缓存丢失问题,同时多实例应用可共享OTP缓存,验证逻辑更可靠。 - 统一缓存键生成规则:对邮箱做标准化处理(转小写、去除首尾空格),确保存、取缓存的键完全一致,示例代码:
var normalizedEmail = req.email.Trim().ToLowerInvariant(); var codeCacheKey = $"{CacheConsts.FORGOT_PASS_CACHE_KEY_PREFIX}:{normalizedEmail}"; - 设置缓存项高优先级:给OTP这类关键缓存项设置高优先级,降低被内存压力驱逐的概率:
_cache.Set(codeCacheKey, code, new MemoryCacheEntryOptions { AbsoluteExpirationRelativeToNow = TimeSpan.FromMinutes(10), Priority = CacheItemPriority.High }); - 添加缓存操作日志:在存、取缓存的位置添加日志,记录缓存键、操作结果、过期时间,便于问题发生时快速定位是存失败还是取失败。
3. .NET Core 8中影响IMemoryCache生产环境行为的特定配置
- 缓存大小限制:通过
MemoryCacheOptions.SizeLimit设置缓存的最大容量(需配合缓存项的Size属性生效),避免缓存无限制占用内存。配置示例:builder.Services.AddMemoryCache(options => { options.SizeLimit = 1024 * 1024; // 自定义容量,单位需与缓存项Size匹配 }); - 缓存项压缩:.NET Core 8新增
MemoryCacheOptions.Compress配置,开启后会对超过阈值的缓存项进行压缩,节省内存空间:builder.Services.AddMemoryCache(options => { options.Compress = true; options.CompressionThreshold = 1024; // 超过1KB的缓存项才会被压缩 }); - 过期项扫描频率:通过
MemoryCacheOptions.ExpirationScanFrequency调整过期项的扫描间隔(默认1分钟),间隔过长会导致过期项延迟清理,但不会影响已过期项的获取(获取时会实时检查过期时间)。 - 缓存统计追踪:开启
MemoryCacheOptions.TrackStatistics可以收集缓存命中、驱逐等统计数据,便于监控缓存状态,但会带来一定性能开销,按需开启。
内容的提问来源于stack exchange,提问作者Vladimir Potapov
相关产品推荐
相关产品推荐

