在Azure批处理中运行Azure数据工厂活动时,应如何处理异步性

时间:2017-06-22 14:26:42

标签: c# azure asynchronous azure-data-factory azure-batch

背景

我在某种程度上简化了这种情况,但这是一般性问题。

我使用Azure数据工厂将自定义API中的数据提取到Azure数据仓库中的表中。我正在使用IDotNetActivity来运行调用API的C#代码并将数据加载到数据仓库中。该活动在Azure Batch中运行。

在活动本身中,在调用自定义API之前,我从Azure Blob存储中的文件加载人员列表。 然后,我为文件中的每个人调用自定义API。 这些调用是一个接一个地顺序进行的。 问题是这种方法需要太长时间。 文件大小可能会增加,所以花费的时间只会变得更糟。

我试图改善效果的事情

  • 使API调用异步并分批调用它们。奇怪的是,这个调用速度较慢。看起来批处理过程不能处理async / await。
  • 我们看到的另一个奇怪的是,MoreLinq的Batch命令根本不起作用。我已经检查了这个源代码: https://github.com/morelinq/MoreLINQ/blob/master/MoreLinq/Batch.cs。这使用了yield return,但我不知道为什么它不起作用,或者它与async / await问题有关。

主要问题

Azure Batch是否支持async / await?

其他问题

  • 如果Azure不支持async / await,那么解决此问题的更好方法是什么?即使用作业管理器并启动更多节点。
  • 有人可以解释为什么MoreLinq的Batch在Azure Batch中不起作用吗? 以下是受影响代码的片段:

    List<int> personIds = GetPersonIds(clientAddress, clientUsername, clientPassword);
    var customResults = new List<CustomApiResult>();
    foreach (var personIdsBatch in personIds.Batch(100))
    {
        customResults.AddRange(GetCustomResultsByBatch(address, username, password, personIdsBatch));
    }
    

1 个答案:

答案 0 :(得分:1)

根据我的理解,personIds.Batch(100)只需将personIds批量放入大小(100)的桶中。

//method1
foreach (var personIdsBatch in personIds.Batch(100))
{
    customResults.AddRange(GetCustomResultsByBatch(address, username, password, personIdsBatch));
}

//method2
customResults.AddRange(GetCustomResultsByBatch(address, username, password, personIds));

上述两种方法都会按顺序为每个人调用自定义API,而method1则为处理同一任务添加了额外的逻辑。

  

Azure Batch是否支持async / await?

根据您的代码,我将IDotNetActivity实现定义如下,您可以参考它:

public class MyDotNetActivity : IDotNetActivity
{
    public IDictionary<string, string> Execute(IEnumerable<LinkedService> linkedServices, IEnumerable<Dataset> datasets, Activity activity, IActivityLogger logger)
    {
        return ExecuteAsync(linkedServices, datasets, activity, logger).Result;
    }

    async Task<IDictionary<string, string>> ExecuteAsync(IEnumerable<LinkedService> linkedServices, IEnumerable<Dataset> datasets, Activity activity, IActivityLogger logger)
    {
        List<int> personIds = await GetPersonIds("{clientAddress}", "{clientUsername}", "{clientPassword}");
        var tasks = new List<Task<List<CustomApiResult>>>();
        foreach (var personIdsBatch in personIds.Batch(100))
        {
            tasks.AddRange(GetCustomResultsByBatch("{address}", "{username}", "{password}", "{personIdsBatch}"));
        }

        var taskResults = await Task.WhenAll(tasks);
        List<CustomApiResult> customResults = taskResults.SelectMany(r=>r).ToList();

        //process the custom api results

        return new Dictionary<string, string>();
    }

    async Task<List<CustomApiResult>> GetCustomResultsByBatch(string address, string username, string password, IEnumerable<int> personIdsBatch)
    {
        //Get Custom Results By Batch
        return new List<CustomApiResult>();
    }

    async Task<List<int>> GetPersonIds(string clientAddress, string clientUsername, string clientPassword)
    {
        //load a list of people from a file in Azure Blob storage
        return new List<int>(); 
    }
}

另外,我假设您可以按如下方式利用Parallel.ForEach并行执行同步作业:

List<int> personIds = GetPersonIds(clientAddress, clientUsername, clientPassword);
var customResults = new List<CustomApiResult>();
Parallel.ForEach(personIds.Batch(100), 
new ParallelOptions()
{
    MaxDegreeOfParallelism=5
},
(personIdsBatch) =>
{
    var results = GetCustomResultsByBatch(address, username, password, personIdsBatch);
    lock (customResults)
    {
        customResults.AddRange(results);
    }
});