You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.27 05:39:40