.NET Core 3.1部署IIS 8.5后WebRequest超时,调试/Postman运行正常
问题场景
- 部署基础环境:运行于Windows Server 2012 R2、IIS 8.5上的.NET Core 3.1应用
- 核心异常表现:应用调用WebRequest推送FCM通知时出现请求超时
- 反常的复现特征:完全相同的请求逻辑,在Postman测试、服务器本机Visual Studio调试模式下均可正常运行,仅当应用通过IIS托管发布后才会触发超时故障。
- 完整业务链路:
- 用户在前端填写通知表单
- 用户提交表单,向Web应用发起POST请求
- 应用读取用户提交的表单数据,构造独立的WebRequest向
fcm.googleapis.com发起POST调用,向对应安卓设备推送通知
排查说明:所有测试操作均直接在目标服务器上完成,没有通过外部客户端跨网验证。为了定位问题甚至专门在服务器上安装了Visual Studio,调试阶段全程没有复现异常,仅IIS托管运行时会触发故障。
关联业务代码
面向用户的表单提交接口
[HttpPost] public async Task<IActionResult> Create(CreateViewModel vm) { var request = new CreateRequest() { Author = String.IsNullOrEmpty(User.Identity.Name) ? "Guest" : User.Identity.Name, Description = vm.Description, }; var response = await _notificationService.Create(request); vm.IsValid = response.IsSuccessfull; vm.ShowBaseMessage = true; vm.BaseMessage = response.Message; return View("_CreateModalPartial", vm); }
上述接口接收到的参数,会直接传递给负责通知提交的业务服务模块。
发起FCM通知请求的核心实现
try { var tRequest = WebRequest.Create("https://fcm.googleapis.com/fcm/send"); tRequest.Method = "post"; tRequest.Headers.Add(string.Format("Authorization: key={0}", "apiKey")); tRequest.Headers.Add(string.Format("Sender: id={0}", "senderId")); tRequest.ContentType = "application/json"; var payload = new { to = clientToken, data = new { author = author, description = description } }; string postbody = JsonConvert.SerializeObject(payload).ToString(); Byte[] byteArray = Encoding.UTF8.GetBytes(postbody); tRequest.ContentLength = byteArray.Length; using (Stream dataStream = await tRequest.GetRequestStreamAsync()) { dataStream.Write(byteArray, 0, byteArray.Length); using (WebResponse tResponse = tRequest.GetResponse()) { using (Stream dataStreamResponse = tResponse.GetResponse()) { if (dataStreamResponse != null) using (StreamReader tReader = new StreamReader(dataStreamResponse)) { String sResponseFromServer = tReader.ReadToEnd(); } } } } } catch(Exception e) { _logger.LogWarning(e.Message); }
故障排查与修复方向
- 校验IIS应用池身份的网络出站权限
调试模式下应用以当前登录服务器的用户身份运行,通常具备完整的外网访问权限;但IIS应用池默认使用的ApplicationPoolIdentity虚拟账号,若没有被加入出站防火墙、内网强制代理的允许列表中,就会直接出现外网请求超时。需要先在IIS应用池高级设置中确认托管进程的运行身份,将该账号加入出站防火墙允许规则,若服务器配置了强制代理,需为该账号配置对应的代理授权或绕过规则。 - 修复TLS版本兼容问题
.NET Core 3.1默认的WebRequest底层在Windows Server 2012 R2环境下,可能默认未启用TLS 1.2协议,而fcm.googleapis.com早已强制要求TLS 1.2及以上版本才能正常访问。调试模式下应用会自动检测系统支持的TLS版本,可正常完成握手;但IIS托管的进程若未显式指定TLS版本,就会因为TLS握手无响应触发超时。可在程序启动的最开头补充全局配置,强制启用TLS 1.2:ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; - 修复异步上下文混用导致的线程饥饿
现有代码存在异步逻辑混用同步阻塞调用的问题:已经通过await获取了请求流,后续却直接调用同步的GetResponse()方法,在ASP.NET Core的请求上下文里,这种写法很容易触发线程池饥饿,最终表现为请求超时。需要将这部分替换为全异步的调用写法:// 替换原有同步的响应获取逻辑 using WebResponse tResponse = await tRequest.GetResponseAsync(); await using Stream dataStreamResponse = await tResponse.GetResponseAsync(); - 适配服务器代理配置
调试模式下应用会自动读取当前登录用户的IE代理配置,而IIS托管的进程默认不会加载用户级别的代理配置,如果服务器必须通过代理才能访问外网,就会因为未走代理直连被网络设备拦截,最终触发超时。可以在WebRequest对象上显式指定与系统一致的代理配置,或在项目配置文件中补充全局代理参数。
内容的提问来源于stack exchange,提问作者MatR
相关产品推荐
相关产品推荐

