.NET6 HttpClient调用WCF服务 大请求触发证书相关超时异常
根因
这个超时和WCF绑定的消息大小配置没有关系,本质是IIS托管层的客户端证书TLS重协商机制,和HttpClient默认请求发送行为不匹配导致的,核心逻辑如下:
- 你配置的
wsHttpBinding采用Transport模式证书认证,当WCF托管在IIS上时,IIS默认不会在TCP连接建立后的初始TLS握手阶段请求客户端证书,而是等收到HTTP请求头、判断目标端点需要证书认证后,才会触发TLS重协商,要求客户端出示证书。 - TLS重协商要求IIS必须缓存已经从客户端收到的所有请求体数据,否则重协商完成后客户端重发请求会出现数据错位。IIS控制这个缓存上限的配置是
uploadReadAheadSize,默认值通常在32KB~48KB区间,和你遇到的37KB超时阈值完全吻合:- 请求体小于阈值时,IIS可以把已收到的全部请求体缓存到内存,顺利完成重协商、校验证书,请求正常处理
- 请求体大于阈值时,IIS无法缓存全部已收到的请求数据,既不能完成重协商,也不会返回错误响应,连接会一直挂起直到客户端触发超时
- 你当前的HttpClient实现直接发送
StringContent时,默认不会携带Expect: 100-continue请求头,会在建立连接后立刻把请求头+完整请求体一次性发给服务端,刚好触发上述大请求重协商死锁。 - 基于
ClientBase的WCF客户端默认会自动携带Expect: 100-continue头,客户端发送请求头后会等待服务端返回100 Continue状态码,之后才发送请求体;而服务端在返回100 Continue之前就会完成客户端证书的TLS重协商,不会出现已经收到大量请求体再协商的情况,所以大请求也能正常通信。关闭服务端证书认证后不需要TLS重协商,自然也不会触发超时。
修复方案
按优先级从高到低选择即可:
方案1:给HttpClient开启100-continue行为(最推荐,无需修改服务端)
让HttpClient的行为和WCF原生客户端对齐,在发送请求体前先等待服务端的确认,从客户端侧避免大请求触发重协商死锁:
var client = _httpClientFactory.CreateClient(); var request = new HttpRequestMessage(HttpMethod.Post, _uri); // 开启Expect: 100-continue行为 request.Headers.ExpectContinue = true; request.Content = new StringContent(message.Body, Encoding.UTF8, "application/soap+xml"); var response = await client.SendAsync(request);
如果需要全局统一配置,可以在IHttpClientFactory注入时设置默认值:
services.AddHttpClient("WcfSoapClient") .ConfigureHttpClient(client => { client.DefaultRequestHeaders.ExpectContinue = true; });
方案2:调整IIS请求预缓存大小(需要服务端管理权限)
如果不方便修改客户端代码,可以在服务端IIS上调大uploadReadAheadSize配置,让IIS可以缓存足够大的请求体完成TLS重协商,配置值建议和WCF绑定的maxReceivedMessageSize保持一致。
可以直接在服务端web.config中添加如下配置:
<system.webServer> <!-- 单位为字节,这里设置为2GB和你当前的WCF绑定配置匹配 --> <serverRuntime uploadReadAheadSize="2147483647" /> </system.webServer>
也可以通过IIS管理器的配置编辑器,定位到system.webServer/serverRuntime节点修改对应值,修改完成后执行iisreset重启IIS生效。
注意:该配置会提高IIS的单请求内存占用,大流量场景下谨慎调整。
方案3:调整IIS SSL协商策略(需要服务端管理权限)
修改IIS配置,让服务端在初始TLS握手阶段就要求客户端出示证书,完全取消HTTP请求阶段的TLS重协商,从根源规避问题:
- 打开IIS管理器,找到对应站点的SSL设置,将客户端证书选项设置为「必需」
- 以管理员身份执行如下命令,修改站点SSL配置禁用重协商:
%windir%\system32\inetsrv\appcmd set config "你的站点名称" -section:system.webServer/security/access /sslFlags:"Ssl, SslRequireCert, SslNegotiateCert" /commit:apphost
注意:该配置在IIS 7.5等旧版本上存在兼容性问题,优先选择方案1。
内容的提问来源于stack exchange,提问作者Eitan
相关产品推荐
相关产品推荐

