本文介绍了是否可以从ASP.NET Core Controller触发3000个并行异步HTTP请求?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个Web API控制器,可以从前端接收过滤器对象.

I have a web API controller that receives a filter object from the front-end.

过滤器基本上只是一个包含过滤条件的对象的json数组.

The filter basically is just a json array of objects that contains the filtering criteria.

现在,基于该过滤器,我必须针对azure API(Azure日志分析api)运行查询.

Now based on that filter I have to run a query against an azure API (Azure log analytics api).

此代码段中对此进行了说明:

This is illustrated in this code snippet :

 var tasks = filters.Select(filter=>
            {
                try
                {
                    return service.GetPerformanceCounters(filter.SComputerId, filter.WorkSpace);
                }
                catch (Exception ex)
                {
                    return Task.Run(() => new List<Metrics>());
                }
            })
            .ToList();

            var metrics = (await Task.WhenAll(tasks))
                                    .SelectMany(metric => metric)
                                    .ToList();

这基本上意味着,如果我有3000个过滤器对象,则必须运行3000个异步并行请求.

This basically means that if I have 3000 filter object I'll have to run 3000 asynchronous Parallel requests.

但是当我测试此代码时,当我到达100个过滤器对象时,异步并行任务失败.

But when I was testing this code as I get to 100 filter object the asynchronous parallel tasks fail.

我在调试面板中收到以下异常:

I get the following Exception in the debug panel :

如何在不出现此问题的情况下运行100个以上的异步HTTP请求,我无法控制从前端发送的过滤器的数量,但我需要能够运行与过滤器的数量.

How to make it possible to run more than 100 async HTTP request without having this problem, I have no control over the number of filters sent from the front but I need to be able to run a number of parallel async operation equivalent to the number of filters.

https://dev.loganalytics.io/documentation/使用-API/限制

文档指出:

GetPerformanceCounters实现.

        public async Task<List<Entities.Metrics>> GetPerformanceCounters(string sourceComputerId, string workspaceId)
    {

        Debug.WriteLine("New Task lauched");


        // The log analytics query 
          var query = $@"Perf 
        | where SourceComputerId == '{sourceComputerId}'
        | summarize arg_max(TimeGenerated, *) by SourceComputerId";




        var response = await RemoteKustoProvider.RunWorkSpaceQueryAsync
                  (
                        workspace: workspaceId,
                        query: query
                 );

        Debug.WriteLine("Task Finished");

        // Send query to the Kusto-Engine. 
        return JsonConvert.DeserializeObject<List<Entities.Metrics>>
        (
           response
       );
    }

预先感谢您帮助我解决此问题.

Thanks in advance for helping me to solve this issue.

推荐答案

此处有 number 个问题.首先,您需要意识到所有活动都需要一个线程.这些线程来自线程池,通常开箱即用.这意味着从一开始,您就用尽了这么多任务的线程池,因此很可能其中有2000个正在排队.异步 helps ,但这不是魔术.一旦请求发出,某些线程可能最终会返回到池,但是不能保证线程切换.无论如何,您耗尽线程池的速度仍然快于填充线程池的速度.

There's a number of issues here. First and foremost, you need to realize that all active work requires a thread. Those threads come from the thread pool, which is generally set to 1000 out of the box. That means right from the start, you're exhausting the thread pool with that many tasks, so likely 2000 of them are being queued. Async helps, but it's not magic. Some of the threads may end up returning the to the pool once the request goes out, but thread switches are not guaranteed. Regardless, you're still draining the thread pool faster than you can fill it.

然后,目前尚不清楚这里实际发生了什么,因为有多个层次,并且您一次向我们提供了一个层次.您发布了 GetPerformanceCounters 的功能,但现在尚不清楚 RemoteKustoProvider.RunWorkSpaceQueryAsync 的功能.在某些时候,您正在使用类似 HttpClient 之类的东西,以便最终向Azure发出请求.根据您的操作方式(坦率地说,根据这段代码的质量,我不太希望您正确执行此操作),您可能还会耗尽连接池.这是一个更为狭窄的资源,因此这意味着您的吞吐量将大大降低,从而导致整个链条的进一步备份.

Then, it's not clear what's actually happening here, because there's multiple layers and you're spoon-feeding us a layer at a time. You posted what GetPerformanceCounters does, but now it's not clear what RemoteKustoProvider.RunWorkSpaceQueryAsync does. At some point, you're using something like HttpClient, though, in order to eventually make the request to Azure. Depending on how you're doing that (and frankly, based on the quality of this code, I have no great hope you're doing it correctly), you're likely also exhausting the connection pool. That's a far more constricted resource, so that means your throughput is drastically reduced, causing further back up down the chain.

接下来,即使您可以一次将所有3000个请求发送出去,这实际上也构成了对收件人服务器的DDoS攻击.如果您实际上受到另一端防火墙的限制或完全阻止,我将不会感到惊讶.那样的话,当然-您猜对了-也会使其他一切都成为瓶颈.

Next, even if you could just blast out all 3000 requests at once, that essentially amounts to a DDoS attack on the recipient server. I wouldn't be surprised at all if you're actually being throttled or outright blocked by firewalls on the other end. That then of course - you guessed it - bottlenecks everything else down the line as well.

最后,即使所有这些功能都按您的预期进行,触发3000个任务也与并行处理"不同.为此,您需要利用TPL中的 Parallel 类.

Finally, even if all this function as you intended, firing off 3000 tasks is not the same thing as "parallel processing". For that you'd need to utilize the Parallel class from the TPL.

我不确定您的最终目标是什么,但是肯定有一种更有效的方法,可以发送3000个单独的请求.如果确实没有,则需要对其进行批处理-在较长的时间内发送几次.整个过程应移至外部过程,并且仅应由Web应用程序安排.然后,您可以使用SignalR之类的工具来推送更新,并在完成所有操作后最终将结果回传给用户.

I'm not sure what you're ultimate goal is here, but there's surely a more efficient way to do it that sending 3000 separate requests. If there truly is not, then you need to batch them - sending a few a time over a longer period. The whole process should be moved off to an external process, and should be merely scheduled by your web application. You can then use something like SignalR to push updates and eventually the result back down to the user when it all completes.

这篇关于是否可以从ASP.NET Core Controller触发3000个并行异步HTTP请求?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

10-24 20:07