简体   繁体   English

同时限制Web服务请求的数量

[英]limit # of web service requests simultaneously

I have an Excel Add-In written in C#, .NET 4.5. 我有一个用C#、. NET 4.5编写的Excel加载项。 It will send many web service requests to a web server to get data. 它将向Web服务器发送许多Web服务请求以获取数据。 Eg it sends 30,000 requests to web service server. 例如,它将30,000个请求发送到Web服务服务器。 When data of a request comes back, the addin will plot the data in Excel. 当请求的数据返回时,插件将在Excel中绘制数据。 Originally I did all the requests asynchronously, but sometime I will get OutOfMemoryException So I changed, sent the requests one by one, but it is too slow, takes long time to finish all requests. 最初,我异步地完成了所有请求,但是有时我会得到OutOfMemoryException,所以我进行了更改,一个接一个地发送了请求,但是它太慢了,需要很长时间才能完成所有请求。 I wonder if there is a way that I can do 100 requests at a time asynchronously, once the data of all the 100 requests come back and plot in Excel, then send the next 100 requests. 我想知道是否有一种方法可以一次异步处理100个请求,一旦所有100个请求的数据返回并在Excel中绘图,然后发送下一个100个请求。

Thanks 谢谢

Edit 编辑
On my addin, there is a ribbon button "Refresh", when it is clicked, refresh process starts. 在我的插件上,有一个功能区按钮“刷新”,单击该按钮将开始刷新过程。

On main UI thread, ribbon/button is clicked, it will call web service BuildMetaData, once it is returned back, in its callback MetaDataCompleteCallback, another web service call is sent 在主UI线程上,单击功能区/按钮,它将调用Web服务BuildMetaData,一旦返回,在其回调MetaDataCompleteCallback中,将发送另一个Web服务调用
Once it is returned back, in its callback DataRequestJobFinished, it will call plot to plot data on Excel. 一旦返回,在其回调DataRequestJobFinished中,它将调用plot以在Excel上绘制数据。 see below 见下文

RefreshBtn_Click()
{
    if (cells == null) return;
    Range firstOccurence = null;

    firstOccurence = cells.Find(functionPattern, null,
            null, null,
            XlSearchOrder.xlByRows,
            XlSearchDirection.xlNext,
            null, null, null);

    DataRequest request = null;
    _reportObj = null;
    Range currentOccurence = null;

    while (!Helper.RefreshCancelled)
    {                                
        if(firstOccurence == null ||IsRangeEqual(firstOccurence, currentOccurence)) break;

        found = true;

currentOccurence = cells.FindNext(currentOccurence ?? firstOccurence);

        try
        {
            var excelFormulaCell = new ExcelFormulaCell(currentOccurence);

            if (excelFormulaCell.HasValidFormulaCell)
            {                                                                                                      
                request = new DataRequest(_unityContainer, XLApp, excelFormulaCell);

                request.IsRefreshClicked = true;
                request.Workbook = Workbook;
                request.Worksheets = Worksheets;

                _reportObj = new ReportBuilder(_unityContainer, XLApp, request, index, false); 
                _reportObj.ParseParameters();

                _reportObj.GenerateReport();
                //this is necessary b/c error message is wrapped in valid object DataResponse
                //if (!string.IsNullOrEmpty(_reportObj.ErrorMessage)) //Clear previous error message
                {
                    ErrorMessage = _reportObj.ErrorMessage;
                    Errors.Add(ErrorMessage);
                    AddCommentToCell(_reportObj);
                    Errors.Remove(ErrorMessage);
                }
            }                    
        }
        catch (Exception ex)
        {
            ErrorMessage = ex.Message;
            Errors.Add(ErrorMessage);
            _reportObj.ErrorMessage = ErrorMessage;
            AddCommentToCell(_reportObj);
            Errors.Remove(ErrorMessage);
            Helper.LogError(ex);
        }
    }  

}

on Class to GenerateReport 在类上GenerateReport

public void GenerateReport()
{
    Request.ParseFunction();
    Request.MetacompleteCallBack = MetaDataCompleteCallback;
    Request.BuildMetaData();
}

public void MetaDataCompleteCallback(int id)
{
    try
    {
        if (Request.IsRequestCancelled)
        {
            Request.FormulaCell.Dispose();
            return;
        }

        ErrorMessage = Request.ErrorMessage;
        if (string.IsNullOrEmpty(Request.ErrorMessage))
        {
            _queryJob = new DataQueryJob(UnityContainer, Request.BuildQueryString(), DataRequestJobFinished, Request);
        }
        else
        {
            ModifyCommentOnFormulaCellPublishRefreshEvent();
        }
    }
    catch (Exception ex)
    {
        ErrorMessage = ex.Message;
        ModifyCommentOnFormulaCellPublishRefreshEvent();
    }
    finally
    {
        Request.MetacompleteCallBack = null;
    }
} 


public void DataRequestJobFinished(DataRequestResponse response)
{
    Dispatcher.Invoke(new Action<DataRequestResponse>(DataRequestJobFinishedUI), response);
}

public void DataRequestJobFinished(DataRequestResponse response)
{
    try
    {
        if (Request.IsRequestCancelled)
        {
            return;
        }

        if (response.status != Status.COMPLETE)
        {
            ErrorMessage = ManipulateStatusMsg(response);
        }
        else // COMPLETE
        {
            var tmpReq = Request as DataRequest;
            if (tmpReq == null) return;

            new VerticalTemplate(tmpReq, response).Plot();

        }
    }
    catch (Exception e)
    {
        ErrorMessage = e.Message;
        Helper.LogError(e);
    }
    finally
    {
        //if (token != null)
        //    this.UnityContainer.Resolve<IEventAggregator>().GetEvent<DataQueryJobComplete>().Unsubscribe(token);
        ModifyCommentOnFormulaCellPublishRefreshEvent();
        Request.FormulaCell.Dispose();
    }
}

on plot class 在情节课上

public void Plot()
{
... 
   attributeRange.Value2 = headerArray;
   DataRange.Value2 = ....
   DataRange.NumberFormat = ... 
}

OutOfMemoryException is not about the too many requests sent simultaneously. OutOfMemoryException与同时发送太多请求无关。 It is about freeing your resources right way. 这是关于以正确的方式释放资源。 In my practice there are two main problems when you are getting such exception: 在我的实践中,遇到此类异常时有两个主要问题:

  • Wrong working with immutable structures or System.String class 使用不可变结构或System.String类时出错
  • Not disposing your disposable resources, especially graphic objects and WCF requests. 不要处理您的可弃资源,尤其是图形对象和WCF请求。

In case of reporting, for my opinion, you got a second one type of a problem. 就报告而言,我认为您遇到了第二类问题。 DataRequest and DataRequestResponse are good point to start the investigation for the such objects. DataRequestDataRequestResponse是开始调查此类对象的好地方。

If this doesn't help, try to use the Tasks library with async/await pattern, you can find good examples here : 如果这样做没有帮助,请尝试以async/await模式使用Tasks库,您可以在此处找到良好的示例:

// Signature specifies Task<TResult>
async Task<int> TaskOfTResult_MethodAsync()
{
    int hours;
    // . . .
    // Return statement specifies an integer result.
    return hours;
}

// Calls to TaskOfTResult_MethodAsync
Task<int> returnedTaskTResult = TaskOfTResult_MethodAsync();
int intResult = await returnedTaskTResult;
// or, in a single statement
int intResult = await TaskOfTResult_MethodAsync();


// Signature specifies Task
async Task Task_MethodAsync()
{
    // . . .
    // The method has no return statement.  
}

// Calls to Task_MethodAsync
Task returnedTask = Task_MethodAsync();
await returnedTask;
// or, in a single statement
await Task_MethodAsync();

In your code I see a while loop, in which you can store your Task[] of size of 100 , for which you can use the WaitAll method , and the problem should be solved. 在您的代码中,我看到一个while循环,您可以在其中存储大小为100 Task[] ,可以使用其中的WaitAll方法 ,该问题应该得到解决。 Sorry, but your code is huge enough, and I can't provide you a more straight example. 抱歉,但是您的代码足够多,我无法提供更直接的示例。

I'm having a lot of trouble parsing your code to figure out is being iterated for your request but the basic template for batching asynchronously is going to be something like this: 我在解析您的代码以发现正在为您的请求进行迭代时遇到很多麻烦,但是异步批处理的基本模板将是这样的:

static const int batchSize = 100;
public async Task<IEnumerable<Results>> GetDataInBatches(IEnumerable<RequestParameters> parameters) {
    if(!parameters.Any())
        return Enumerable.Empty<Result>();
    var batchResults = await Task.WhenAll(parameters.Take(batchSize).Select(doQuery));
    return batchResults.Concat(await GetDataInBatches(parameters.Skip(batchSize));        
}

where doQuery is something with the signature 其中doQuery是带有签名的东西

Task<Results> async doQuery(RequestParameters parameters) {
   //.. however you do the query
}

I wouldn't use this for a million requests since its recursive, but your case should would generate a callstack only 300 deep so you'll be fine. 自从它递归以来,我不会将其用于一百万个请求,但是您的情况将生成仅300深度的调用栈,所以您会很好的。

Note that this also assumes that your data request stuff is done asynchronously and returns a Task . 请注意,这还假设您的数据请求工作是异步完成的,并返回Task Most libraries have been updated to do this (look for methods with the Async suffix). 大多数库都已更新为执行此操作(查找带有Async后缀的方法)。 If it doesn't expose that api you might want to create a separate question for how to specifically get your library to play nice with the TPL. 如果未公开该api,则可能要创建一个单独的问题,说明如何专门使您的库与TPL配合使用。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM