C# .NET5 Selenium WebDriver IIS生产环境偶发60秒超时异常
.NET 5 IIS托管环境下Selenium ChromeDriver启动阶段偶发60秒超时异常排查
问题特征
- 技术栈:.NET 5.0 + C# + Selenium.WebDriver + ChromeDriver 103
- 异常规律:仅在ChromeDriver启动阶段触发,业务执行过程无同类异常;开发环境无法复现,仅Windows Server 2016 + IIS托管的生产环境偶发
- 异常触发点:
driver.Close()调用位置,对应代码逻辑:
if (driver != null) { driver.Close(); // 异常抛出位置 driver.Quit(); }
- 核心异常表现:向ChromeDriver本地服务发送
/session/{sessionId}/window请求时,触发HttpClient默认60秒超时,底层Socket返回错误码995(I/O操作因线程退出或应用请求被中止),完整堆栈如下:
OpenQA.Selenium.WebDriverException: The HTTP request to the remote WebDriver server for URL http://localhost:63127/session/32e3a5ce458f1cc856653180a734b3dc/window timed out after 60 seconds. ---> System.Threading.Tasks.TaskCanceledException: The request was canceled due to the configured HttpClient.Timeout of 60 seconds elapsing. ---> System.TimeoutException: The operation was canceled. ---> System.Threading.Tasks.TaskCanceledException: The operation was canceled. ---> System.IO.IOException: Unable to read data from the transport connection: The I/O operation has been aborted because of either a thread exit or an application request.. ---> System.Net.Sockets.SocketException (995): The I/O operation has been aborted because of either a thread exit or an application request. --- End of inner exception stack trace --- at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.ThrowException(SocketError error, CancellationToken cancellationToken) at System.Net.Sockets.Socket.AwaitableSocketAsyncEventArgs.GetResult(Int16 token) at System.Net.Http.HttpConnection.FillAsync(Boolean async) at System.Net.Http.HttpConnection.ReadNextResponseHeaderLineAsync(Boolean async, Boolean foldedHeadersAllowed) at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) --- End of inner exception stack trace --- at System.Net.Http.HttpConnection.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken) at System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken) at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken) --- End of inner exception stack trace --- --- End of inner exception stack trace --- at System.Net.Http.HttpClient.SendAsyncCore(HttpRequestMessage request, HttpCompletionOption completionOption, Boolean async, Boolean emitTelemetryStartStop, CancellationToken cancellationToken) at OpenQA.Selenium.Remote.HttpCommandExecutor.MakeHttpRequest(HttpRequestInfo requestInfo) at OpenQA.Selenium.Remote.HttpCommandExecutor.Execute(Command commandToExecute) --- End of inner exception stack trace --- at OpenQA.Selenium.Remote.HttpCommandExecutor.Execute(Command commandToExecute) at OpenQA.Selenium.Remote.DriverServiceCommandExecutor.Execute(Command commandToExecute) at OpenQA.Selenium.WebDriver.Execute(String driverCommandToExecute, Dictionary`2 parameters) at OpenQA.Selenium.WebDriver.Close() at ServicioScraper.Controllers.RCVController.GetVentasAsync(Int32 mes, Int32 anio, BasicData input) in C:\Users\Gonzalo Bustamante\source\repos\chilesystems\ServicioScraper\Controllers\RCVController.cs:line 252
- 原有ChromeDriver初始化配置:
var options = new ChromeOptions(); options.AddUserProfilePreference("download.default_directory", System.IO.Path.GetFullPath(downloadFolder)); options.AddUserProfilePreference("disable-popup-blocking", true); options.AddUserProfilePreference("download.prompt_for_download", false); options.AddArgument("--safebrowsing-disable-download-protection"); options.AddUserProfilePreference("safebrowsing.enabled", true); options.AddArgument("no-sandbox"); options.AddArguments("--window-size=1920,1080"); options.SetLoggingPreference(LogType.Performance, LogLevel.All); options.LeaveBrowserRunning = false; driver = new ChromeDriver(options);
根因分析
- IIS生命周期机制冲突:IIS默认开启应用池空闲回收、固定间隔回收、请求线程动态调度、快速故障保护机制,生产环境偶发ChromeDriver进程刚拉起、会话未完成初始化时,对应请求线程被IIS中止或回收,此时
Close()发出的HTTP请求未等到ChromeDriver响应,底层Socket连接被强制断开触发超时。开发环境一般通过Kestrel直接运行,无IIS的进程/线程回收逻辑,因此无法复现问题。 - 资源释放逻辑缺陷:Selenium的
Quit()方法本身会自动完成所有关联窗口关闭、会话销毁、ChromeDriver进程终止操作,不需要提前单独调用Close()。在Driver启动阶段,窗口句柄还未完成会话绑定,此时调用Close()会持续等待窗口就绪响应,直到触发超时。 - 服务端运行适配缺失:一是启动参数不全,Windows Server下IIS运行在会话0隔离层,无桌面交互环境,原配置未添加无头模式参数,Chrome偶发无法创建渲染窗口卡死;二是原配置中
no-sandbox参数缺少--前缀,实际未生效;三是IIS默认应用池使用虚拟标识ApplicationPoolIdentity,且默认关闭「加载用户配置文件」选项,Chrome启动时无法访问用户临时目录、配置目录,偶发启动无响应。 - 超时配置不足:原初始化代码未显式指定Driver命令超时,使用Selenium默认的60秒HttpClient超时阈值,生产环境服务器资源紧张时Chrome启动速度变慢,容易触发超时。
可行解决方案
- 修正资源释放逻辑,移除冗余
Close()调用,增加异常容错和进程兜底清理:
if (driver != null) { try { // 移除冗余的driver.Close()调用,Quit()会自动完成全量资源释放 driver.Quit(); } catch (WebDriverException ex) when ( ex.InnerException is TaskCanceledException || ex.InnerException is TimeoutException || ex.InnerException is IOException) { // 启动阶段的连接中断、超时异常直接忽略,走后续兜底清理逻辑 } finally { driver.Dispose(); // 兜底杀死残留进程,避免资源泄漏 var residualProcesses = Process.GetProcessesByName("chromedriver") .Concat(Process.GetProcessesByName("chrome")); foreach (var process in residualProcesses) { try { process.Kill(entireProcessTree: true); } catch { /* 忽略杀进程异常 */ } } } }
- 补全Chrome启动参数,显式配置超时时间:
var options = new ChromeOptions(); options.AddUserProfilePreference("download.default_directory", System.IO.Path.GetFullPath(downloadFolder)); options.AddUserProfilePreference("disable-popup-blocking", true); options.AddUserProfilePreference("download.prompt_for_download", false); options.AddArgument("--safebrowsing-disable-download-protection"); options.AddUserProfilePreference("safebrowsing.enabled", true); options.AddArgument("--no-sandbox"); // 修正参数前缀,确保沙箱关闭配置生效 options.AddArgument("--disable-dev-shm-usage"); // 解决服务端共享内存不足导致的启动卡死 options.AddArgument("--headless=new"); // 启用新版无头模式,适配IIS会话0无桌面环境 options.AddArgument("--disable-gpu"); // 无GPU环境禁用硬件加速 options.AddArgument("--disable-extensions"); // 禁用扩展减少启动耗时 options.AddArguments("--window-size=1920,1080"); options.SetLoggingPreference(LogType.Performance, LogLevel.All); options.LeaveBrowserRunning = false; // 初始化Driver服务,隐藏命令行窗口 var driverService = ChromeDriverService.CreateDefaultService(); driverService.HideCommandPromptWindow = true; // 显式设置命令超时为120秒,覆盖默认60秒配置 driver = new ChromeDriver(driverService, options, TimeSpan.FromSeconds(120));
- 调整IIS配置适配Selenium运行要求:
- 将对应应用池的「加载用户配置文件」选项设置为
True,为Chrome提供可用的用户配置目录 - 将应用池运行标识修改为具备本地交互权限的专用服务账户,避免默认虚拟账户的权限不足问题
- 关闭应用池空闲超时回收、固定间隔回收配置,避免运行中回收打断Driver进程
- 开启站点预加载功能,减少首次冷启动的资源竞争
- 将对应应用池的「加载用户配置文件」选项设置为
- 版本一致性校验:确保生产环境安装的Chrome浏览器大版本与ChromeDriver版本完全一致,版本差超过1会存在接口兼容性问题,导致请求无响应超时。
内容的提问来源于stack exchange,提问作者Gonzalo Bustamante
相关产品推荐
相关产品推荐

