在本地运行一次计时器触发的 Azure 函数的最简单方法是啥?

Posted

技术标签:

【中文标题】在本地运行一次计时器触发的 Azure 函数的最简单方法是啥?【英文标题】:What is the simplest way to run a timer-triggered Azure Function locally once?在本地运行一次计时器触发的 Azure 函数的最简单方法是什么? 【发布时间】:2018-03-15 08:39:35 【问题描述】:

我有几个使用 timer triggers 按计划运行的 C# Azure 函数。我已经将它们设置为这样,其中%TimerSchedule% 指的是应用程序设置中的 cron 表达式:

public static void Run([TimerTrigger("%TimerSchedule%")]TimerInfo myTimer, TraceWriter log)

在开发过程中,我经常想使用 Azure Functions Tools for Visual Studio + Azure Functions Core Tools 在本地运行函数。但是当我按 F5 在本地调试该功能时,它(通常)不会立即运行。相反,它会根据计时器计划开始等待下一次发生。例如,如果我的 cron 表达式说每天晚上 8 点运行,我必须等到晚上 8 点才能在我的机器上实际运行该函数。

所以我的问题是:让函数在本地运行一次的最简单和最好的方法是什么?

我尝试过或考虑过的事情:

    仅针对本地开发使用更频繁的计时器计划 这没问题,但并不完美——除非非常频繁,否则您仍然需要稍等片刻,如果非常频繁,则该函数可能会运行多次。这就是我现在正在做的事情。 编写直接调用函数的Run()方法的控制台应用程序或单元测试 这不是 100% 简单的,因为您必须向 Run() 提供 TimerInfoTraceWriter 参数——而且我发现这方面的文档非常少。

Microsoft 的Strategies for testing your code in Azure Functions 页面在这个主题上不是很有帮助 - 它只提到计时器触发器作为测试其他触发器类型的一种方式。

在一个完美的世界里,我按下 F5,该函数会立即运行一次——就像开发一个“普通”的 .NET 应用程序一样。

【问题讨论】:

【参考方案1】:

我有同样的问题,并使用 DEBUG 标志仅在调试时使用 RunOnStartup:

        public static void Run(
            [TimerTrigger("* 0 7 * * 1-5"
#if DEBUG
            , RunOnStartup=true
#endif
            )]TimerInfo myTimer, TraceWriter log)
        

【讨论】:

它看起来很糟糕,但是这正是我所需要的,而且它有效。 这就是我最终做的,是的,它很丑陋,但它适用于本地调试目的! 我不明白它有什么难看的。 @Szybki 标志弄乱了缩进。但如果它有效,它就会有效。 (确实如此。) 您可以将硬编码的true 替换为在别处定义的常量,并在常量声明中使用#if DEBUG。这至少让你的 Run 方法签名看起来更干净一些。【参考方案2】:

您也许可以使用 RunOnStartup 标志,如文档中的 here 所述。它不完全符合您关于它只运行一次的简要说明,但它至少应该在应用程序启动后在本地执行它。

/// Gets or sets a value indicating whether the function should be invoked
/// immediately on startup. After the initial startup run, the function will
/// be run on schedule thereafter.

使用属性绑定的示例:

[TimerTrigger("%TimerSchedule%", RunOnStartup = true)]TimerInfo myTimer

【讨论】:

我是否正确地认为,每当我部署该功能时,它也会在云中运行一次该功能? This comment 似乎表示是。如果是这样,这是朝着正确方向迈出的一大步,但如果我不想改变“生产”行为,就必须在部署之前恢复它,这有点不幸。 @ripley_ 是的,我相信你是对的。一个选项可能是将布尔值绑定到配置中的变量中,类似于绑定“TimerSchedule”的方式。从那里,您可以将本地开发设置为 true,并将生产(或其他)环境设置为 false,只要您认为合适。 如何将 RunOnStartup 绑定到我的配置中定义的布尔值。 “%%”语法不适用于非字符串值。 来自Microsoft documentation 似乎建议不要在大多数情况下使用 RunOnStartup 参数。 If true, the function is invoked when the runtime starts. For example, the runtime starts when the function app wakes up after going idle due to inactivity. when the function app restarts due to function changes, and when the function app scales out. **So runOnStartup should rarely if ever be set to true, especially in production** @Nissan 这是真的,但是,OP 表示这是为了在本地运行而不是在生产环境中运行......【参考方案3】:

来自https://docs.microsoft.com/en-us/azure/azure-functions/functions-run-local?tabs=windows%2Ccsharp%2Cbash#non-http-triggered-functions

非 HTTP 触发函数

对于 HTTP 触发器和 Webhook 以外的所有类型的函数,您可以通过调用管理端点在本地测试您的函数。在本地服务器上使用 HTTP POST 请求调用此端点会触发该函数。您可以选择将测试数据传递给 POST 请求正文中的执行。此功能类似于 Azure 门户中的“测试”选项卡。

您调用以下管理员端点来触发非 HTTP 函数:

http://localhost:port/admin/functions/function_name

要将测试数据传递到函数的管理员端点,您必须在 POST 请求消息的正文中提供数据。消息正文必须具有以下 JSON 格式:


    "input": "<trigger_input>"

【讨论】:

这应该是公认的答案! Run On Startup 很危险,可能会花费你很多钱! 请注意,默认端口为:7071【参考方案4】:

如果您使用的是 VS Code,请使用 Azure Functions extension:

    按 F5 进入调试模式,这将启动函数应用。 转到活动栏中的 Azure 图标。 在Local Project下,找到你要运行的函数,右击,选择“立即执行函数”。

查看此 MS quickstart guide。

【讨论】:

不知道为什么花了这么长时间才找到这个。如果您使用的是 VScode,这是迄今为止最简单的方法,老实说,如果您正在构建 Azure Functions,您可能就是这样。谢谢!【参考方案5】:

使用邮递员应该可以解决问题。按照以下步骤在本地运行或调试您的计时器触发器。

1 .运行您的项目。

    打开 Postman 并通过此 url http://localhost:port/admin/functions/function_name

    确保使用带有 Json 主体的 POST 方法 “输入”: ””

    按发送。

您应该收到 202 的响应。

【讨论】:

【参考方案6】:

我也有同样的问题。我用单元测试修复了它。 实际上,您需要删除 TraceWriter 和 TimerInfo。

这里有一些我是如何做到这一点的代码。

定时器信息:

public class ScheduleStub : TimerInfo

    public ScheduleStub(TimerSchedule schedule, ScheduleStatus status, bool isPastDue = false) : base(schedule, status, isPastDue)
    
    

还有 TraceWriter:

 public class TraceWriterStub : TraceWriter

    protected TraceLevel _level;
    protected List<TraceEvent> _traces;

    public TraceWriterStub(TraceLevel level) : base(level)
    
        _level = level;
        _traces = new List<TraceEvent>();
    

    public override void Trace(TraceEvent traceEvent)
    
        _traces.Add(traceEvent);
    

    public List<TraceEvent> Traces => _traces;

【讨论】:

【参考方案7】:

用这个 curl 命令启动你的函数

curl --request POST -H "Content-Type:application/json" --data '"input":""'  http://localhost:7071/admin/functions/function_name

输入数据是必需的,没有它不会触发函数。

【讨论】:

【参考方案8】:

只需在同一个类中添加另一个具有 HTTP 触发器类型的函数,添加您的代码,或从该函数调用您的 Run 方法并从您的浏览器调用它。

确保在部署到 prod 时注释/删除该函数,否则您将能够通过 prod 中的 HTTP 调用触发该函数。

【讨论】:

以上是关于在本地运行一次计时器触发的 Azure 函数的最简单方法是啥?的主要内容,如果未能解决你的问题,请参考以下文章

使 Azure Webjob 计时器触发器不作为单例运行

Azure Function App:Http 触发器与计时器触发器

Azure 函数从门户或 Visual Studio-Blob 触发器执行

如何在没有绑定的情况下运行 Azure Function

Azure 函数中的 Az.Functions 模块引发错误

当我执行代码+测试时,Azure 函数中的计时器触发器不起作用