上篇博客说到使用单例HttpClient,以GET请求方法为例。可以看到对于Http请求头中Authorization参数,会根据传入的accessToken是否为空来判断是否添加此请求头。
public async Task<HttpResponseMessage> GetRequestAsync(string requestUri, string accessToken) { HttpRequestMessage message = new HttpRequestMessage(HttpMethod.Get, requestUri); if (!string.IsNullOrEmpty(accessToken)) { message.Headers.Add("Authorization", "Bearer " + accessToken); } try { var response = await _httpClient.SendAsync(message); //var response = await this._client.GetAsync(requestUri); if (response.IsSuccessStatusCode) { return response; } else { throw new Exception(response.Content.ReadAsStringAsync().Result); } } catch (Exception ex) { throw ex; } }
假设现在有两类请求,一类accessToken有值,一类accessToken值为null。那么在高并发请求中(两类请求都有),经常会曝出如下异常信息。
<error xmlns="http://schemas.microsoft.com/ado/2007/08/dataservices/metadata"> <code>AuthenticationFailed</code> <message xml:lang="en-US">Server failed to authenticate the request. Make sure the value of Authorization header is formed correctly including the signature. RequestId:6c793977-1002-0001-25dc-9ace78000000 Time:2018-12-23T16:24:40.3625699Z</message> </error>
之前一直以为是并行代码有问题,因为使用串行代码执行是能正常运行的。后来测试的时候对并行请求那块代码加lock锁,同样会报这个错误。那么说明并行代码应该是没问题的。
因为报这个错误的请求都是accessToken为null的这类请求。而这类请求由于做了判断,请求头中并不应该有Authorization参数,而异常信息却说我的Authorization格式不正确,那么
似乎是accessToken为null的这类请求的请求头中有key为Authorization的请求头,只是value值不正确,但这类请求是不应该有key为Authorization的请求头的。
后来看到这篇文档(https://mitra.computa.asia/articles/msdn-azure-batch-server-failed-authenticate-request),发现是因为客户端的代理层(proxy layer)会缓存请求信息。也就是说在高并发请求中,带Authorization请求头的请求会缓存下来(而我猜测这因为有一个很短的过期时间,不然就无法解释串行请求是正常的),当切换到不应该带Authorization请求头的请求时(因为Authorization请求头的值为空),就会出现此异常。所以我们需要将Proxy禁用掉就可以的。
//创建HttpClient对象时,禁用Proxy HttpClientHandler handler = new HttpClientHandler() { UseProxy = false }; _httpClient = new HttpClient(handler);
或者也可以使用如下方式禁用客户端Proxy
<system.net> <requestCaching defaultPolicyLevel="NoCacheNoStore"/> </system.net>
这样客户端Proxy就不会缓存Http信息了,两类请求在高并发场景下也能互不干扰。