Azure Functions 使用计划 WCF 客户端错误 "attempt failed because the connected..."

Azure Functions Consumption Plan WCF Client Errors "attempt failed because the connected..."

我们最近将基于 azure functions durable functions 的应用程序从专用 s1/standard 应用程序服务计划切换到动态 y1 计划以支付相同的费用,现在我们遇到了一个常见错误:

“连接尝试失败,因为连接方在一段时间后没有正确响应,或者建立的连接失败,因为连接的主机没有响应。”

这发生在应用程序大约一个小时后 运行。异常来自 svcutil 生成的 wcf 客户端。我相当确定这与来自消费功能应用程序的套接字连接的限制与 https://docs.microsoft.com/en-us/azure/azure-functions/functions-scale#service-limits but not totally convinced because i do NOT see the log message "Host thresholds exceeded: Connections" listed at https://docs.microsoft.com/en-us/azure/azure-functions/manage-connections#connection-limit

中所述的“专用”应用程序计划有关

我们的客户端实际上是围绕在我们的包装器构造上实例化的十几个 wcf 客户端的包装器。包装器作为单例注册到 di

builder.Services.AddSingleton<IWrapperClient, OurSoapClient>();

public OurSoapClient(
            IMemoryCache memoryCache,
            IOptions<Options> options,
            ILogger<OurSoapClient> log
        )
        {
            this.options = options.Value;
            this.memoryCache = memoryCache;
            this.log = log;


            this.metaClient = new Meta.MetaWebServiceClient(
                Meta.MetaWebServiceClient.EndpointConfiguration.MetaWebServicePort,
                this.options.MetaHref
            );
            

            this.wmsClient = new Wms.WmsWebServiceClient(
                Wms.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsStageItemsClient = new Wms.Stage.Items.WmsWebServiceClient(
                Wms.Stage.Items.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsReceiptClient = new Wms.Stage.ExpectedReceipts.WmsWebServiceClient(
                Wms.Stage.ExpectedReceipts.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsStageRmaClient = new Wms.Stage.Rma.WmsWebServiceClient(
                Wms.Stage.Rma.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsStageShipmentsClient = new Wms.Stage.Shipments.WmsWebServiceClient(
                Wms.Stage.Shipments.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );


            this.wmsUpdateShipmentsClient = new Wms.Updates.ShippingResults.WmsWebServiceClient(
                Wms.Updates.ShippingResults.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsUpdatesReceivingResultsClient = new Wms.Updates.ReceivingResults.WmsWebServiceClient(
                Wms.Updates.ReceivingResults.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsUpdatesInventoryAdjustmentClient = new Wms.Updates.InventoryAdjustments.WmsWebServiceClient(
                Wms.Updates.InventoryAdjustments.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsInboundOrderClient = new Wms.Inbound.CurrentAndHistory.WmsWebServiceClient(
                Wms.Inbound.CurrentAndHistory.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsOutboundOrderClient = new Wms.Outbound.CurrentAndHistory.WmsWebServiceClient(
                Wms.Outbound.CurrentAndHistory.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsInboundOrderDetailsClient = new Wms.Inbound.CurrentAndHistoryDetails.WmsWebServiceClient(
                Wms.Inbound.CurrentAndHistoryDetails.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );

            this.wmsOutboundOrderDetailsClient = new Wms.Outbound.CurrentAndHistoryDetails.WmsWebServiceClient(
                Wms.Outbound.CurrentAndHistoryDetails.WmsWebServiceClient.EndpointConfiguration.WmsWebServicePort,
                this.options.WmsHref
            );
        }

切换回标准应用服务计划似乎可以解决这个问题。 我相当确定持久函数不是这里的原因,但只是为了清楚所有对客户端的调用都是从 Orchestrator 或 Activity 函数发生的......我们在两种函数类型中看到相同的失败错误。

我注意到重复的一个轶事是错误似乎发生在第二个 OurWrapperClient 被实例化(再次实例化所有 wcf 客户端)之后......因为它是一个单例,这一定是 azure 函数控制平面旋转启动我的应用程序的另一个实例

所以有几个问题:

  1. 知道如何证明这是与最大出站连接数相关的问题吗?
  2. 关于这成为问题的原因的任何建议
  3. 假设这与 WCF 有关
    1. 使用 wcf 客户端的正确方法是什么?应该为每次使用 usings 的调用实例化它们,还是像我们一样为每个包装器客户端实例化一次然后仅处理一次?
    2. 我们是否应该使用 DI 将它们实例化为单例,然后注入它们?这意味着我相信 DI 会在他们身上调用 Dispose
    3. 有没有办法将要使用的http客户端传递给wcf客户端生成的代码?很多 azure 函数最佳实践都说要为所有 http I/O 注入一个单独的 http 客户端,但我不知道如何使用 WCF 做到这一点。

使用应用程序洞察,我注意到大约需要一个小时的时间对应于我的应用程序在那个时间切换主机实例。最终我开始看到在部署时它会立即失败..ie 有一个“坏”主机。打开了一个 MS 支持案例,他们远程访问了一个错误的实例,发现他们无法从该主机进行 TCP ping。

您分配的每个网站空间都从 IP 池发出请求,我怀疑我的目标 WAF 出于某种原因阻止了其中一些 IP。切换到保证新网站空间的新区域(它们在创建时分配,但特定于区域)使问题消失。

在此期间确实找到了 https://github.com/dotnet/runtime/issues/35508,这看起来很相似