调用 Web API 有时会工作,但通常会出现有关关闭连接的错误。令牌验证是否会导致问题?
Call to an Web API works sometime, but usually get error about closed connection. Could the token validation be causing the issue?
更新 4 更加困惑
我以为我在做某事,但同样的错误不断出现。这个解决方案看起来很有希望,但不是 100%,但认为它可能有助于缩小错误范围。
步骤:
- 重新启动 API 和 Identity Server IIS 应用程序池
- 使用 QA 通过 SoapUI 获取访问令牌 URL
- 使用 SoapUI 在 QA 中点击 API
- 失败 - 不起作用(请参阅下面的堆栈跟踪)
- 使用 SoapUI 在本地点击 API(使用相同的 QA 身份服务器 URL)
- 本地APIURLreturns预期数据
- 使用 QA URL 使用 SoapUI 命中 API(我只是在 SoapUI 中更改端点)
- QA API URL returns 预期数据
我能够重复 4 次,然后在点击 QA 时仍然会出现错误。再次重新启动此过程后,我现在可以让我的 QA 环境正常工作,而无需在本地使用 API。任何想法发生了什么?似乎是设置 issue/proxy/certificate 问题,但不知道如何调试它。
这是我现在看到的错误:
“/”应用程序中的服务器错误。
现有连接被远程主机强行关闭
说明:在执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其在代码中的来源的更多信息。
异常详细信息:System.Net.Sockets.SocketException:现有连接被远程主机强行关闭
来源错误:
在执行当前 Web 请求期间生成了未处理的异常。可以使用下面的异常堆栈跟踪来识别有关异常来源和位置的信息。
堆栈跟踪:
[SocketException (0x2746): An existing connection was forcibly closed by the remote host]
System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult) +8156963
System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult) +48
[IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.]
System.Net.Security._SslStream.EndRead(IAsyncResult asyncResult) +8111720
System.Net.TlsStream.EndRead(IAsyncResult asyncResult) +275
System.Net.Connection.ReadCallback(IAsyncResult asyncResult) +45
[WebException: The underlying connection was closed: An unexpected error occurred on a receive.]
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) +764
System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) +78
[HttpRequestException: An error occurred while sending the request.]
[AggregateException: One or more errors occurred.]
System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) +4451240
Microsoft.IdentityModel.Protocols.<GetDocumentAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\HttpDocumentRetriever.cs:53
[IOException: Unable to get document from: https://securityeliqa.twcable.com/core/.well-known/openid-configuration]
Microsoft.IdentityModel.Protocols.<GetDocumentAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\HttpDocumentRetriever.cs:59
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.IdentityModel.Protocols.<GetAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\OpenIdConnectConfigurationRetriever.cs:81
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.IdentityModel.Protocols.<GetConfigurationAsync>d__3.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\ConfigurationManager.cs:0
[InvalidOperationException: IDX10803: Unable to create to obtain configuration from: 'https://securityeliqa.twcable.com/core/.well-known/openid-configuration'.]
Microsoft.IdentityModel.Protocols.<GetConfigurationAsync>d__3.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\ConfigurationManager.cs:212
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.IdentityModel.Protocols.<GetConfigurationAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\ConfigurationManager.cs:0
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +31
IdentityServer.WebApi.AccessTokenValidation.<<RetrieveMetadata>b__0>d__4.MoveNext() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\DiscoveryDocumentIssuerSecurityTokenProvider.cs:123
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +31
IdentityServer.WebApi.AccessTokenValidation.AsyncHelper.RunSync(Func`1 func) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\AsyncHelper.cs:18
IdentityServer.WebApi.AccessTokenValidation.DiscoveryDocumentIssuerSecurityTokenProvider.RetrieveMetadata() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\DiscoveryDocumentIssuerSecurityTokenProvider.cs:141
IdentityServer.WebApi.AccessTokenValidation.DiscoveryDocumentIssuerSecurityTokenProvider..ctor(String discoveryEndpoint, IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\DiscoveryDocumentIssuerSecurityTokenProvider.cs:43
Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.ConfigureLocalValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\IdentityServerBearerTokenValidationAppBuilderExtensions.cs:129
Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.UseIdentityServerBearerTokenAuthentication(IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\IdentityServerBearerTokenValidationAppBuilderExtensions.cs:39
Company.WebApi.waAddressQualification.Startup.Configuration(IAppBuilder app) in e:\Source Code\GitHub\waDemo\Startup.cs:23
[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +128
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +146
Owin.Loader.<>c__DisplayClass12.<MakeDelegate>b__b(IAppBuilder builder) +93
Owin.Loader.<>c__DisplayClass1.<LoadImplementation>b__0(IAppBuilder builder) +209
Microsoft.Owin.Host.SystemWeb.OwinAppContext.Initialize(Action`1 startup) +843
Microsoft.Owin.Host.SystemWeb.OwinBuilder.Build(Action`1 startup) +51
Microsoft.Owin.Host.SystemWeb.OwinHttpModule.InitializeBlueprint() +101
System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) +137
Microsoft.Owin.Host.SystemWeb.OwinHttpModule.Init(HttpApplication context) +172
System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +618
System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172
System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +402
System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +343
[HttpException (0x80004005): Exception has been thrown by the target of an invocation.]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +579
System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +112
System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +712
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1055.0
UPDATE 3 更加困惑
此时我相信是服务器取消了我的请求,但即便如此我也不能 100% 确定。以下是一些额外的数据点:
- 在 Dev 和 QA 机器上都设置了服务,Dev 机器大约 100 个请求中有 1 个出错,QA 大约有 50% 的时间。
- QA 框将身份服务器设置为负载平衡器后面的 https,Dev 是 http
- 服务设置模仿 Identity Server,在 QA 中它是负载均衡器后面的 https,Dev 是 http
- QA 中的一组三个地址通常至少有一个地址失败,我在服务器日志中看到请求中有两个有效,难道我不应该看到第三个吗? 如果第三个请求不在服务器日志中是否意味着它从未到达那里?
- 真正让我着迷的是,除了在客户端进行 HttpClient.GetAsync 调用之外,我似乎在任何地方都看不到错误。 如果服务器正在取消请求,难道不应该为它取消的每个请求创建一个日志吗?
- 我在 windows 服务器上的事件日志中发现有关取消任务的服务错误,但是并非所有失败的请求都在这里结束?
操作被取消。 at System.Threading.CancellationToken.ThrowOperationCanceledException() at System.Web.Http.Owin.HttpMessageHandlerAdapter.d__13.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Owin.HttpMessageHandlerAdapter.d__0.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at IdentityServer.WebApi.AccessTokenValidation.ScopeRequirementMiddleware.d__0.MoveNext() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\ScopeRequirementMiddleware .cs:line 48 --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在 Microsoft.Owin.Security.Infrastructure.AuthenticationMiddleware`1 .d__0.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.GetResult() at IdentityServer.WebApi.AccessTokenValidation.IdentityServerBearerTokenValidationMiddleware.d__6.MoveNext() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\IdentityServerBearerTokenValidationMiddleware.cs:line 81 --- 上一位置的堆栈跟踪结束抛出异常的地方 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 在 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContextStage.d__5.MoveNext() --- 上一个堆栈跟踪的结尾抛出异常的位置---在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.d__2.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) 在 System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 在 System.Web.HttpApplication.ExecuteStep(IExecutionStep 步骤,Boolean& completedSynchronously )
原始问题
我想知道我是否正确地调用了我的演示 API,或者 API 是否给我带来了问题。如果我同时发送三个请求,我至少会在我尝试处理的地址之一上遇到问题。
更新一号
在内部异常中,我收到以下消息,我能够捕获如下更完整的错误:"The underlying connection was closed: An unexpected error occurred on a receive. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host"
我在客户端和服务器端都使用 .NET 4.6.1
我没有使用代理。
我能够在大约 1/3 的时间内成功调用 API
我在调用服务时尝试了几个不同的选项,第一个只是将 DemoApiClient 包装在一个锁中,接下来是使用 Task 运行 和 async 和 await 关键字,最后使用 Task 运行 检索令牌以进行身份验证,或者只是调用它并在等待行中列出 ConfigureAwait(false) 。我不确定这是否重要,或者服务器在调用时是否只是失败。服务器并没有那么复杂,但我不想 post 此处使用该代码。它确实会验证令牌,然后处理一个地址,进行几次数据库调用并从数据库中找到 returns 数据。
更新 2
我一次测试三个地址,当我通过 SoapUI 将它们发送到服务器时,这三个地址都可以正常处理,但是对于使用下面代码的 .Net C# 客户端,三个请求中只有一个似乎可以发送到服务器。我期待看到两个堆栈跟踪和一个成功的请求,但 IIS 日志只显示一个请求进来。
我用来调用 API 的代码在这里:
using Demo.IdentityServer.IdentityModel.Client;
using Newtonsoft.Json;
using PartnerPortal.Model;
using System;
using System.Configuration;
using System.Diagnostics;
using System.Net.Http;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace DemoPortal.Client
{
public static class DemoApiClient
{
public readonly static string waDemoUrl = ConfigurationManager.AppSettings["waDemoURL"];
public static async Task<DemoData> GetDemoData(RequestAddress requestAddress)
{
try
{
using (var httpClient = new HttpClient() { MaxResponseContentBufferSize = 10000000, Timeout = TimeSpan.FromMilliseconds(5000) })
{
string reasonPhrase = "";
var demoUri = new Uri(string.Format(waDemoUrl + "api/DemoApp/GetSomeData?trackingId={0}&clientKey={1}&address={2}", requestAddress.TrackingId, requestAddress.ClientKey, requestAddress.Address));
var httpResult = new HttpResponseMessage();
var result = RequestAccessToken.RequestToken().Result;
var accessToken = await Task.Run(() => RequestAccessToken.RequestToken().Result);
//var accessToken = RequestAccessToken.RequestToken().Result;
//which of the above two options should I use? Does it matter?
httpClient.SetBearerToken(accessToken);
HttpResponseMessage response = await httpClient.GetAsync(demoUri);
HttpContent httpContent = response.Content;
if (httpResult.IsSuccessStatusCode)
{
var content = await httpContent.ReadAsStringAsync();
DemoData demoData = null;
demoData = JsonConvert.DeserializeObject<DemoData>(content);
return demoData;
}
else
{
reasonPhrase = httpResult.ReasonPhrase;
if (reasonPhrase.ToUpper() == "UNAUTHORIZED")
{
throw new KeyNotFoundException("Not authorized");
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Message is:" + ex);
throw (ex);
}
return null;
}
}
}
这是检索令牌的方式:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Thinktecture.IdentityModel.Tokens.Http;
namespace Demo.IdentityServer.IdentityModel.Client
{
public static class RequestAccessToken
{
public readonly static string securityUrl = ConfigurationManager.AppSettings["securityUrl"];
public readonly static string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
public static async Task<string> RequestToken()
{
var url = new Uri(securityUrl);
var fields = new Dictionary<string, string>
{
{ OAuth2Constants.GrantType, OAuth2Constants.GrantTypes.ClientCredentials },
{ OAuth2Constants.Scope, "Read"}
};
using (var httpClient = new HttpClient())
{
var cancellationToken = new CancellationToken();
httpClient.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue("Demo", clientSecret);
var ss = JsonConvert.SerializeObject(fields);
var data = new FormUrlEncodedContent(fields);
var s = data.ReadAsStringAsync();
//var response = await httpClient.PostAsync(url, data, cancellationToken).ConfigureAwait(false);
var response = await httpClient.PostAsync(url, data, cancellationToken);
// Should I use ConfigureAwait(false) or call method with a Task(Run() ... ?
if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.BadRequest)
{
//var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync();
var token = new TokenResponse(content);
var accessToken = token.AccessToken;
return accessToken;
}
else
{
return new TokenResponse(response.StatusCode, response.ReasonPhrase).AccessToken;
}
}
}
}
}
找到解决方案!!! 整个问题源于错误配置的负载平衡器。 SNAT 池的东西。我不是网络专家,不管他做了什么,都解决了这个问题。负载均衡器之类的东西直接引用了盒子名称而不是 SNAT 池名称,或者 SNAT 池的名称错误。无论哪种方式,一旦更新,间歇性的成功就变成了总是成功。
我可以告诉您,您正面临大规模 SNAT 端口耗尽的问题。在 using 块中实例化 HttpClient 是有意义的,因为它实现了 IDisposable。但是这样做会导致HttpClient在处置后独占一个SNAT端口240秒。 See here for a great explanation 这个问题。
更新 4 更加困惑 我以为我在做某事,但同样的错误不断出现。这个解决方案看起来很有希望,但不是 100%,但认为它可能有助于缩小错误范围。
步骤:
- 重新启动 API 和 Identity Server IIS 应用程序池
- 使用 QA 通过 SoapUI 获取访问令牌 URL
- 使用 SoapUI 在 QA 中点击 API
- 失败 - 不起作用(请参阅下面的堆栈跟踪)
- 使用 SoapUI 在本地点击 API(使用相同的 QA 身份服务器 URL)
- 本地APIURLreturns预期数据
- 使用 QA URL 使用 SoapUI 命中 API(我只是在 SoapUI 中更改端点)
- QA API URL returns 预期数据
我能够重复 4 次,然后在点击 QA 时仍然会出现错误。再次重新启动此过程后,我现在可以让我的 QA 环境正常工作,而无需在本地使用 API。任何想法发生了什么?似乎是设置 issue/proxy/certificate 问题,但不知道如何调试它。
这是我现在看到的错误: “/”应用程序中的服务器错误。 现有连接被远程主机强行关闭 说明:在执行当前 Web 请求期间发生未处理的异常。请查看堆栈跟踪以获取有关错误及其在代码中的来源的更多信息。 异常详细信息:System.Net.Sockets.SocketException:现有连接被远程主机强行关闭 来源错误: 在执行当前 Web 请求期间生成了未处理的异常。可以使用下面的异常堆栈跟踪来识别有关异常来源和位置的信息。 堆栈跟踪:
[SocketException (0x2746): An existing connection was forcibly closed by the remote host]
System.Net.Sockets.Socket.EndReceive(IAsyncResult asyncResult) +8156963
System.Net.Sockets.NetworkStream.EndRead(IAsyncResult asyncResult) +48
[IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host.]
System.Net.Security._SslStream.EndRead(IAsyncResult asyncResult) +8111720
System.Net.TlsStream.EndRead(IAsyncResult asyncResult) +275
System.Net.Connection.ReadCallback(IAsyncResult asyncResult) +45
[WebException: The underlying connection was closed: An unexpected error occurred on a receive.]
System.Net.HttpWebRequest.EndGetResponse(IAsyncResult asyncResult) +764
System.Net.Http.HttpClientHandler.GetResponseCallback(IAsyncResult ar) +78
[HttpRequestException: An error occurred while sending the request.]
[AggregateException: One or more errors occurred.]
System.Threading.Tasks.Task`1.GetResultCore(Boolean waitCompletionNotification) +4451240
Microsoft.IdentityModel.Protocols.<GetDocumentAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\HttpDocumentRetriever.cs:53
[IOException: Unable to get document from: https://securityeliqa.twcable.com/core/.well-known/openid-configuration]
Microsoft.IdentityModel.Protocols.<GetDocumentAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\HttpDocumentRetriever.cs:59
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.IdentityModel.Protocols.<GetAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\OpenIdConnectConfigurationRetriever.cs:81
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.IdentityModel.Protocols.<GetConfigurationAsync>d__3.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\ConfigurationManager.cs:0
[InvalidOperationException: IDX10803: Unable to create to obtain configuration from: 'https://securityeliqa.twcable.com/core/.well-known/openid-configuration'.]
Microsoft.IdentityModel.Protocols.<GetConfigurationAsync>d__3.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\ConfigurationManager.cs:212
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
Microsoft.IdentityModel.Protocols.<GetConfigurationAsync>d__0.MoveNext() in c:\workspace\WilsonForDotNet45Release\src\Microsoft.IdentityModel.Protocol.Extensions\Configuration\ConfigurationManager.cs:0
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +31
IdentityServer.WebApi.AccessTokenValidation.<<RetrieveMetadata>b__0>d__4.MoveNext() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\DiscoveryDocumentIssuerSecurityTokenProvider.cs:123
System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) +13847892
System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) +61
System.Runtime.CompilerServices.TaskAwaiter`1.GetResult() +31
IdentityServer.WebApi.AccessTokenValidation.AsyncHelper.RunSync(Func`1 func) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\AsyncHelper.cs:18
IdentityServer.WebApi.AccessTokenValidation.DiscoveryDocumentIssuerSecurityTokenProvider.RetrieveMetadata() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\DiscoveryDocumentIssuerSecurityTokenProvider.cs:141
IdentityServer.WebApi.AccessTokenValidation.DiscoveryDocumentIssuerSecurityTokenProvider..ctor(String discoveryEndpoint, IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\DiscoveryDocumentIssuerSecurityTokenProvider.cs:43
Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.ConfigureLocalValidation(IdentityServerBearerTokenAuthenticationOptions options, ILoggerFactory loggerFactory) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\IdentityServerBearerTokenValidationAppBuilderExtensions.cs:129
Owin.IdentityServerBearerTokenValidationAppBuilderExtensions.UseIdentityServerBearerTokenAuthentication(IAppBuilder app, IdentityServerBearerTokenAuthenticationOptions options) in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\IdentityServerBearerTokenValidationAppBuilderExtensions.cs:39
Company.WebApi.waAddressQualification.Startup.Configuration(IAppBuilder app) in e:\Source Code\GitHub\waDemo\Startup.cs:23
[TargetInvocationException: Exception has been thrown by the target of an invocation.]
System.RuntimeMethodHandle.InvokeMethod(Object target, Object[] arguments, Signature sig, Boolean constructor) +0
System.Reflection.RuntimeMethodInfo.UnsafeInvokeInternal(Object obj, Object[] parameters, Object[] arguments) +128
System.Reflection.RuntimeMethodInfo.Invoke(Object obj, BindingFlags invokeAttr, Binder binder, Object[] parameters, CultureInfo culture) +146
Owin.Loader.<>c__DisplayClass12.<MakeDelegate>b__b(IAppBuilder builder) +93
Owin.Loader.<>c__DisplayClass1.<LoadImplementation>b__0(IAppBuilder builder) +209
Microsoft.Owin.Host.SystemWeb.OwinAppContext.Initialize(Action`1 startup) +843
Microsoft.Owin.Host.SystemWeb.OwinBuilder.Build(Action`1 startup) +51
Microsoft.Owin.Host.SystemWeb.OwinHttpModule.InitializeBlueprint() +101
System.Threading.LazyInitializer.EnsureInitializedCore(T& target, Boolean& initialized, Object& syncLock, Func`1 valueFactory) +137
Microsoft.Owin.Host.SystemWeb.OwinHttpModule.Init(HttpApplication context) +172
System.Web.HttpApplication.RegisterEventSubscriptionsWithIIS(IntPtr appContext, HttpContext context, MethodInfo[] handlers) +618
System.Web.HttpApplication.InitSpecial(HttpApplicationState state, MethodInfo[] handlers, IntPtr appContext, HttpContext context) +172
System.Web.HttpApplicationFactory.GetSpecialApplicationInstance(IntPtr appContext, HttpContext context) +402
System.Web.Hosting.PipelineRuntime.InitializeApplication(IntPtr appContext) +343
[HttpException (0x80004005): Exception has been thrown by the target of an invocation.]
System.Web.HttpRuntime.FirstRequestInit(HttpContext context) +579
System.Web.HttpRuntime.EnsureFirstRequestInit(HttpContext context) +112
System.Web.HttpRuntime.ProcessRequestNotificationPrivate(IIS7WorkerRequest wr, HttpContext context) +712
Version Information: Microsoft .NET Framework Version:4.0.30319; ASP.NET Version:4.6.1055.0
UPDATE 3 更加困惑
此时我相信是服务器取消了我的请求,但即便如此我也不能 100% 确定。以下是一些额外的数据点:
- 在 Dev 和 QA 机器上都设置了服务,Dev 机器大约 100 个请求中有 1 个出错,QA 大约有 50% 的时间。
- QA 框将身份服务器设置为负载平衡器后面的 https,Dev 是 http
- 服务设置模仿 Identity Server,在 QA 中它是负载均衡器后面的 https,Dev 是 http
- QA 中的一组三个地址通常至少有一个地址失败,我在服务器日志中看到请求中有两个有效,难道我不应该看到第三个吗? 如果第三个请求不在服务器日志中是否意味着它从未到达那里?
- 真正让我着迷的是,除了在客户端进行 HttpClient.GetAsync 调用之外,我似乎在任何地方都看不到错误。 如果服务器正在取消请求,难道不应该为它取消的每个请求创建一个日志吗?
- 我在 windows 服务器上的事件日志中发现有关取消任务的服务错误,但是并非所有失败的请求都在这里结束? 操作被取消。 at System.Threading.CancellationToken.ThrowOperationCanceledException() at System.Web.Http.Owin.HttpMessageHandlerAdapter.d__13.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- at System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) at System.Web.Http.Owin.HttpMessageHandlerAdapter.d__0.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) at System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务) at System.Runtime.CompilerServices.TaskAwaiter.GetResult() at IdentityServer.WebApi.AccessTokenValidation.ScopeRequirementMiddleware.d__0.MoveNext() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\Plumbing\ScopeRequirementMiddleware .cs:line 48 --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在 Microsoft.Owin.Security.Infrastructure.AuthenticationMiddleware`1 .d__0.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在 System.Runtime.CompilerServices.TaskAwaiter.GetResult() at IdentityServer.WebApi.AccessTokenValidation.IdentityServerBearerTokenValidationMiddleware.d__6.MoveNext() in e:\Source Code\GitHub\IdentityServer\IdentityServer.WebApi.AccessTokenValidation\IdentityServerBearerTokenValidationMiddleware.cs:line 81 --- 上一位置的堆栈跟踪结束抛出异常的地方 --- 在 System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(Task task) 在 System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(Task task) 在 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContextStage.d__5.MoveNext() --- 上一个堆栈跟踪的结尾抛出异常的位置---在System.Runtime.CompilerServices.TaskAwaiter.ThrowForNonSuccess(任务任务)在System.Runtime.CompilerServices.TaskAwaiter.HandleNonSuccessAndDebuggerNotification(任务任务)在Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.IntegratedPipelineContext.d__2.MoveNext() --- 从先前抛出异常的位置开始的堆栈跟踪结束 --- 在 Microsoft.Owin.Host.SystemWeb.IntegratedPipeline.StageAsyncResult.End(IAsyncResult ar) 在 System.Web.HttpApplication.AsyncEventExecutionStep.System.Web.HttpApplication.IExecutionStep.Execute() 在 System.Web.HttpApplication.ExecuteStep(IExecutionStep 步骤,Boolean& completedSynchronously )
原始问题
我想知道我是否正确地调用了我的演示 API,或者 API 是否给我带来了问题。如果我同时发送三个请求,我至少会在我尝试处理的地址之一上遇到问题。
更新一号
在内部异常中,我收到以下消息,我能够捕获如下更完整的错误:"The underlying connection was closed: An unexpected error occurred on a receive. ---> System.IO.IOException: Unable to read data from the transport connection: An existing connection was forcibly closed by the remote host. ---> System.Net.Sockets.SocketException: An existing connection was forcibly closed by the remote host"
我在客户端和服务器端都使用 .NET 4.6.1 我没有使用代理。 我能够在大约 1/3 的时间内成功调用 API
我在调用服务时尝试了几个不同的选项,第一个只是将 DemoApiClient 包装在一个锁中,接下来是使用 Task 运行 和 async 和 await 关键字,最后使用 Task 运行 检索令牌以进行身份验证,或者只是调用它并在等待行中列出 ConfigureAwait(false) 。我不确定这是否重要,或者服务器在调用时是否只是失败。服务器并没有那么复杂,但我不想 post 此处使用该代码。它确实会验证令牌,然后处理一个地址,进行几次数据库调用并从数据库中找到 returns 数据。
更新 2 我一次测试三个地址,当我通过 SoapUI 将它们发送到服务器时,这三个地址都可以正常处理,但是对于使用下面代码的 .Net C# 客户端,三个请求中只有一个似乎可以发送到服务器。我期待看到两个堆栈跟踪和一个成功的请求,但 IIS 日志只显示一个请求进来。
我用来调用 API 的代码在这里:
using Demo.IdentityServer.IdentityModel.Client;
using Newtonsoft.Json;
using PartnerPortal.Model;
using System;
using System.Configuration;
using System.Diagnostics;
using System.Net.Http;
using System.Linq;
using System.Collections.Generic;
using System.Threading.Tasks;
namespace DemoPortal.Client
{
public static class DemoApiClient
{
public readonly static string waDemoUrl = ConfigurationManager.AppSettings["waDemoURL"];
public static async Task<DemoData> GetDemoData(RequestAddress requestAddress)
{
try
{
using (var httpClient = new HttpClient() { MaxResponseContentBufferSize = 10000000, Timeout = TimeSpan.FromMilliseconds(5000) })
{
string reasonPhrase = "";
var demoUri = new Uri(string.Format(waDemoUrl + "api/DemoApp/GetSomeData?trackingId={0}&clientKey={1}&address={2}", requestAddress.TrackingId, requestAddress.ClientKey, requestAddress.Address));
var httpResult = new HttpResponseMessage();
var result = RequestAccessToken.RequestToken().Result;
var accessToken = await Task.Run(() => RequestAccessToken.RequestToken().Result);
//var accessToken = RequestAccessToken.RequestToken().Result;
//which of the above two options should I use? Does it matter?
httpClient.SetBearerToken(accessToken);
HttpResponseMessage response = await httpClient.GetAsync(demoUri);
HttpContent httpContent = response.Content;
if (httpResult.IsSuccessStatusCode)
{
var content = await httpContent.ReadAsStringAsync();
DemoData demoData = null;
demoData = JsonConvert.DeserializeObject<DemoData>(content);
return demoData;
}
else
{
reasonPhrase = httpResult.ReasonPhrase;
if (reasonPhrase.ToUpper() == "UNAUTHORIZED")
{
throw new KeyNotFoundException("Not authorized");
}
}
}
}
catch (Exception ex)
{
Debug.WriteLine("Message is:" + ex);
throw (ex);
}
return null;
}
}
}
这是检索令牌的方式:
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Net;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
using System.Web;
using Thinktecture.IdentityModel.Tokens.Http;
namespace Demo.IdentityServer.IdentityModel.Client
{
public static class RequestAccessToken
{
public readonly static string securityUrl = ConfigurationManager.AppSettings["securityUrl"];
public readonly static string clientSecret = ConfigurationManager.AppSettings["clientSecret"];
public static async Task<string> RequestToken()
{
var url = new Uri(securityUrl);
var fields = new Dictionary<string, string>
{
{ OAuth2Constants.GrantType, OAuth2Constants.GrantTypes.ClientCredentials },
{ OAuth2Constants.Scope, "Read"}
};
using (var httpClient = new HttpClient())
{
var cancellationToken = new CancellationToken();
httpClient.DefaultRequestHeaders.Authorization = new BasicAuthenticationHeaderValue("Demo", clientSecret);
var ss = JsonConvert.SerializeObject(fields);
var data = new FormUrlEncodedContent(fields);
var s = data.ReadAsStringAsync();
//var response = await httpClient.PostAsync(url, data, cancellationToken).ConfigureAwait(false);
var response = await httpClient.PostAsync(url, data, cancellationToken);
// Should I use ConfigureAwait(false) or call method with a Task(Run() ... ?
if (response.StatusCode == HttpStatusCode.OK || response.StatusCode == HttpStatusCode.BadRequest)
{
//var content = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
var content = await response.Content.ReadAsStringAsync();
var token = new TokenResponse(content);
var accessToken = token.AccessToken;
return accessToken;
}
else
{
return new TokenResponse(response.StatusCode, response.ReasonPhrase).AccessToken;
}
}
}
}
}
找到解决方案!!! 整个问题源于错误配置的负载平衡器。 SNAT 池的东西。我不是网络专家,不管他做了什么,都解决了这个问题。负载均衡器之类的东西直接引用了盒子名称而不是 SNAT 池名称,或者 SNAT 池的名称错误。无论哪种方式,一旦更新,间歇性的成功就变成了总是成功。
我可以告诉您,您正面临大规模 SNAT 端口耗尽的问题。在 using 块中实例化 HttpClient 是有意义的,因为它实现了 IDisposable。但是这样做会导致HttpClient在处置后独占一个SNAT端口240秒。 See here for a great explanation 这个问题。