考虑到Azure.NET web应用程序中的SNAT端口限制,对于数千个调用来说,什么是最理想的并行化策略?

考虑到Azure.NET web应用程序中的SNAT端口限制,对于数千个调用来说,什么是最理想的并行化策略?,.net,azure,parallel-processing,architecture,azure-web-app-service,.net,Azure,Parallel Processing,Architecture,Azure Web App Service,我设计了一个.NETAPI,需要每隔几分钟处理数千(最终)数十万个请求 这是一个自定义推送API,它反过来使用Azure通知中心(以及相应的NuGet包)将请求发送到Azure通知中心 我遇到的问题是,由于Azure SNAT端口耗尽,我得到了内部异常。该应用程序在1个S3实例上运行,理论上限制为8064个tcp连接(每个)。端口限制适用于所有连接(http、tcp等)。我在Azure应用程序上遇到了几个例外,它们说它无法连接到主机notificationhub.windows.net,我认为它

我设计了一个.NETAPI,需要每隔几分钟处理数千(最终)数十万个请求

这是一个自定义推送API,它反过来使用Azure通知中心(以及相应的NuGet包)将请求发送到Azure通知中心

我遇到的问题是,由于Azure SNAT端口耗尽,我得到了内部异常。该应用程序在1个S3实例上运行,理论上限制为8064个tcp连接(每个)。端口限制适用于所有连接(http、tcp等)。我在Azure应用程序上遇到了几个例外,它们说它无法连接到主机notificationhub.windows.net,我认为它是Azure通知中心基础设施的一部分

无法连接到远程服务器。TrackingId:xxxxxx,时间戳:2019-11-25T11:39:02.9349637Z无法连接到远程服务器连接尝试失败,因为连接方在一段时间后没有正确响应,或者由于连接的主机没有响应23.100.65.137:443而建立的连接失败

上述IP解析为notificationhub.windows.net

在Push API中,有一个异步端点,该端点接受通知对象列表,以允许使用者对此API发出单个调用,然后在内部有一个Task.whalll来满足每个请求/对象

一个要考虑的是,由于在应用程序的设计上的限制,消费者正在发送单个对象的所有对象(现在在5K到8K之间)。我知道将它们分成1k个包是很理想的,然后我们可以利用向外扩展的优势,但目前无法做到这一点

public async Task<IList<NotificationResponse>> SendNotifications(IList<NotificationRequest> pushRequests, string appId)
    {
        var responses = new List<Task<NotificationResponse>>();

        var app = await GetAppFromCache(appId).ConfigureAwait(false);

        foreach (var req in pushRequests)
        {
            responses.Add(SendPushNotification(req, app));
        }

        return await Task.WhenAll(responses.ToArray());
    }


public async Task<NotificationResponse> SendPushNotification(PushNotificationRequest pushReq, Application application)
        {
        NotificationOutcome outcome = null;
        var result = new NotificationResponse();

        var _hub = await GetNotificationHub(application.HubName).ConfigureAwait(false);

        var tag = //create Tag logic here

        var notification = GetBasicNotification(pushReq.Message, pushReq.Title);

        outcome = await _hub.SendNotificationAsync(notification, tag).ConfigureAwait(false);

        if (outcome != null)
        {
            result.NotificationId = outcome.NotificationId;
            result.Status = Constants.Success;
        }
        else
        {
          //error handling omitted for brevity
        }

        return result;
    }
公共异步任务发送通知(IList pushRequests,字符串appId)
{
var responses=新列表();
var app=await GetAppFromCache(appId).ConfigureAwait(false);
foreach(pushRequests中的var req)
{
添加(发送推送通知(请求、应用));
}
return wait Task.WhenAll(responses.ToArray());
}
公共异步任务SendPushNotification(PushNotificationRequest pushReq,应用程序)
{
NotificationOutput=null;
var结果=新的NotificationResponse();
var_hub=await GetNotificationHub(application.HubName).ConfigureAwait(false);
var tag=//在此处创建标记逻辑
var通知=GetBasicNotification(pushReq.Message,pushReq.Title);
结果=等待_hub.SendNotificationAsync(通知,标记).ConfigureAwait(false);
如果(结果!=null)
{
result.NotificationId=output.NotificationId;
result.Status=Constants.Success;
}
其他的
{
//为简洁起见,省略了错误处理
}
返回结果;
}
这并不多,内部检索的大部分数据都来自缓存(应用程序和集线器连接),但始终需要连接到集线器以发送每条消息。我没有研究Microsoft.Azure.NotificationHubs nuget包的内部结构,但我认为它们的连接代码是有效的,并且它们可能在内部使用(并重用)HttpClient

问题是,理论上,每个连接都会保持打开状态100秒。限制为8064,如果我想处理200000个通知,似乎在处理过程中很快就会遇到端口耗尽

Task.whallparallelization策略在这里不是最理想的吗?我是否应该查看其他设置以避免失败?我不确定切换到Parallel.ForEach也能解决这个问题,因为即使我处理的是500或1000个块,调用也会成功,但连接保持打开100秒,在此期间,我已经在处理其他块,这些块最终会遇到端口饥饿

由于这是一个批量端点,而问题在于传出tcp连接,因此我无法通过扩展到更多实例来解决问题,因为整个调用运行在一段代码上。目前也不可能将其转移到基于事件(函数)的解决方案中

public async Task<IList<NotificationResponse>> SendNotifications(IList<NotificationRequest> pushRequests, string appId)
    {
        var responses = new List<Task<NotificationResponse>>();

        var app = await GetAppFromCache(appId).ConfigureAwait(false);

        foreach (var req in pushRequests)
        {
            responses.Add(SendPushNotification(req, app));
        }

        return await Task.WhenAll(responses.ToArray());
    }


public async Task<NotificationResponse> SendPushNotification(PushNotificationRequest pushReq, Application application)
        {
        NotificationOutcome outcome = null;
        var result = new NotificationResponse();

        var _hub = await GetNotificationHub(application.HubName).ConfigureAwait(false);

        var tag = //create Tag logic here

        var notification = GetBasicNotification(pushReq.Message, pushReq.Title);

        outcome = await _hub.SendNotificationAsync(notification, tag).ConfigureAwait(false);

        if (outcome != null)
        {
            result.NotificationId = outcome.NotificationId;
            result.Status = Constants.Success;
        }
        else
        {
          //error handling omitted for brevity
        }

        return result;
    }
这里的最佳解决方案是什么

主要是,我想了解一般系统设计的目的,如果基于此端口限制,并且考虑到处理任何请求可能会导致1到5个传出连接(到Cosmos、通知集线器、表存储等),如果具有“批量”端点,甚至像web作业一样处理(检索所有挂起的请求并尝试从一个实例中立即完成)并不是Azure中设计或支持消息处理的最佳方式


非常感谢您的任何反馈或评论!

我看到,在您的
SendPushNotification
方法中,您通过
var\u hub=wait GetNotificationHub(application.HubName)。configurewait(false);

我可以知道您的
GetNotificationHub
方法中发生了什么吗?是否每次调用该方法时都会创建一个新的
NotificationHubClient


据我所知,您只需要一个
NotificationHubClient
实例。它可以用来推送通知。有关更多信息,您可以参考示例。

Hi Jack。对不起,我忘了提到这一点。不,绝对不是每次都创建一个新实例。我创建了一次并将其缓存在MemoryCache中,因此应该只有一次命中。请注意,因为这是一个企业API,我确实需要为每个应用程序提供一个NotificationHubClient,所以我最多只能为每个应用程序创建一个NHC,并将它们缓存在内存中。现在,只有2个。正如我提到的,对该应用程序的检索是缓存的,因此