如何在IIS应用中启动AppPool停止后仍不退出的独立进程
IIS托管应用启动的独立更新进程随AppPool下线异常退出问题
问题场景
- 自研
.exe更新程序用于更新IIS站点下运行的主应用,当前AppPool运行身份为LocalService或指定自定义用户。 - 非IIS托管场景下更新全流程运行正常;部署为IIS应用后,更新程序总会在IIS AppPool下线后数毫秒无异常终止,代码外层全局
try/catch未捕获到任何异常信息。 - 任务管理器中可观察到更新程序拥有独立PID。
相关实现代码
更新程序启动逻辑
public bool ExecuteCommand(string domain, string userName, SecureString password, string extractedFolderPath, string arguments) { Arguments.CheckNonEmptyString(extractedFolderPath, nameof(extractedFolderPath)); if (Directory.Exists(extractedFolderPath) && File.Exists(Path.Combine(extractedFolderPath, UPDATER_EXE_FILENAME))) { Logger.Info($"Executing command with {UPDATER_EXE_FILENAME} and arguments {arguments} in working directory {extractedFolderPath}"); var startInfo = new ProcessStartInfo() { UseShellExecute = true, RedirectStandardOutput = false, FileName = Path.Combine(extractedFolderPath, UPDATER_EXE_FILENAME), Arguments = arguments, WorkingDirectory = extractedFolderPath, //UserName = userName, //Domain = domain, //Password = password }; Process process = Process.Start(startInfo); Logger.Info($"Startet update process. [PID: {process.Id}]"); return process != null; } else return false; }
AppPool停止逻辑
Logger.Debug($"Process with id '{processId.Value}' will be shut down!"); Process.GetProcessById(processId.Value).Close(); if (ProcessExists(processId.Value)) Process.GetProcessById(processId.Value).Kill();
现场运行日志
Updater-2022-06-28 12:10:16-Debug: Process with id '14868' still running! at Updater.IISHelper.StopApplication(ApplicationPool pool, Nullable`1 processId) at Updater.Program.Main(String[] args) Updater-2022-06-28 12:10:16-Debug: Process with id '14868' will be shut down! at Updater.IISHelper.StopApplication(ApplicationPool pool, Nullable`1 processId) at Updater.Program.Main(String[] args) Updater-2022-06-28 12:10:16-Info: Backup will be created shortly at path: 'F:\path\Temp\software_update\App_V1.0.1\backup' at Updater.Program.CreateBackup(String backupPath, String applicationPath) at Updater.Program.Main(String[] args) Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\i18n'! at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles) at Updater.Program.CreateBackup(String backupPath, String applicationPath) at Updater.Program.Main(String[] args) ... ... ... Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\images'! at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles) at Updater.Program.CreateBackup(String backupPath, String applicationPath) at Updater.Program.Main(String[] args) Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\Logs'! at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles) at Updater.Program.CreateBackup(String backupPath, String applicationPath) at Updater.Program.Main(String[] args) Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\wwwroot\assets\svg'! at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles) at Updater.Program.CreateBackup(String backupPath, String applicationPath) at Updater.Program.Main(String[] args) Updater-2022-06-28 12:10:16-Debug: Created directory at path 'F:\path\Temp\software_update\App_V1.0.1\backup\brgData\transfer\persist-Brd-2-Skr\persist-Mqtt-Brd-2-Skr'! at Updater.Program.CopyContent(String sourcePath, String targetPath, List`1 ignoreFolders, List`1 ignoreFiles) at Updater.Program.CreateBackup(String backupPath, String applicationPath) at Updater.Program.Main(String[] args)
问题根因说明
- 进程绑定关系结论:哪怕更新程序拥有独立PID、通过
UseShellExecute = true方式启动,它依然和启动它的主应用所属AppPool存在强绑定关联。 - 无异常退出原因:IIS会为每个应用程序池创建独立的Windows Job对象,默认开启
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSE标志,所有由该AppPool进程拉起的子进程(包含Shell启动的独立进程)都会被自动纳入这个Job对象的管控范围。当AppPool下线时,对应的Job对象句柄会被关闭,Windows内核会直接终止该Job关联的所有进程。这个终止动作发生在内核层,不会走用户态进程的异常分发流程,因此用户代码中配置的所有异常捕获逻辑都无法感知,也不会留下异常日志。
方案验证
后续调整为通过Windows服务监听更新事件、再触发更新程序执行更新的方案可稳定运行,本质是因为此时更新程序的启动源脱离了IIS AppPool的Job对象管控范围,不会随AppPool生命周期被连带终止,和更新逻辑本身无关。
如果不希望引入Windows服务,也可以通过Windows API主动将新启动的更新进程从当前AppPool所属Job对象中移除,同样可以避免进程被连带终止。
内容的提问来源于stack exchange,提问作者Timo K.
相关产品推荐
相关产品推荐

