不同部署槽中的 TimerTriggered Azure Webjob
TimerTriggered Azure Webjob in different deployment slots
我有一个关于 Azure 应用服务的网站 运行 在 5 个不同的部署槽(Dev、UAT、Staging、Prelive-1、Prelive-2)中,每个都包含一个定时器触发的 azure webjob,写在.NET Core 2.2(正确放置在 App_Data 中的正确位置)。每个 Webjob 都包含一个 appsettings.json 文件,其中有不同的 URL 可以命中。
webjob 每 5 分钟 运行s 并从应用程序设置中点击 url,但是当我检查 azure insights 中的日志(配置正确)时,我只看到一个部署槽是运行ning(90% 的时间是第一个部署槽——“DEV”槽)。我想在所有部署槽中创建所有 webjob 运行。
还有一个问题假设我是否重新启动任何部署槽,例如Prelive-1 然后立即执行 webjobs,5 分钟后它没有。
有没有办法让它们一次全部 运行 或者一个一个地做,但我只想让所有的部署槽到 运行 每 5 个网络作业分钟
下面是我的Program.cs
public class Program
{
public static async Task Main()
{
HostBuilder hostBuilder = new HostBuilder();
hostBuilder.ConfigureWebJobs(builder =>
{
builder.AddAzureStorageCoreServices();
builder.AddAzureStorage();
builder.AddTimers();
})
.ConfigureAppConfiguration((hostContext, configurationBuilder) =>
{
configurationBuilder.AddJsonFile($"appsettings.json", optional: false, reloadOnChange: false);
})
.ConfigureLogging((context, loggingBuilder) =>
{
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
loggingBuilder.AddApplicationInsightsWebJobs(logBuilder => logBuilder.InstrumentationKey = instrumentationKey);
})
.ConfigureServices(services =>
{
services.AddHttpClient();
})
.UseConsoleLifetime();
using (var host = hostBuilder.Build())
{
await host.RunAsync();
}
}
Function.cs
public class Function1
{
const string CheckPendingFulfillmentsUrlConfigKey = "CheckPendingFulfillmentsUrl";
const string WebJobTimerExpression = "0 */5 * * * *";
readonly IHttpClientFactory httpClientFactory;
readonly ILogger<Function1> logger;
readonly string CheckPendingFulfillmentsUrl;
public Function1(IHttpClientFactory HttpClientFactory, ILogger<Function1> Logger, IConfiguration config)
{
httpClientFactory = HttpClientFactory;
logger = Logger;
CheckPendingFulfillmentsUrl = config[CheckPendingFulfillmentsUrlConfigKey];
}
public async Task TriggerCheckPendingFulfillments([TimerTrigger(WebJobTimerExpression, RunOnStartup = false)] TimerInfo timerInfo, CancellationToken cancellationToken)
{
using (var httpClient = httpClientFactory.CreateClient())
{
if (!string.IsNullOrEmpty(CheckPendingFulfillmentsUrl))
{
try
{
Console.WriteLine("Calling the url " + CheckPendingFulfillmentsUrl);
await httpClient.GetAsync(CheckPendingFulfillmentsUrl);
}
catch(Exception Ex)
{
Console.WriteLine("Error -- "+ Ex.Message);
}
logger.LogInformation(string.Format(Properties.Resources.Info_CheckPendingFulfillmentsTriggered, CheckPendingFulfillmentsUrl));
}
}
}
}
当您创建一个新插槽时,您将获得一个新的 App services(slot)
。
这意味着无论您创建多少个插槽,它们都是独立的。 webjob
的执行应该是相互独立的,他们都会运行.
这是我的 webapp 生产槽。
创建一个名称为 Test1
的新插槽。我相信 webjob 的执行与 TRAFFIC %
.
无关
这是我的 test1 插槽。
下面是我的 Test1 运行ning 输出。
疑难解答:
- 像我的测试代码一样创建一个演示 webjob。
- 修改
Settings.job
文件。
- 检查日志。
Test1
插槽。
Production
插槽。
结论:
每个插槽相当于一个全新的应用服务。
webjob的执行不会互相干扰,都会执行。 (不受流量百分比干扰)
slot的本质应该是用于热部署的开发环境和生产环境的切换
如果想单独执行webjob或者一起执行webjob,可以通过配置文件或者读取数据库值来执行
示例:
在webjob的配置文件中,当前webjob的名字是test1
。
其他的分别是test2
, test3
.
假设timetrigger的时间设置为1min,那么数据库中一个名为Table_ExecutionSchedule
的table可以每分钟读取一次。当程序读取到特定指令如test1-start
时,程序执行特定的业务功能。否则跳过这个触发条件。
这可能是因为您对所有这些 Web 作业使用相同的存储。
Behind the scenes, TimerTrigger uses the Singleton feature of the WebJobs SDK to ensure
that only a single instance of your triggered function is running at any given time.
When the JobHost starts up, for each of your TimerTrigger functions a blob lease (the Singleton Lock) is taken. This distrubuted lock ensures that only a single instance of your scheduled function is running at any time. If the blob for that function is not currently leased, the function will acquire the lease and start running on schedule immediately. If the blob lease cannot be acquired, it generally means that another instance of that function is running, so the function is not started in the current host. When this happens, the host will continue to periodically check to see if it can acquire the lease. This is done as a sort of "recovery" mode to ensure that when running steady state, if an instance goes down, another instance notice and pick up where the other left off.
所以有两种可能:
具有单独的存储帐户:如果每个环境都有一个存储帐户,这很有意义 (dev/staging/prod)
指定 JobHostConfiguration 的 HosId 属性 class:
var config = new JobHostConfiguration();
config.HostId = "dev|staging|prod";
我有一个关于 Azure 应用服务的网站 运行 在 5 个不同的部署槽(Dev、UAT、Staging、Prelive-1、Prelive-2)中,每个都包含一个定时器触发的 azure webjob,写在.NET Core 2.2(正确放置在 App_Data 中的正确位置)。每个 Webjob 都包含一个 appsettings.json 文件,其中有不同的 URL 可以命中。
webjob 每 5 分钟 运行s 并从应用程序设置中点击 url,但是当我检查 azure insights 中的日志(配置正确)时,我只看到一个部署槽是运行ning(90% 的时间是第一个部署槽——“DEV”槽)。我想在所有部署槽中创建所有 webjob 运行。
还有一个问题假设我是否重新启动任何部署槽,例如Prelive-1 然后立即执行 webjobs,5 分钟后它没有。
有没有办法让它们一次全部 运行 或者一个一个地做,但我只想让所有的部署槽到 运行 每 5 个网络作业分钟
下面是我的Program.cs
public class Program
{
public static async Task Main()
{
HostBuilder hostBuilder = new HostBuilder();
hostBuilder.ConfigureWebJobs(builder =>
{
builder.AddAzureStorageCoreServices();
builder.AddAzureStorage();
builder.AddTimers();
})
.ConfigureAppConfiguration((hostContext, configurationBuilder) =>
{
configurationBuilder.AddJsonFile($"appsettings.json", optional: false, reloadOnChange: false);
})
.ConfigureLogging((context, loggingBuilder) =>
{
string instrumentationKey = context.Configuration["APPINSIGHTS_INSTRUMENTATIONKEY"];
if (!string.IsNullOrEmpty(instrumentationKey))
loggingBuilder.AddApplicationInsightsWebJobs(logBuilder => logBuilder.InstrumentationKey = instrumentationKey);
})
.ConfigureServices(services =>
{
services.AddHttpClient();
})
.UseConsoleLifetime();
using (var host = hostBuilder.Build())
{
await host.RunAsync();
}
}
Function.cs
public class Function1
{
const string CheckPendingFulfillmentsUrlConfigKey = "CheckPendingFulfillmentsUrl";
const string WebJobTimerExpression = "0 */5 * * * *";
readonly IHttpClientFactory httpClientFactory;
readonly ILogger<Function1> logger;
readonly string CheckPendingFulfillmentsUrl;
public Function1(IHttpClientFactory HttpClientFactory, ILogger<Function1> Logger, IConfiguration config)
{
httpClientFactory = HttpClientFactory;
logger = Logger;
CheckPendingFulfillmentsUrl = config[CheckPendingFulfillmentsUrlConfigKey];
}
public async Task TriggerCheckPendingFulfillments([TimerTrigger(WebJobTimerExpression, RunOnStartup = false)] TimerInfo timerInfo, CancellationToken cancellationToken)
{
using (var httpClient = httpClientFactory.CreateClient())
{
if (!string.IsNullOrEmpty(CheckPendingFulfillmentsUrl))
{
try
{
Console.WriteLine("Calling the url " + CheckPendingFulfillmentsUrl);
await httpClient.GetAsync(CheckPendingFulfillmentsUrl);
}
catch(Exception Ex)
{
Console.WriteLine("Error -- "+ Ex.Message);
}
logger.LogInformation(string.Format(Properties.Resources.Info_CheckPendingFulfillmentsTriggered, CheckPendingFulfillmentsUrl));
}
}
}
}
当您创建一个新插槽时,您将获得一个新的 App services(slot)
。
这意味着无论您创建多少个插槽,它们都是独立的。 webjob
的执行应该是相互独立的,他们都会运行.
这是我的 webapp 生产槽。
创建一个名称为 Test1
的新插槽。我相信 webjob 的执行与 TRAFFIC %
.
这是我的 test1 插槽。
下面是我的 Test1 运行ning 输出。
疑难解答:
- 像我的测试代码一样创建一个演示 webjob。
- 修改
Settings.job
文件。
- 检查日志。
Test1
插槽。
Production
插槽。
结论:
每个插槽相当于一个全新的应用服务。
webjob的执行不会互相干扰,都会执行。 (不受流量百分比干扰)
slot的本质应该是用于热部署的开发环境和生产环境的切换
如果想单独执行webjob或者一起执行webjob,可以通过配置文件或者读取数据库值来执行
示例:
在webjob的配置文件中,当前webjob的名字是test1
。
其他的分别是test2
, test3
.
假设timetrigger的时间设置为1min,那么数据库中一个名为Table_ExecutionSchedule
的table可以每分钟读取一次。当程序读取到特定指令如test1-start
时,程序执行特定的业务功能。否则跳过这个触发条件。
这可能是因为您对所有这些 Web 作业使用相同的存储。
Behind the scenes, TimerTrigger uses the Singleton feature of the WebJobs SDK to ensure that only a single instance of your triggered function is running at any given time. When the JobHost starts up, for each of your TimerTrigger functions a blob lease (the Singleton Lock) is taken. This distrubuted lock ensures that only a single instance of your scheduled function is running at any time. If the blob for that function is not currently leased, the function will acquire the lease and start running on schedule immediately. If the blob lease cannot be acquired, it generally means that another instance of that function is running, so the function is not started in the current host. When this happens, the host will continue to periodically check to see if it can acquire the lease. This is done as a sort of "recovery" mode to ensure that when running steady state, if an instance goes down, another instance notice and pick up where the other left off.
所以有两种可能:
具有单独的存储帐户:如果每个环境都有一个存储帐户,这很有意义 (dev/staging/prod)
指定 JobHostConfiguration 的 HosId 属性 class:
var config = new JobHostConfiguration();
config.HostId = "dev|staging|prod";