.NET 4.6.1项目HttpClient异步Get请求挂起超时 同步调用正常排查
更新/补充
我拉取对应仓库代码编译后单步调试,发现HttpClient在以下位置触发超时:
var result = await client.SendAsync(request) // 运行一段时间后超时
改成阻塞调用后即可正常运行:
var result = client.SendAsync(request).GetAwaiter().GetResult() // 运行正常
后续的异步读取响应操作也可以正常执行:
var body = await result.Content.ReadAsStringAsync(); // 只要Send是同步调用,这一步就能正常运行
我不清楚为什么异步发送请求会超时,改成阻塞发送之后再异步读取结果就正常,想知道背后的运行逻辑。
原帖内容
我正在调试第三方API请求类库的使用问题,项目目标框架为.NET 4.6.1,是同时包含WebApi和WebForms的混合Web项目。
使用的HTTP请求类库为Postmark.Net。
问题现象
项目运行后直接挂起,不会触发超时返回调用方,调试窗口仅显示线程以代码0退出。
示例代码
Dim cc = New PostmarkDotNet.PostmarkClient("key") Dim msg = cc.GetOutboundMessageDetailsAsync("some-id").GetAwaiter.GetResult() Log.Debug(msg.Status.ToString & " - " & msg.ReceivedAt.ToString() & " - " & msg.To.FirstOrDefault.Email.ToString())
备注:此处特意使用GetAwaiter阻塞线程方便排查问题。
已尝试方案
- 新建目标框架为4.7.1的.NET测试项目,类库和代码运行正常,可确定问题与当前项目有关
- 清理并重新生成项目
- 启动Fiddler抓包,可看到发往API的出站请求及携带SSL证书的响应,之后无后续数据,怀疑是SSL握手阶段异常,但同机器测试项目运行正常,排除TLS版本问题。
待解答问题
- 如何调试进入第三方类库内部查看执行逻辑?
- 该类库要求依赖System.Net.Http 4.3.4版本,但当前项目web.config中绑定重定向指向4.2.0.0版本,测试项目同配置却无异常,请问有什么排查建议?
异步调用超时/死锁原因
这是.NET Framework下ASP.NET项目非常典型的同步上下文死锁问题:
- .NET 4.x的Web项目默认使用
AspNetSynchronizationContext,默认情况下await会捕获当前同步上下文,在异步操作完成后回到原上下文执行后续代码 - 你的上层代码用
GetAwaiter().GetResult()阻塞了当前请求线程(属于同步上下文的独占线程),等待异步任务完成 - 异步的
SendAsync完成后,需要等待同步上下文的线程释放才能执行回调,但此时线程已经被你的阻塞调用占用,就形成了互等的死锁,表现为请求挂起、看起来像超时 - 你直接对
SendAsync使用GetAwaiter().GetResult()时,相当于直接同步等待任务完成,没有用到await的上下文捕获逻辑,所以不会触发死锁,后续的ReadAsStringAsync能正常运行是因为此时已经没有上层的阻塞调用占着线程了。
如果要避免这个问题,可以在所有异步调用后加ConfigureAwait(false),告诉await不需要捕获原同步上下文,就不会触发死锁。
调试第三方类库的方法
- 开源类库最推荐直接拉取对应版本的源码,把你项目里的类库引用替换为源码项目引用,直接下断点调试即可
- 不想编译源码的话可以用Visual Studio的反编译调试功能:
- 打开
工具->选项->调试->常规,关闭启用仅我的代码,开启反编译源(仅限托管) - 调试运行到对应调用时,直接按F11步进即可自动反编译类库代码进行调试
- 打开
- 也可以用专用反编译工具生成类库的pdb符号文件,挂载到调试进程后也能正常步进调试。
System.Net.Http版本问题排查建议
首先要明确:System.Net.Http的Nuget包版本和程序集版本是不对应的,4.3.4版本的Nuget包在.NET Framework下对应的程序集版本就是4.2.0.0,所以你看到的绑定重定向配置本身是正常的,排查可以按以下步骤走:
- 检查项目bin目录下的
System.Net.Http.dll,右键查看属性的详细信息标签,确认文件版本、产品版本和你预期的版本一致 - 用系统自带的
fuslogvw.exe(程序集绑定日志查看器)开启程序集绑定日志,复现问题后查看日志,就能明确看到有没有System.Net.Http的绑定失败、失败原因是什么 - 打开Nuget包管理控制台,执行
Get-Project -All | Add-BindingRedirect,工具会自动扫描所有依赖的程序集版本,生成正确的绑定重定向规则覆盖web.config里的旧规则 - 检查项目中其他引用的第三方类库,确认没有其他依赖更高/更低版本System.Net.Http的组件引发冲突。
内容的提问来源于stack exchange,提问作者DDulla
相关产品推荐
相关产品推荐

