Функция Azure работает в System.Net.Sockets.SocketException - PullRequest
0 голосов
/ 20 сентября 2019

У меня есть приложение AzureFunctions с 2 HTTP-функциями.Оба направлены из одного класса, но используют разные URL для получения данных.Каждый день конвейер фабрики данных Azure запускает 1-ую функцию HTTP, а другой конвейер вызывает 2-ю функцию в течение 1 минуты

Каждая функция отправляет около 1300 HTTP-запросов на сторонний веб-сайт и сохраняет каждый ответ в виде отдельного файла JSON в хранилище BLOB-объектов.

Проблема возникает почти каждый раз (но не всегда), 2-я функция выдает исключение System.Net.Sockets.SocketException, потому что несколько исходящих запросов работают с общим тайм-аутом TCP 21 с.Странная вещь, которую я заметил - вероятно, Azure по какой-то причине душит мои исходящие запросы: первый пакет занимает около 300 мс, следующий цикл занимает 4,3 с, затем 9,5 с, а следующий пакет достигает 21 с, за исключением

Вот изображение увеличения времени исходящих запросов

Трассировка стека исключений:

System.Net.Http.HttpRequestException: попытка подключения не удалась, потому что подключенная сторона не правильноответить через определенный промежуток времени или не удалось установить установленное соединение, поскольку подключенный узел не смог ответить ---> System.Net.Sockets.SocketException: попытка подключения не удалась, поскольку подключенная сторона не ответила должным образом через определенный промежуток времени или не установилане удалось подключиться, поскольку подключенный узел не смог ответить в System.Net.Http.ConnectHelper.ConnectAsync (строковый узел, порт Int32, CancellationToken cancellationToken) --- конец трассировки стека внутренних исключений --- в System.Net.Http.ConnectHelper.ConnectAsync (Строковый хост, яПорт nt32, CancellationToken CancellationToken) в System.Threading.Tasks.ValueTask 1.get_Result() at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Threading.Tasks.ValueTask 1.get_Result () в System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync (ValueTask 1 creationTask) at System.Threading.Tasks.ValueTask 1.get_Result в (101) .Net (*) *).Http.HttpConnectionPool.SendWithRetryAsync (запрос HttpRequestMessage, Boolean doRequestAuth, CancellationToken cancellationToken)
в System.Net.Http.RedirectHandler.SendAsync.http:cancellationToken) в System.Net.Http.HttpClient.FinishSendAsyncBuffered (Task`1 sendTask, запрос HttpRequestMessage, CancellationTokenSource cts, Boolean disposeCts) в FunctionApp.BaseFunc. <> c__DisplayClass7_2.d.MoveNext () в E: \ vsts-agent-win-1_work \ 339 \ s \ Services \ Host \ Controllers \ BaseFunc.cs: строка 102 --- Конец трассировки стека из предыдущего расположения, в котором было сгенерировано исключение ---в FunctionApp.BaseFunc.ProcessRun (журнал ILogger, String runId) в E: \ vsts-agent-win-1_work \ 339 \ s \ Services \ Host \ Controllers \ BaseFunc.cs: строка 122.

FunctionApp размещается в плане AppService S1, поэтому нет ограничений на исходящие соединения в 600 (я так полагаю)

Метрики соединений TCP во время исключения (макс было 498): Метрики приложения AzureFunction

TCP-соединения от помощника «Решить проблему» приложения AzureFunction Максимальное количество TCP-соединений во всех состояниях было 502

План обслуживания CPU и Memory of App во время исключения: Метрики плана обслуживания приложения

Приложение - .Net Core 2.2

Мне не удалось воспроизвести это на моем локальном ПК.Но в Azure это происходит почти каждый день в каждой среде (dev, test, prod).После такого сбоя фабрика данных Azure выполняет повторную попытку через 5 минут, и она выполняется каждый раз успешно.

Вот код базового класса, который используется обеими функциями:

 public abstract class BaseFunc
{
    protected abstract string BlobFolderName { get; }
    protected TelemetryClient telemetryClient;
    private static HttpClient _httpClient;

    static BaseFunc()
    {
        HttpClientHandler handler = new HttpClientHandler();
        handler.MaxConnectionsPerServer = 300;
        _httpClient = new HttpClient(handler);
    }
    protected async Task ProcessRun(ILogger log, string runId)
    {
        int processedItems = 0;
        try
        {
            Stopwatch sw = Stopwatch.StartNew();
            ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

            await Authentication("url", log, runId); //sets default Authorization header

            string getIdeaResult = await _httpClient.GetStringAsync("url");
            JObject jsonObject = JObject.Parse(getIdeaResult);
            int ideaCount = (int)jsonObject.SelectToken("total_count");

            List<Task> tasks = new List<Task>();
            string DataPulledDate = DateTime.Now.ToString("dd-MMM-yyyy");
            CloudStorageAccount storageAccount = CloudStorageAccount.Parse("connection string");
            CloudBlobClient cloudBlobClient = storageAccount.CreateCloudBlobClient();
            CloudBlobContainer cloudBlobContainer = cloudBlobClient.GetContainerReference("container");

            string getIdsUri = "url" + $"&limit={batchSize}&offset=";
            int iterations = (int)Math.Ceiling((decimal)ideaCount/batchSize);

            for (int i = 0; i < iterations; i++)
            {
                string result = await _httpClient.GetStringAsync("url" + i * 50);
                JObject jsonIdsObject = JObject.Parse(result);
                int[] ideaIds = jsonIdsObject["content"].Children().Values<int>("id").ToArray();
                foreach (int id in ideaIds)
                {
                    tasks.Add(Task.Run(async () =>
                    {
                        string content = null;
                        using (var response = await _httpClient.SendAsync(new HttpRequestMessage(HttpMethod.Get, "url"+ id))) //Exception is thrown on this line
                        {
                            content = await response.Content.ReadAsStringAsync();
                            response.EnsureSuccessStatusCode();
                        }
                        CloudBlockBlob cloudBlockBlob = cloudBlobContainer.GetBlockBlobReference($"{DataPulledDate}/{BlobFolderName}/ideaId-{id}.json");
                        await cloudBlobContainer.CreateIfNotExistsAsync();
                        await cloudBlockBlob.UploadTextAsync(content);
                        Interlocked.Increment(ref processedItems);
                    }));
                }
            }
            await Task.WhenAll(tasks);
            sw.Stop();
        }
        catch (Exception ex)
        {
            log.LogError(ex, "{RunId}: Run failed. {Items} items processed successfully, Exception: {Exception}.", runId, processedItems, ex.ToString());
            throw;
        }
        finally
        {
            if (telemetryClient != null)
            {
                telemetryClient.Flush();
                Thread.Sleep(3000);
            }
        }
    }
}

сам код функции:

namespace FunctionApp
{
    public class GetIdeas : BaseFunc
    {
        public GetIdeas(TelemetryClient telemetryClient)
        {
            this.telemetryClient = telemetryClient;
        }

        protected override string BlobFolderName { get => "folder"; }
        protected override string GetItemUrl { get => "url"; }

        [FunctionName("GetIdeasFn")]
        public async Task Run([HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = null)] HttpRequest req, ILogger log)
        {
            await ProcessRun(log, $"GetIdeasFn - {DateTime.UtcNow.Ticks}");
        }
    }
}

Ценю любую помощь.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...