C# 使用TimerTrigger对连续作业使用Azure WebJob超时

C# 使用TimerTrigger对连续作业使用Azure WebJob超时,c#,azure,azure-webjobs,azure-webjobssdk,C#,Azure,Azure Webjobs,Azure Webjobssdk,我有一个连续的WebJob,其中有一个函数使用TimerTrigger每30秒运行一个进程。函数中的某个特定调用偶尔挂起,看起来是随机挂起的,这会导致webjob无限期地等待。当前的解决方案是,请注意服务已停止,然后登录Azure仪表板并手动中止它 请注意,我知道正确的做法是找出根本原因并加以解决。相信我,我们正在努力。同时,我想治疗这种症状,需要帮助 我正试图让WebJob使用超时装饰器检测状态,如本文在Azure WebJobs SDK上所述:。实现这个建议后,我可以看到当有问题的调用挂起时

我有一个连续的WebJob,其中有一个函数使用TimerTrigger每30秒运行一个进程。函数中的某个特定调用偶尔挂起,看起来是随机挂起的,这会导致webjob无限期地等待。当前的解决方案是,请注意服务已停止,然后登录Azure仪表板并手动中止它

请注意,我知道正确的做法是找出根本原因并加以解决。相信我,我们正在努力。同时,我想治疗这种症状,需要帮助

我正试图让WebJob使用超时装饰器检测状态,如本文在Azure WebJobs SDK上所述:。实现这个建议后,我可以看到当有问题的调用挂起时,会检测到超时,但是WebJob仍然没有死掉。我在这里做错了什么,不会杀死函数以允许后续调用

Program.cs 函数.cs
[Singleton]
[超时(“00:05:00”)]
公共异步静态任务周期进程([TimerTrigger(“00:00:30”,runnstartup=true)]TimerInfo计时器、CancellationToken cancelToken、TextWriter日志)
{
log.WriteLine(“--Processing Begin--”;
列表案例=GetEmailsAndWhatNot();
foreach(电子邮件中的电子邮件)
{
尝试
{
发送电子邮件(e、日志)的问题功能;
}
捕获(例外e)
{
//做事
}
}
log.WriteLine(“--Processing End--”);
}
公共静态无效问题功能\u发送电子邮件(电子邮件e、TextWriter日志)
{
//发送电子邮件
}
问题期间的WebJob输出 --处理开始--

函数“Functions.PeriodicProcess”(Id:'0f7438bd-baad-451f-95a6-9461f35bfb2d')超出了00:05:00的超时值。开始取消

尽管webjob启动了取消,但该功能并没有消失。我需要监视CancellationToken吗?我需要在多大程度上实现异步调用?我在这里遗漏了什么,实际上会中止这个过程

如TimerTrigger所述:

单例锁

TimerTrigger使用WebJobs SDK的Singleton功能来确保在任何给定时间只有一个触发函数实例在运行

日程安排

如果函数执行时间长于计时器间隔,则在当前调用完成之前不会触发另一次执行。计划在当前执行完成后执行下一次

这是我对该场景的测试,您可以参考它:

  • 使用
    CancellationToken.None
    并且永远不要分发取消令牌

    注意:功能
    PeriodicProcess
    将在30秒后超时,但耗时的作业仍在运行,长时间运行的作业完成后,将打印处理结束日志

  • 分发取消令牌

    注意:如果我们分发取消令牌,耗时的作业将立即取消

代码片段

[Timeout("00:00:30")]
[Singleton]
public async static Task PeriodicProcess([TimerTrigger("00:00:10", RunOnStartup = true)] TimerInfo timer, CancellationToken cancelToken, TextWriter log)
{
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Processing Begin --");

    try
    {
        await longRunningJob(log, cancelToken);
    }
    catch (Exception e)
    {
        // do stuff
    }
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Processing End -- ");
}


private async static Task longRunningJob(TextWriter log, CancellationToken cancelToken)
{
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Begin Time-consuming jobs --");
    await Task.Delay(TimeSpan.FromMinutes(1), cancelToken);
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Complete Time-consuming jobs --");
}

是,您需要监视取消令牌上的
IsCancellationRequested
属性。最好的做法是在有支持的情况下尽可能地向下传播。一旦你实现了这一点,它就会起作用。
[Singleton]
[Timeout("00:05:00")]
public async static Task PeriodicProcess([TimerTrigger("00:00:30", RunOnStartup = true)] TimerInfo timer, CancellationToken cancelToken, TextWriter log)
{
    log.WriteLine("-- Processing Begin --");

    List<Emails> cases = GetEmailsAndWhatNot();
    foreach (Email e in Emails)
    {
        try
        {
            ProblematicFunction_SendEmail(e, log);
        }
        catch(Exception e)
        {
            // do stuff
        }
    }
    log.WriteLine("-- Processing End -- ");
}


public static void ProblematicFunction_SendEmail(Email e, TextWriter log)
{
    // send email
}
[Timeout("00:00:30")]
[Singleton]
public async static Task PeriodicProcess([TimerTrigger("00:00:10", RunOnStartup = true)] TimerInfo timer, CancellationToken cancelToken, TextWriter log)
{
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Processing Begin --");

    try
    {
        await longRunningJob(log, cancelToken);
    }
    catch (Exception e)
    {
        // do stuff
    }
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Processing End -- ");
}


private async static Task longRunningJob(TextWriter log, CancellationToken cancelToken)
{
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Begin Time-consuming jobs --");
    await Task.Delay(TimeSpan.FromMinutes(1), cancelToken);
    log.WriteLine($"-- [{DateTime.Now.ToString()}] Complete Time-consuming jobs --");
}