簡體   English   中英

同時限制Web服務請求的數量

[英]limit # of web service requests simultaneously

我有一個用C#、. NET 4.5編寫的Excel加載項。 它將向Web服務器發送許多Web服務請求以獲取數據。 例如,它將30,000個請求發送到Web服務服務器。 當請求的數據返回時,插件將在Excel中繪制數據。 最初,我異步地完成了所有請求,但是有時我會得到OutOfMemoryException,所以我進行了更改,一個接一個地發送了請求,但是它太慢了,需要很長時間才能完成所有請求。 我想知道是否有一種方法可以一次異步處理100個請求,一旦所有100個請求的數據返回並在Excel中繪圖,然后發送下一個100個請求。

謝謝

編輯
在我的插件上,有一個功能區按鈕“刷新”,單擊該按鈕將開始刷新過程。

在主UI線程上,單擊功能區/按鈕,它將調用Web服務BuildMetaData,一旦返回,在其回調MetaDataCompleteCallback中,將發送另一個Web服務調用
一旦返回,在其回調DataRequestJobFinished中,它將調用plot以在Excel上繪制數據。 見下文

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);
        }
    }  

}

在類上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();
    }
}

在情節課上

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

OutOfMemoryException與同時發送太多請求無關。 這是關於以正確的方式釋放資源。 在我的實踐中,遇到此類異常時有兩個主要問題:

  • 使用不可變結構或System.String類時出錯
  • 不要處理您的可棄資源,尤其是圖形對象和WCF請求。

就報告而言,我認為您遇到了第二類問題。 DataRequestDataRequestResponse是開始調查此類對象的好地方。

如果這樣做沒有幫助,請嘗試以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();

在您的代碼中,我看到一個while循環,您可以在其中存儲大小為100 Task[] ,可以使用其中的WaitAll方法 ,該問題應該得到解決。 抱歉,但是您的代碼足夠多,我無法提供更直接的示例。

我在解析您的代碼以發現正在為您的請求進行迭代時遇到很多麻煩,但是異步批處理的基本模板將是這樣的:

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));        
}

其中doQuery是帶有簽名的東西

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

自從它遞歸以來,我不會將其用於一百萬個請求,但是您的情況將生成僅300深度的調用棧,所以您會很好的。

請注意,這還假設您的數據請求工作是異步完成的,並返回Task 大多數庫都已更新為執行此操作(查找帶有Async后綴的方法)。 如果未公開該api,則可能要創建一個單獨的問題,說明如何專門使您的庫與TPL配合使用。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM