.NET6 Blazor Server应用池回收致集合项重复Bug排查求助
.NET6 Blazor Server Azure应用池回收后集合映射重复问题分析
一、应用池回收时的SignalR Circuit行为
Azure App Service默认采用滚动部署策略:新应用池启动后,旧应用池不会立即停止,会继续处理现有请求直到超时或完成,形成新旧实例并行运行的阶段。
对于Blazor Server的SignalR Circuits:
- 旧池中的活跃Circuit会继续在旧实例上运行,直到用户主动断开或旧池被强制回收;
- 新的请求(包括重连请求)会被路由到新实例;
- 旧池收到停止信号后,会逐步终止Circuit,客户端会触发自动重连,重连成功后Circuit迁移到新实例。
这种双实例并行的场景,会导致全局静态资源在两个实例中分别初始化,若初始化逻辑非线程安全,高并发下就会出现异常。
二、核心排查方向
1. Mapster全局配置的线程安全问题
- 检查
TypeAdapterConfig是否为全局静态实例,且初始化过程是否存在并发修改。例如:- 是否在请求线程中延迟初始化映射配置(如第一次请求时才注册规则);
- 初始化时未加锁,导致多线程同时注册同一映射规则,最终映射器对集合项执行多次转换添加,造成重复。
- 验证方法:将Mapster配置改为启动时一次性初始化,用
lock确保初始化逻辑仅执行一次,或使用Mapster提供的线程安全配置API。
2. 集合映射规则的重复注册
- 检查Mapster集合映射的具体配置:是否多次注册同一
List<VM>→List<DM>的映射规则,或配置了PreserveReference等可能导致重复的选项。重复注册会让映射器在转换时多次处理集合项,最终生成重复数据。
3. 双实例并行下的状态隔离问题
- 确认是否有代码在Circuit级别持有映射器实例,但初始化时未正确隔离状态;若全局静态状态存在跨实例共享逻辑(如静态缓存),也需排查是否出现状态不一致。
4. 部署策略的影响验证
- 对比滚动部署和「先停止再启动」的差异:后者无实例并行,问题消失,进一步佐证问题出在双实例并行时的并发初始化。
三、修复建议
- 调整部署策略:使用Azure部署槽交换,先停止旧槽再启动新槽,避免实例并行;
- 修复Mapster初始化逻辑:在
Program.cs中一次性完成全局配置,添加锁确保线程安全,示例:private static readonly object _mapsterLock = new object(); if (!TypeAdapterConfig.GlobalSettings.HasConfigured<ViewModel, DomainModel>()) { lock (_mapsterLock) { if (!TypeAdapterConfig.GlobalSettings.HasConfigured<ViewModel, DomainModel>()) { TypeAdapterConfig<ViewModel, DomainModel>.NewConfig() .Map(dest => dest.Items, src => src.Items); // 其他映射规则 } } } - 添加初始化日志:记录Mapster配置初始化的线程ID和次数,生产环境中排查是否存在并发初始化。
内容的提问来源于stack exchange,提问作者Trygve
相关产品推荐
相关产品推荐

