C# 如何确定有毒队列消息的原因

C# 如何确定有毒队列消息的原因,c#,azure,azure-functions,azure-webjobs,C#,Azure,Azure Functions,Azure Webjobs,我有一个带有QueueTrigger的azure函数,它处理一个映像并输出一个blob和一个表记录 有时在处理多个大型图像时,我会遇到OutOfMemory异常,这可能会导致队列项目被放入有毒队列 有时会出现竞争条件,插入的表记录会出错,因为已经有一条记录具有该分区键和行键 我可以自己在函数中解决这些问题,但我更喜欢的处理方法是在毒药消息中包含导致项目被放入毒药队列的原因或异常。这样我就可以让另一个触发器监听毒药队列,并在评估出什么地方出错后采取相应的行动 这应该如何处理?我知道ErrorTri

我有一个带有QueueTrigger的azure函数,它处理一个映像并输出一个blob和一个表记录

有时在处理多个大型图像时,我会遇到OutOfMemory异常,这可能会导致队列项目被放入有毒队列

有时会出现竞争条件,插入的表记录会出错,因为已经有一条记录具有该分区键和行键

我可以自己在函数中解决这些问题,但我更喜欢的处理方法是在毒药消息中包含导致项目被放入毒药队列的原因或异常。这样我就可以让另一个触发器监听毒药队列,并在评估出什么地方出错后采取相应的行动


这应该如何处理?我知道ErrorTrigger,这很好地获得了异常,但我不知道如何将其与导致异常的特定队列项目联系起来。

在队列触发器失败后,无法更改有毒存储队列消息队列

Azure函数使用Web作业SDK。直接使用Web Jobs SDK可以覆盖QueueProcessor.CopyMessageTopoIsonQueueEasync。一些有用的链接:


队列触发器失败后,无法更改有毒存储队列消息队列

Azure函数使用Web作业SDK。直接使用Web Jobs SDK可以覆盖QueueProcessor.CopyMessageTopoIsonQueueEasync。一些有用的链接:


使用FunctionInvocation筛选器跟踪异常和导致该消息的消息是一种很有技巧的方法

public static class QueueFunction
{
    [FunctionName("QueueFunction")]
    [TrackFailedMessages]
    public static void Run([QueueTrigger("myqueue")]string message)
    {
        throw new Exception("OMG");
    }
}

public class TrackFailedMessages : FunctionInvocationFilterAttribute
{
    private static readonly ConcurrentDictionary<Guid, object> QueueParamters = new ConcurrentDictionary<Guid, object>();
    public override Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken)
    {
        if (executingContext.Arguments.TryGetValue("message", out var data))
        {
            QueueParamters.AddOrUpdate(executingContext.FunctionInstanceId, data, (id, obj) => data);
        }
        return Task.CompletedTask;
    }

    public override Task OnExecutedAsync(FunctionExecutedContext executedContext, CancellationToken cancellationToken)
    {
        if (executedContext.FunctionResult.Exception != null &&
            QueueParamters.TryGetValue(executedContext.FunctionInstanceId, out var message))
        {
            executedContext.Logger.LogCritical(
                "The message {message} caused the exception {exception}",
                message,
                executedContext.FunctionResult.Exception.ToString());
        }
        QueueParamters.TryRemove(executedContext.FunctionInstanceId, out var _);
        return Task.CompletedTask;
    }
}
公共静态类队列函数
{
[FunctionName(“QueueFunction”)]
[跟踪失败消息]
公共静态无效运行([QueueTrigger(“myqueue”)]字符串消息)
{
抛出新异常(“OMG”);
}
}
公共类TrackFailedMessages:FunctionInvocationFilterAttribute
{
私有静态只读ConcurrentDictionary QueueParameters=新ConcurrentDictionary();
公共覆盖任务OneExecutingAsync(函数executingContext executingContext,CancellationToken CancellationToken)
{
if(executingContext.Arguments.TryGetValue(“消息”,输出变量数据))
{
QueueParameters.AddOrUpdate(executingContext.FunctionInstanceId,data,(id,obj)=>data);
}
返回Task.CompletedTask;
}
公共覆盖任务OneExecutedAsync(FunctionExecuteContext ExecuteContext,CancellationToken CancellationToken)
{
if(executedContext.FunctionResult.Exception!=null&&
QueueParameters.TryGetValue(ExecuteContext.FunctionInstanceId,out var消息))
{
ExecuteContext.Logger.LogCritical(
“消息{message}导致异常{exception}”,
消息
executeContext.FunctionResult.Exception.ToString());
}
QueueParameters.TryRemove(executedContext.FunctionInstanceId,out var);
返回Task.CompletedTask;
}
}
调用函数时,将调用OnExecutinAsync。使用executingContext可以访问函数的参数。您可以搜索QueueTrigger参数并将值存储在字典中,其中FunctionInstanceId是键。FunctionInstanceId是每个调用的唯一Id

在OnExecuted中,您可以检查函数是否引发异常和异常,并从字典中获取消息,然后将其存储在其他位置(数据库、日志等)


我不确定这是否捕获了表存储异常…

有一种黑客方法可以使用FunctionInvocation筛选器跟踪异常和导致消息的消息

public static class QueueFunction
{
    [FunctionName("QueueFunction")]
    [TrackFailedMessages]
    public static void Run([QueueTrigger("myqueue")]string message)
    {
        throw new Exception("OMG");
    }
}

public class TrackFailedMessages : FunctionInvocationFilterAttribute
{
    private static readonly ConcurrentDictionary<Guid, object> QueueParamters = new ConcurrentDictionary<Guid, object>();
    public override Task OnExecutingAsync(FunctionExecutingContext executingContext, CancellationToken cancellationToken)
    {
        if (executingContext.Arguments.TryGetValue("message", out var data))
        {
            QueueParamters.AddOrUpdate(executingContext.FunctionInstanceId, data, (id, obj) => data);
        }
        return Task.CompletedTask;
    }

    public override Task OnExecutedAsync(FunctionExecutedContext executedContext, CancellationToken cancellationToken)
    {
        if (executedContext.FunctionResult.Exception != null &&
            QueueParamters.TryGetValue(executedContext.FunctionInstanceId, out var message))
        {
            executedContext.Logger.LogCritical(
                "The message {message} caused the exception {exception}",
                message,
                executedContext.FunctionResult.Exception.ToString());
        }
        QueueParamters.TryRemove(executedContext.FunctionInstanceId, out var _);
        return Task.CompletedTask;
    }
}
公共静态类队列函数
{
[FunctionName(“QueueFunction”)]
[跟踪失败消息]
公共静态无效运行([QueueTrigger(“myqueue”)]字符串消息)
{
抛出新异常(“OMG”);
}
}
公共类TrackFailedMessages:FunctionInvocationFilterAttribute
{
私有静态只读ConcurrentDictionary QueueParameters=新ConcurrentDictionary();
公共覆盖任务OneExecutingAsync(函数executingContext executingContext,CancellationToken CancellationToken)
{
if(executingContext.Arguments.TryGetValue(“消息”,输出变量数据))
{
QueueParameters.AddOrUpdate(executingContext.FunctionInstanceId,data,(id,obj)=>data);
}
返回Task.CompletedTask;
}
公共覆盖任务OneExecutedAsync(FunctionExecuteContext ExecuteContext,CancellationToken CancellationToken)
{
if(executedContext.FunctionResult.Exception!=null&&
QueueParameters.TryGetValue(ExecuteContext.FunctionInstanceId,out var消息))
{
ExecuteContext.Logger.LogCritical(
“消息{message}导致异常{exception}”,
消息
executeContext.FunctionResult.Exception.ToString());
}
QueueParameters.TryRemove(executedContext.FunctionInstanceId,out var);
返回Task.CompletedTask;
}
}
调用函数时,将调用OnExecutinAsync。使用executingContext可以访问函数的参数。您可以搜索QueueTrigger参数并将值存储在字典中,其中FunctionInstanceId是键。FunctionInstanceId是每个调用的唯一Id

在OnExecuted中,您可以检查函数是否引发异常和异常,并从字典中获取消息,然后将其存储在其他位置(数据库、日志等)


我不确定这是否捕获了表存储异常…

我以前在ServiceBus队列中处理过这个问题,方法是将代码包装在try/catch中,在catch中,我获取堆栈跟踪并显式地对消息进行死信处理,并将堆栈跟踪作为消息包含在内。我是