定时器触发Azure Function按配置时间调用POST API启动App Service的实现问题
解决方案
首先修正现有代码的两处基础错误:
- 节假日判断逻辑写反:现有
if (holidaychk != 0)的含义是「当天是节假日才执行后续逻辑」,和需求的「当日不属于节假日」不符,需修改为if (holidaychk == 0) - SQL语句语法错误:
settingscheck变量的拼接缺空格,拼接后会出现OrderDayFROM的错误语法,需要在第一行SQL末尾加空格
推荐方案:使用Durable Function实现无超时的定时等待
普通Azure Function(消费计划)最长执行时间仅为10分钟,如果StartTime距离早5点的间隔超过10分钟,直接使用Thread.Sleep/Task.Delay会触发执行超时,任务被强制终止。Durable Function的持久定时器机制不需要占用执行资源,不受普通执行超时限制,是最适配该场景的方案。
实现步骤:
- 安装Durable Function依赖NuGet包:
Microsoft.Azure.WebJobs.Extensions.DurableTask - 调整代码结构,拆分三个部分:定时触发的客户端函数、协调器函数、调用启动API的活动函数
- 代码示例:
using Microsoft.Azure.WebJobs; using Microsoft.Azure.WebJobs.Extensions.DurableTask; using Microsoft.Extensions.Logging; using System; using System.Data.SqlClient; using System.Threading.Tasks; using Azure.Identity; using Azure.Core; using System.Net.Http; using System.Net.Http.Headers; public static class StartorStopSMAppService { // 原定时触发函数作为Durable客户端 [FunctionName("StartorStopSMAppService_TimerTrigger")] public static async Task Run([TimerTrigger("0 0 5 * * *")] TimerInfo myTimer, [DurableClient] IDurableOrchestrationClient starter, ILogger log) { int holidaychk = 0; var str = Environment.GetEnvironmentVariable("SurplusMouseDBConnection"); using (SqlConnection conn = new SqlConnection(str)) { try { conn.Open(); var holidaycheck = "SELECT Count(*) FROM[dbo].[HolidayWeeks] where FORMAT(getdate(), 'yyyy-MM-dd') = [HolidateDate]"; SqlCommand cmd1 = new SqlCommand(holidaycheck, conn); holidaychk = (Int32)cmd1.ExecuteScalar(); // 非节假日才继续判断 if (holidaychk == 0) { var settingscheck = "SELECT TOP 1 [EndTime],[StartTime],[OrderDay] " + "FROM[dbo].[Settings] " + "where SUBSTRING(DATENAME(weekday, getdate() AT TIME ZONE 'UTC' AT TIME ZONE 'Eastern Standard Time'), 0, 4) = OrderDay"; SqlCommand command = new SqlCommand(settingscheck, conn); using (SqlDataReader reader = command.ExecuteReader()) { if (reader.Read() && reader.HasRows) { var starttimeStr = reader["StartTime"].ToString(); // 解析StartTime为今日的DateTime var today = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, "Eastern Standard Time").Date; var startTime = today.Add(TimeSpan.Parse(starttimeStr)); var now = TimeZoneInfo.ConvertTimeBySystemTimeZoneId(DateTime.UtcNow, "Eastern Standard Time"); // 计算延时时间,如果StartTime已经过了就直接执行 var delay = startTime > now ? startTime - now : TimeSpan.Zero; // 启动协调器,传入延时和Web App资源ID string instanceId = await starter.StartNewAsync("StartAppServiceOrchestrator", Tuple.Create(delay, "你的Web App资源ID")); log.LogInformation($"Started orchestration with ID = '{instanceId}', delay = {delay.TotalMinutes} minutes"); } } } } catch (Exception ex) { log.LogError(ex.Message); } finally { if (conn.State != System.Data.ConnectionState.Closed) { conn.Close(); } } } } // 协调器函数 [FunctionName("StartAppServiceOrchestrator")] public static async Task RunOrchestrator([OrchestrationTrigger] IDurableOrchestrationContext context) { var input = context.GetInput<Tuple<TimeSpan, string>>(); var delay = input.Item1; var webAppId = input.Item2; // 持久等待,不占用执行资源 if(delay > TimeSpan.Zero) { await context.CreateTimer(context.CurrentUtcDateTime.Add(delay), CancellationToken.None); } // 调用活动函数执行API请求 await context.CallActivityAsync("CallStartAppServiceApi", webAppId); } // 活动函数:调用App Service启动API [FunctionName("CallStartAppServiceApi")] public static async Task CallStartApi([ActivityTrigger] string webAppId, ILogger log) { var credential = new DefaultAzureCredential(); var token = await credential.GetTokenAsync(new TokenRequestContext(new[] { "https://management.azure.com/.default" })); using var httpClient = new HttpClient(); httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); var apiUrl = $"https://management.azure.com{webAppId}/start?api-version=2022-03-01"; var response = await httpClient.PostAsync(apiUrl, null); response.EnsureSuccessStatusCode(); log.LogInformation("App Service started successfully"); } }
替代方案:使用队列存储延时消息
如果不想引入Durable Function依赖,也可以用Azure Queue Storage实现延时:
- 计算当前时间到StartTime的时间差,将启动任务参数发送到队列,设置消息的
InitialVisibilityDelay为对应的延时时长 - 新增一个队列触发的Azure Function,收到队列消息后执行API调用即可
- 队列消息最长支持延时7天,完全满足当日延时需求
内容的提问来源于stack exchange,提问作者trx
相关产品推荐
相关产品推荐

