排查Azure连续WebJobs自动重启及相关代码异常问题
排查Azure连续WebJob自动重启问题并解决死锁困境
首先,咱们先聚焦最核心的问题:查明WebJob自动重启的原因,再逐步解决重启后作业互相等待的死锁问题,以及你之前遇到的FileSystemWatcher初始化错误。
一、排查WebJob自动重启的原因
从你的描述来看,重启发生在夜间且无手动部署操作,CPU内存使用率也始终低于40%,可以从以下几个方向深入排查:
1. 查看Azure平台级日志与通知
- 实时查看Log Stream:在Azure门户进入你的App Service → Monitoring → Log Stream,能实时获取平台层面的重启触发信息,比如应用池回收、主机维护通知等。
- 开启Diagnostic Settings:将App Service日志发送到Log Analytics,方便回溯历史重启事件,日志里会明确标注重启的触发源(如资源限制、平台维护)。
- 检查资源健康与通知:在App Service的Overview页面顶部查看Notification区域,Azure会提前通知底层主机维护;同时在Resource Health里可以查看最近的服务中断或重启记录。
- 通过Kudu站点深挖:访问
https://<你的应用名>.scm.azurewebsites.net,进入Process Explorer查看应用池回收历史;也可以在Debug console里打开D:\home\LogFiles\WebJobs\Continuous\<你的WebJob名>目录,里面的系统日志可能包含重启前的关键细节。
2. 排查应用池回收配置
Azure App Service默认的应用池规则可能触发夜间重启:
- 进入App Service → Configuration → General settings,检查Idle timeout(空闲超时)和Regular time interval(固定时间回收)。如果设置了较短的空闲超时(比如默认20分钟),夜间无用户请求时可能触发回收,你可以根据业务需求调大或禁用空闲超时。
- 另外,即使总内存使用率不高,应用池也可能因私有内存限制触发回收,可在Kudu的Process Explorer查看w3wp.exe的私有内存使用情况。
3. 开启WebJob详细诊断日志
在App Service的Configuration → Application settings里添加以下配置,获取更精细的WebJob运行日志:
- 设置
WEBJOBS_DIAGNOSTICS_LOG_LEVEL为Verbose - 设置
WEBJOBS_LOGS_MAX_SIZE_IN_BYTES为10485760(10MB)或更大值
这些日志能帮你捕捉到重启前的异常信号或系统通知。
二、修复FileSystemWatcher初始化错误
你后来修改的代码方向完全正确!之前的错误是把文件名传给了FileSystemWatcher的构造函数,而它需要的是目录路径。这里给你补充一些优化细节:
// 获取关机文件路径 _shutdownFile = Environment.GetEnvironmentVariable("WEBJOBS_SHUTDOWN_FILE"); if (string.IsNullOrEmpty(_shutdownFile)) { _log.Info("未检测到WEBJOBS_SHUTDOWN_FILE环境变量,跳过关机监听(本地调试时可能出现此情况)"); return; } _log.Info("监听关机文件:" + _shutdownFile); var folder = Path.GetDirectoryName(_shutdownFile); var fileName = Path.GetFileName(_shutdownFile); if (folder != null && !string.IsNullOrEmpty(fileName)) { // 直接指定目录和文件名过滤,避免后续事件判断 var fileSystemWatcher = new FileSystemWatcher(folder, fileName); fileSystemWatcher.Created += OnAzureRestart; fileSystemWatcher.Changed += OnAzureRestart; fileSystemWatcher.NotifyFilter = NotifyFilters.CreationTime | NotifyFilters.FileName | NotifyFilters.LastWrite; fileSystemWatcher.IncludeSubdirectories = false; fileSystemWatcher.EnableRaisingEvents = true; _log.Info("关机文件监听已设置完成"); } // 关机事件处理方法 private void OnAzureRestart(object sender, FileSystemEventArgs e) { _log.Info($"检测到关机信号:{e.FullPath}"); // 核心操作:更新运行作业表,释放当前处理的用户锁 ReleaseCurrentUserLock(); _log.Info("优雅关机清理完成,即将退出"); Environment.Exit(0); }
三、解决重启后作业互相等待的死锁问题
即使找到了重启原因,也要做好异常重启的兜底方案,建议给运行作业表添加心跳与超时机制:
- 改造运行作业表结构:新增
LastHeartbeat(DateTime类型)字段,记录作业的最后活跃时间。 - 优化作业处理逻辑:
- 当作业要处理某个用户的文件时,先查询运行作业表:
- 若该用户无锁记录,插入新记录(包含作业ID、userId、当前时间作为LastHeartbeat)。
- 若已有锁记录,检查
LastHeartbeat是否超过超时时间(比如10分钟):- 超时则判定原作业已异常终止,更新记录为当前作业ID和最新心跳。
- 未超时则跳过该用户的文件,等待下一轮检查。
- 文件处理过程中,定期(比如每5分钟)更新
LastHeartbeat为当前时间。
- 当作业要处理某个用户的文件时,先查询运行作业表:
- 优雅关机时主动清理:在
OnAzureRestart方法里,主动将当前处理的用户锁标记为已释放,或直接删除对应记录。
这个机制能确保即使作业异常重启,锁也会在超时后自动释放,彻底避免死锁。
总结
- 先通过Azure平台日志、Kudu站点定位重启的根本原因(大概率是应用池回收或平台维护)。
- 完善优雅关机逻辑,确保重启时及时释放用户锁。
- 添加锁的心跳与超时机制,作为异常重启后的兜底保障。
内容的提问来源于stack exchange,提问作者Paul Meems




