如何在Azure WebJob函数

时间:2016-07-07 15:48:14

标签: azure azure-storage azure-webjobs

我有一个从服务器获取api数据的异步方法。当我在本地计算机上运行此代码时,在控制台应用程序中,它以高速执行,每分钟在异步函数中推送几百个http调用。但是当我从Azure WebJob队列消息中触发相同的代码时,它似乎同步操作并且我的数字爬行 - 我确信我的方法中缺少一些简单的东西 - 任何帮助都会受到赞赏。

(1).. WebJob函数,用于侦听队列中的消息并在收到的消息上启动api get进程:

public class Functions
    {
        // This function will get triggered/executed when a new message is written 
        // on an Azure Queue called queue.

        public static async Task ProcessQueueMessage ([QueueTrigger("myqueue")] string message, TextWriter log)
        {
            var getAPIData = new GetData();
            getAPIData.DoIt(message).Wait();
            log.WriteLine("*** done: " + message);
        }
    }

(2)天青以外的班级以异步模式工作......

 class GetData
    {
        // wrapper that is called by the message function trigger
        public async Task DoIt(string MessageFile)
        {
            await CallAPI(MessageFile);
        }

        public async Task<string> CallAPI(string MessageFile)
        {
            /// create a list of sample APIs to call...
            var apiCallList = new List<string>();
            apiCallList.Add("localhost/?q=1");
            apiCallList.Add("localhost/?q=2");
            apiCallList.Add("localhost/?q=3");
            apiCallList.Add("localhost/?q=4");
            apiCallList.Add("localhost/?q=5");

            // setup httpclient
            HttpClient client =
                new HttpClient() { MaxResponseContentBufferSize = 10000000 };
            var timeout = new TimeSpan(0, 5, 0); // 5 min timeout
            client.Timeout = timeout;

            // create a list of http api get Task...
            IEnumerable<Task<string>> allResults = apiCallList.Select(str => ProcessURLPageAsync(str, client));
            // wait for them all to complete, then move on...
            await Task.WhenAll(allResults);

            return allResults.ToString();
        }

        async Task<string> ProcessURLPageAsync(string APIAddressString, HttpClient client)
        {
            string page = "";
            HttpResponseMessage resX;

            try
            {
                // set the address to call
                Uri URL = new Uri(APIAddressString);
                // execute the call
                resX = await client.GetAsync(URL);
                page = await resX.Content.ReadAsStringAsync();
                string rslt = page;
                // do something with the api response data
            }
            catch (Exception ex)
            {
                // log error
            }
            return page;
        }

    }

2 个答案:

答案 0 :(得分:6)

首先,因为您的触发功能是async,所以您应该使用await而不是.Wait()。等待将阻止当前线程。

public static async Task ProcessQueueMessage([QueueTrigger("myqueue")] string message, TextWriter log)
{
    var getAPIData = new GetData();
    await getAPIData.DoIt(message);
    log.WriteLine("*** done: " + message);
}

无论如何,您可以从documentation

中找到有用的信息
  

并行执行

     

如果您有多个功能侦听不同的队列,SDK会在同时收到消息时并行调用它们。

     

当收到单个队列的多条消息时也是如此。默认情况下,SDK一次获取一批16个队列消息,并执行并行处理它们的功能。 The batch size is configurable。当正在处理的数量减少到批量大小的一半时,SDK将获得另一个批处理并开始处理这些消息。 因此,每个函数处理的最大并发消息数是批量大小的1.5倍。此限制分别适用于具有QueueTrigger属性的每个函数。

以下是配置批量大小的示例代码:

var config = new JobHostConfiguration();
config.Queues.BatchSize = 50;
var host = new JobHost(config);
host.RunAndBlock();

但是,同时运行太多线程并不一定是一个好的选择,可能导致性能不佳。

另一个选择是扩展您的webjob:

  

多个实例

     

如果您的Web应用程序在多个实例上运行,则在每台计算机上运行连续的WebJob,并且每台计算机将等待触发器并尝试运行功能。 WebJobs SDK队列触发器自动阻止函数多次处理队列消息;函数不必写成幂等的。但是,如果要确保即使存在多个主机Web应用程序实例,也只运行一个函数实例,则可以使用Singleton属性。

答案 1 :(得分:2)

读取此Webjobs SDK documentation - 您应该期望的行为是您的流程将一次运行并处理一条消息,但如果创建了更多实例(您的应用服务),则会扩展。如果您有多个队列,它们将并行触发。

为了提高性能,请参阅我发送给您的链接中的配置设置部分,该部分指的是批处理中可以触发的消息数。

如果你想并行处理多个消息,并且不想依赖实例扩展,那么你需要使用线程(异步不是关于多线程并行,而是更有效地使用你正在使用的线程)。所以你的队列触发器函数应该从队列中读取消息,创建一个线程并“发射并忘记”该线程,然后从触发器函数返回。这会将消息标记为已处理,并允许处理队列中的下一条消息,即使理论上您仍处理较早的消息。请注意,您需要包含自己的错误处理逻辑,并确保在线程抛出异常或无法处理消息时数据不会丢失(例如,将其置于毒性队列中)。

另一个选择是不使用[queuetrigger]属性,并直接使用Azure存储队列sdk API函数来根据您的要求连接和处理消息。