我有一个从服务器获取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;
}
}
答案 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函数来根据您的要求连接和处理消息。