持久函数可以有多个触发器吗?

Can durable functions have multiple triggers?

我有一个每天由定时器触发器触发一次的持久函数:

[FunctionName("MyDurableFunction")]
public static async Task Run(
    [TimerTrigger("0 0 23 * * *", RunOnStartup = false)] TimerInfo myTimer,
    [OrchestrationClient] DurableOrchestrationClient starter,
    ILogger log)
{
    await starter.StartNewAsync("OrchestrationFunction", null);
}

[FunctionName("OrchestrationFunction")]
public static async Task OrchestrationFunction(
    [OrchestrationTrigger]DurableOrchestrationContext context,
    ILogger log)
{
    // do stuff
}

这很好用。出于测试目的,我还希望能够通过 Http 触发器触发持久函数,所以我添加了这个:

[FunctionName("MyDurableFunctionHttpTrigger")]
public static async Task<IActionResult> Run(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", "post", Route = "demo")]HttpRequest req, 
    [OrchestrationClient] DurableOrchestrationClient starter, 
    ILogger log)
{
    await starter.StartNewAsync("OrchestrationFunction", null);
    return new OkObjectResult("blah");
}

运行这些在本地,包括http trigger或者timer trigger都会触发该函数,但是在class中同时包含这两种触发事件都不会发生。是否可以让多个触发器类型启动一个编排触发器?

我相信每个函数只能有一个触发器类型,但可以建议您将所有逻辑写到一个单独的 project/assembly 中,然后只引用程序集并通过参数调用入口点,保留您的函数实现简洁明了,并将执行逻辑集中在另一个项目中(或 类 在同一项目中)。

在您的代码中,您应该具有 Orchestrator 和 Activity 函数,因此您可以编写一个 Activity 函数来完成工作并从两个 orchestrator 调用它。 Durable Functions 的指导是让编排器保持干净和简单的管理 - 编排,将工作卸载到活动。

我建议您查看 durable monitor pattern for your timer based requirement and look at the HTTP APIs 的 HTTP 触发器。

您可以做的是创建多个普通函数,每个函数对应一种类型的触发器。预定触发器、http 触发器、blob 触发器或任何其他受支持的触发器。

在该函数中,您可以启动一个新的编排函数。该编排功能本身不需要触发器。您只需要 DurableOrchestrationContext。

public static async Task<object> RunOrchestrator(
    [OrchestrationTrigger] DurableOrchestrationContext context,
    ILogger log)
{
    // orchestration logic here
}

[FunctionName("Info_HttpStart1")]
public static async Task<HttpResponseMessage> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "starter1")]HttpRequestMessage req,
    [OrchestrationClient]DurableOrchestrationClient starter,
    ILogger log)
{
    // Function input comes from the request content.
    string instanceId = await starter.StartNewAsync("Info", null);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    return starter.CreateCheckStatusResponse(req, instanceId);
}

[FunctionName("Info_HttpStart2")]
public static async Task<HttpResponseMessage> HttpStart(
    [HttpTrigger(AuthorizationLevel.Anonymous, "get", Route = "starter2")]HttpRequestMessage req,
    [OrchestrationClient]DurableOrchestrationClient starter,
    ILogger log)
{
    // Function input comes from the request content.
    string instanceId = await starter.StartNewAsync("Info", null);

    log.LogInformation($"Started orchestration with ID = '{instanceId}'.");

    return starter.CreateCheckStatusResponse(req, instanceId);
}