C# Aws Sqs消费者-仅当消息可以立即处理时进行轮询
我正在尝试创建一个AWS SQS windows服务使用者,它将以10个为一批轮询消息。每个消息将在其自己的任务中执行,以便并行执行。消息处理包括调用不同的api和发送电子邮件,因此可能需要一些时间 我的问题是,首先,我只想在可以立即处理10条消息时轮询队列。这是由于sqs可见性超时,接收到的消息“wait”可能超过可见性超时并“返回”队列。这将产生重复。我不认为调整可见性超时是好的,因为仍然有消息被复制的机会,这正是我试图避免的。其次,我希望对并行性有某种限制(例如,100个并发任务的最大限制),这样服务器资源就可以被隔离,因为服务器中还有其他应用程序在运行C# Aws Sqs消费者-仅当消息可以立即处理时进行轮询,c#,concurrency,task-parallel-library,amazon-sqs,C#,Concurrency,Task Parallel Library,Amazon Sqs,我正在尝试创建一个AWS SQS windows服务使用者,它将以10个为一批轮询消息。每个消息将在其自己的任务中执行,以便并行执行。消息处理包括调用不同的api和发送电子邮件,因此可能需要一些时间 我的问题是,首先,我只想在可以立即处理10条消息时轮询队列。这是由于sqs可见性超时,接收到的消息“wait”可能超过可见性超时并“返回”队列。这将产生重复。我不认为调整可见性超时是好的,因为仍然有消息被复制的机会,这正是我试图避免的。其次,我希望对并行性有某种限制(例如,100个并发任务的最大限制
如何做到这一点?或者有其他方法来解决这些问题吗?此答案做出以下假设:
/// <summary>
/// Starts an execution loop that fetches batches of messages sequentially,
/// and process them one by one in parallel.
/// </summary>
public static async Task ExecutionLoopAsync<TMessage>(
Func<int, Task<TMessage[]>> fetchMessagesAsync,
Func<TMessage, Task> processMessageAsync,
int fetchCount,
int maxDegreeOfParallelism,
CancellationToken cancellationToken = default)
{
// Arguments validation omitted
var semaphore = new SemaphoreSlim(maxDegreeOfParallelism, maxDegreeOfParallelism);
// Count how many times we have acquired the semaphore, so that we know
// how many more times we have to acquire it before we exit from this method.
int acquiredCount = 0;
try
{
while (true)
{
Debug.Assert(acquiredCount == 0);
for (int i = 0; i < fetchCount; i++)
{
await semaphore.WaitAsync(cancellationToken);
acquiredCount++;
}
TMessage[] messages = await fetchMessagesAsync(fetchCount)
?? Array.Empty<TMessage>();
for (int i = 0; i < messages.Length; i++)
{
if (i >= fetchCount) // We got more messages than we asked for
{
await semaphore.WaitAsync();
acquiredCount++;
}
ProcessAndRelease(messages[i]);
acquiredCount--;
}
if (messages.Length < fetchCount)
{
// We got less messages than we asked for
semaphore.Release(fetchCount - messages.Length);
acquiredCount -= fetchCount - messages.Length;
}
// This method is 'async void' because it is not expected to throw ever
async void ProcessAndRelease(TMessage message)
{
try { await processMessageAsync(message); }
catch { } // Swallow exceptions
finally { semaphore.Release(); }
}
}
}
catch (SemaphoreFullException)
{
// Guard against the (unlikely) scenario that the counting logic is flawed.
// The counter is no longer reliable, so skip the awaiting in finally.
acquiredCount = maxDegreeOfParallelism;
throw;
}
finally
{
// Wait for all pending operations to complete. This could cause a deadlock
// in case the counter has become out of sync.
for (int i = acquiredCount; i < maxDegreeOfParallelism; i++)
await semaphore.WaitAsync();
}
}
//
///启动执行循环,按顺序获取成批消息,
///一个接一个地并行处理。
///
公共静态异步任务执行LoopAsync(
Func fetchMessagesAsync,
Func processMessageAsync,
int fetchCount,
int maxDegreeOfParallelism,
CancellationToken CancellationToken=默认值)
{
//省略参数验证
var信号量=新信号量lim(maxDegreeOfParallelism,maxDegreeOfParallelism);
//计算我们获得信号量的次数,以便我们知道
//在我们退出这个方法之前,我们还要获得多少次。
int acquiredCount=0;
尝试
{
while(true)
{
Assert(acquiredCount==0);
for(int i=0;i=fetchCount)//我们收到的消息比我们要求的要多
{
wait semaphore.WaitAsync();
acquiredCount++;
}
ProcessAndRelease(消息[i]);
已获取帐户--;
}
if(messages.Length
用法示例:
var cts = new CancellationTokenSource();
Task executionTask = ExecutionLoopAsync<Message>(async count =>
{
return await GetBatchFromAwsAsync(count);
}, async message =>
{
await ProcessMessageAsync(message);
}, fetchCount: 10, maxDegreeOfParallelism: 100, cts.Token);
var cts=new CancellationTokenSource();
任务executionTask=ExecutionLoopAsync(异步计数=>
{
返回wait wait GetBatchFromAwsAsync(计数);
},异步消息=>
{
等待ProcessMessageAsync(消息);
},fetchCount:10,maxDegreeOfParallelism:100,cts.Token);
感谢@Theodor为理解和回答问题花了大量时间。你的假设是正确的。对于#5,当从aws获取时发生异常时。如果我捕捉到异常并只返回0消息,它会工作吗?@eyese7en是的,如果fetchMessagesAsync
返回了一个空数组,那么循环中将不再需要执行任何操作,并且fetchMessagesAsync
将立即再次调用(没有任何延迟)。您可以考虑通过添加一个额外的<代码> TimeStudioFixMeaseRealyTrime< /Cuff>参数来增强<代码> ActhRunyActhyCys机制,并在一个内部重试循环中包含<代码>等待FETCHMISGASEASYNC/<代码>行,其中包含一个具有指定持续时间的异步延迟。@特奥多尔天真地说,睡眠会像延迟一样工作吗?如果可以的话,我会在几天内完全实现后将其标记为答案。@eyese7en是的,Thread.Sleep
将起作用