Azure 函数遇到 System.Net.Sockets.SocketException

Posted

技术标签:

【中文标题】Azure 函数遇到 System.Net.Sockets.SocketException【英文标题】:Azure Function runs into System.Net.Sockets.SocketException 【发布时间】:2020-01-21 13:26:05 【问题描述】:

我有带有 2 个 HTTP 触发函数的 AzureFunctions 应用程序。两者都来自同一个类,但使用不同的 url 来获取数据。 Azure 数据工厂管道每天都会触发第一个 HTTP 函数 另一个管道在 1 分钟内调用第二个函数

每个函数向第三方网站发出大约 1300 个 HTTP 请求,并将每个响应作为单独的 json 文件存储在 Blob 存储中。

问题几乎每次(但并非总是)第二个函数都会抛出 System.Net.Sockets.SocketException,因为很少有出站请求会遇到常见的 21 秒 TCP 超时。 我注意到奇怪的事情 - Azure 可能出于某种原因限制了我的出站请求:第一批需要近 300 毫秒,下一个序列需要 4.3 秒,然后是 9.5 秒,下一批达到 21 秒,但有异常

Here is image of timing increasing of outbound requests

异常堆栈跟踪:

System.Net.Http.HttpRequestException:连接尝试失败 因为连接方在一段时间后没有正确响应 时间,或建立连接失败,因为连接的主机有 未能响应---> System.Net.Sockets.SocketException: A 连接尝试失败,因为连接方没有正确 一段时间后响应,或建立连接失败 因为连接的主机未能在 System.Net.Http.ConnectHelper.ConnectAsync(字符串主机,Int32 端口, CancellationToken cancelToken) --- 内部异常结束 堆栈跟踪 --- 在 System.Net.Http.ConnectHelper.ConnectAsync(字符串主机,Int32 端口, CancellationToken 取消令牌)在 System.Threading.Tasks.ValueTask1.get_Result() at System.Net.Http.HttpConnectionPool.CreateConnectionAsync(HttpRequestMessage request, CancellationToken cancellationToken) at System.Threading.Tasks.ValueTask1.get_Result() 在 System.Net.Http.HttpConnectionPool.WaitForCreatedConnectionAsync(ValueTask1 creationTask) at System.Threading.Tasks.ValueTask1.get_Result() 在 System.Net.Http.HttpConnectionPool.SendWithRetryAsync(HttpRequestMessage request, Boolean doRequestAuth, CancellationToken cancelToken) 在 System.Net.Http.RedirectHandler.SendAsync(HttpRequestMessage 请求,CancellationToken 取消令牌)在 System.Net.Http.DiagnosticsHandler.SendAsync(HttpRequestMessage 请求,CancellationToken 取消令牌)在 System.Net.Http.HttpClient.FinishSendAsyncBuffered(Task`1 sendTask, HttpRequestMessage 请求,CancellationTokenSource cts,布尔值 处置)在 FunctionApp.BaseFunc.c__DisplayClass7_2.d.MoveNext() 在 E:\vsts-agent-win-1_work\339\s\Services\Host\Controllers\BaseFunc.cs:line 102 --- 从之前抛出异常的位置结束堆栈跟踪 --- 在 FunctionApp.BaseFunc.ProcessRun(ILogger 日志,字符串 runId) 在 E:\vsts-agent-win-1_work\339\s\Services\Host\Controllers\BaseFunc.cs:line 122.

FunctionApp 托管在 AppService 计划 S1 上,因此没有 600 个出站连接的限制(我相信是这样)

异常期间 TCP 连接的指标(最大值为 498): Metrics of AzureFunction App

来自 AzureFunction App 的“解决问题”助手的 TCP 连接 Max TCP connections in all states was 502

异常期间App服务计划的CPU和内存: App Service Plan metrics

应用是.Net Core 2.2

我没能在我的本地电脑上重现这个。但在 Azure 上,它几乎每天都会在每个环境(开发、测试、产品)上发生。 此类失败后,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");
        
    

感谢任何帮助。

【问题讨论】:

您是否为每个连接创建一个新的BaseFunc? aspnetmonsters.com/2016/08/2016-08-27-httpclientwrong @Neil 感谢您的回复。否 - 在初始问题中添加了函数代码 您多久创建一次new GetIdeas?如果每次“通话”一次,那么您就违反了我发布的链接。 @Neil 我没有实例化 GetIdeas。它是 Azure Function 的入口点。这就像以“始终开启”设置运行的 WebJob。所以应该一直运行。 Http调用从外部调用“运行” 抱歉,我错过了 BaseFunc 构造函数上的 static 【参考方案1】:

我遇到了同样的问题,但在我的情况下,有一个代码补丁具有一个长循环机制,它创建了数百个对 Microsoft GraphApi 的请求,并且没有响应一个请求,它正在创建另一个请求。已更正并修复问题!

【讨论】:

以上是关于Azure 函数遇到 System.Net.Sockets.SocketException的主要内容,如果未能解决你的问题,请参考以下文章

Azure 函数和 HTTP OPTIONS 请求

带有 azure 函数输出的流分析,错误写入批次

Azure 函数中的 Az.Functions 模块引发错误

高级计划中的 Python Azure 函数:函数的发布/部署失败……

在 Azure 函数 V2 中将 TraceWriter 替换为 ILogger

囤货Azure Api网关免费版:一“关”通吃微服务/云函数,真香