Как предотвратить исключение «Операция не поддерживается» при выполнении HTTP-запроса из веб-экземпляра Cloud Run?C#

Место общения программистов C#
Anonymous
Как предотвратить исключение «Операция не поддерживается» при выполнении HTTP-запроса из веб-экземпляра Cloud Run?

Сообщение Anonymous »

У меня есть веб-API ASP.NET Core 8, работающий в Google Cloud Run. Он использует HttpClient для вызова другого внутреннего API, доступного через балансировщик нагрузки HTTP(S) Google Cloud.
Балансировщик нагрузки добавляет следующий заголовок:

Код: Выделить всё

Alt-Svc: h3=":443"; ma=2592000, h3-29=":443";  ma=2592000
Проблема
Периодически запросы завершаются с ошибкой:

Код: Выделить всё

System.Net.NetworkInformation.NetworkInformationException (95):  Operation not supported
at System.Net.NetworkInformation.NetworkChange.CreateSocket()
at System.Net.NetworkInformation.NetworkChange.add_NetworkAddressChanged(NetworkAddressChangedEventHandler value)
at System.Net.Http.HttpConnectionPoolManager.StartMonitoringNetworkChanges()
at System.Net.Http.HttpConnectionPool.HandleAltSvc(IEnumerable`1 altSvcHeaderValues, Nullable`1 responseAge)
at System.Net.Http.HttpConnectionPool.ProcessAltSvc(HttpResponseMessage response)
at System.Net.Http.HttpConnectionPool.SendWithVersionDetectionAndRetryAsync(HttpRequestMessage request, Boolean async, Boolean doRequestAuth, CancellationToken cancellationToken)
at System.Net.Http.DiagnosticsHandler.SendAsyncCore(HttpRequestMessage request, Boolean async, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.Logging.LoggingHttpMessageHandler.g__Core|4_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.PolicyHttpMessageHandler.SendCoreAsync(HttpRequestMessage request, Context context, CancellationToken cancellationToken)
at Polly.Retry.AsyncRetryEngine.ImplementationAsync[TResult](Func`3 action, Context context, ExceptionPredicates shouldRetryExceptionPredicates, ResultPredicates`1 shouldRetryResultPredicates, Func`5 onRetryAsync, CancellationToken cancellationToken, Int32 permittedRetryCount, IEnumerable`1 sleepDurationsEnumerable, Func`4 sleepDurationProvider, Boolean continueOnCapturedContext)
at Polly.AsyncPolicy`1.ExecuteInternalAsync(Func`3 action, Context context, Boolean continueOnCapturedContext, CancellationToken cancellationToken)
at Microsoft.Extensions.Http.PolicyHttpMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
at Redacted.Signon.Client.Service.SusMessageHandler.c__DisplayClass3_0.d.MoveNext() in /workspace/Redacted.Billing/Redacted.Signon.Client/Service/SusMessageHandler.cs:line 51
--- End of stack trace from previous location ---
at Polly.Retry.AsyncRetryEngine.ImplementationAsync[TResult](Func`3 action, Context context, ExceptionPredicates shouldRetryExceptionPredicates, ResultPredicates`1 shouldRetryResultPredicates, Func`5 onRetryAsync, CancellationToken cancellationToken, Int32 permittedRetryCount, IEnumerable`1 sleepDurationsEnumerable, Func`4 sleepDurationProvider, Boolean continueOnCapturedContext)
at Polly.AsyncPolicy`1.ExecuteInternalAsync(Func`3 action, Context context, Boolean continueOnCapturedContext, CancellationToken cancellationToken)
at Redacted.Signon.Client.Service.SusMessageHandler.SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) in /workspace/Redacted.Billing/Redacted.Signon.Client/Service/SusMessageHandler.cs:line 44
at Microsoft.Extensions.Http.Logging.LoggingScopeHttpMessageHandler.g__Core|4_0(HttpRequestMessage request, Boolean useAsync, CancellationToken cancellationToken)
at System.Net.Http.HttpClient.g__Core|83_0(HttpRequestMessage request, HttpCompletionOption completionOption, CancellationTokenSource cts, Boolean disposeCts, CancellationTokenSource pendingRequestsCts, CancellationToken originalCancellationToken)
at Redacted.Signon.Client.Service.SusService.GetUsersByIds(IEnumerable`1 susUserIds, Boolean includeProviderPictureUrls, CancellationToken cancellationToken) in /workspace/Redacted.Billing/Redacted.Signon.Client/Service/SusService.cs:line 114
at Redacted.Signon.Client.Service.SusService.GetUserById(String userId, Boolean includeProviderPictureUrls, CancellationToken cancellationToken) in /workspace/Redacted.Billing/Redacted.Signon.Client/Service/SusService.cs:line 126
at Redacted.Billing.Services.Authentication.TokenValidationService.ValidateUserTokenAsync(IServiceProvider services, ClaimsPrincipal principal, ClaimsIdentity claimsIdentity, CancellationToken cancellationToken) in /workspace/Redacted.Billing/Redacted.Billing/Services/Authentication/TokenValidationService.cs:line 101
at Redacted.Billing.Services.Authentication.TokenValidationService.ValidateTokenAsync(IServiceProvider services, ClaimsPrincipal principal, CancellationToken cancellationToken) in /workspace/Redacted.Billing/Redacted.Billing/Services/Authentication/TokenValidationService.cs:line 35
at Redacted.Billing.App_Start.Services.Auth.SusConfiguration.
b__4_0(TokenValidatedContext ctx) in /workspace/Redacted.Billing/Redacted.Billing/App_Start/Services/Auth.cs:line 77
at Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler.HandleAuthenticateAsync()
Из трассировки стека видно, что исключение возникает, когда HttpClient обрабатывает заголовок Alt-Svc.
Наблюдения
  • Это происходит только в Cloud Run (невозможно воспроизвести локально)
  • В противном случае запросы выполняются через HTTP/2
  • Сбои возникают периодически, вероятно, из-за внутреннего кэширования/отсрочки Alt-Svc в HttpClient
  • Исключение выдается, когда HttpClient начинает отслеживать изменения в сети (через NetworkChange)
Конфигурация HttpClient

Код: Выделить всё

services
.AddHttpClient(Constants.RedactedApiClient)
.ConfigurePrimaryHttpMessageHandler(() => new SocketsHttpHandler()
{
AllowAutoRedirect = false
})
.ConfigureHttpClient(client =>
{
client.DefaultRequestVersion = HttpVersion.Version20;
client.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
});
Использование:

Код: Выделить всё

var client = factory.CreateClient(Constants.RedactedApiClient);
using var response = await client.GetAsync(url, cancellationToken);
response.EnsureSuccessStatusCode();
Что я пробовал
  • Принудительное использование HTTP/2 (см. код выше):

    Код: Выделить всё

    DefaultRequestVersion = HttpVersion.Version20;
    DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrLower;
    
  • Отключение HTTP/3 через переменную среды:

    Код: Выделить всё

    DOTNET_SYSTEM_NET_HTTP_SOCKETSHTTPHANDLER_HTTP3SUPPORT=false
    
  • Просмотр связанной проблемы:

    https://github.com/dotnet/runtime/issues/94794
Ничто из этого не предотвращает исключение.
Как решить эту проблему, чтобы запросы выполнялись нормально? На данный момент я прибегнул к добавлению повторной попытки при возникновении NetworkInformationException.

Вернуться в «C#»